@gjsify/webkit-native 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# @gjsify/webkit-native
|
|
2
|
+
|
|
3
|
+
Apple's WebKit (`WKWebView`) behind a GObject API shaped like WebKitGTK 6.0 — the
|
|
4
|
+
darwin backend for [`@gjsify/iframe`](../iframe/README.md).
|
|
5
|
+
|
|
6
|
+
Decision and measurements: [ADR 0022](../../../docs/adr/0022-webkit-on-darwin.md).
|
|
7
|
+
|
|
8
|
+
## Why this exists
|
|
9
|
+
|
|
10
|
+
`gi://WebKit` version 6.0 has no macOS provider, and that is upstream's position
|
|
11
|
+
rather than a packaging gap: Homebrew's `webkitgtk` formula carries an
|
|
12
|
+
unconditional `depends_on :linux` whose own comment says *"Use
|
|
13
|
+
JavaScriptCore.Framework on macOS."*, ships Linux-only bottles, and builds the
|
|
14
|
+
GTK**3** port anyway (`-DUSE_GTK4=OFF`). So `@gjsify/iframe` could not load on
|
|
15
|
+
macOS at all:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
JS ERROR: Error: Requiring WebKit, version 6.0:
|
|
19
|
+
Typelib file for namespace 'WebKit', version '6.0' not found
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
macOS does have complete WebKit with a complete DOM — what it lacks is a
|
|
23
|
+
GObject-Introspection binding and a GTK widget. This package supplies both.
|
|
24
|
+
|
|
25
|
+
## What it is
|
|
26
|
+
|
|
27
|
+
One Objective-C library exposing a **subset of WebKitGTK 6.0's API under the
|
|
28
|
+
same namespace**: `WebKit-6.0`. That is the whole integration strategy —
|
|
29
|
+
`@gjsify/iframe` keeps `import WebKit from 'gi://WebKit?version=6.0'` verbatim
|
|
30
|
+
and carries **no backend seam, no OS branch, and no `gjsify.os` declaration**.
|
|
31
|
+
Which typelib answers to that name is decided by what is on `GI_TYPELIB_PATH`,
|
|
32
|
+
i.e. by packaging.
|
|
33
|
+
|
|
34
|
+
| exposed | backed by |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `WebKit.WebView` (a real `Gtk.Widget`, derivable) | `WKWebView`, offscreen |
|
|
37
|
+
| `WebKit.UserContentManager` / `UserScript` | `WKUserContentController` / `WKUserScript` |
|
|
38
|
+
| `WebKit.Settings` | `WKWebViewConfiguration` / `WKPreferences` |
|
|
39
|
+
| `load-changed` + `WebKit.LoadEvent` | `WKNavigationDelegate` |
|
|
40
|
+
| `script-message-received::<name>` | `window.webkit.messageHandlers.<name>.postMessage` |
|
|
41
|
+
| `evaluate_javascript()` → a value with `to_string()` | `evaluateJavaScript:completionHandler:` |
|
|
42
|
+
| `get_snapshot()` → `Gdk.Texture` | `takeSnapshotWithConfiguration:` |
|
|
43
|
+
|
|
44
|
+
## The two things that are not obvious
|
|
45
|
+
|
|
46
|
+
**The run loop.** `WKWebView` delivers everything through a CFRunLoop; GJS runs a
|
|
47
|
+
GMainContext, and a GMainContext does not pump a CFRunLoop. Measured: a
|
|
48
|
+
`WKWebView` driven from a bare `g_main_loop_run()` never even reaches
|
|
49
|
+
`didFinishNavigation`. The library installs a drain source while at least one
|
|
50
|
+
view is alive — [`docs/poc/webkit-runloop-darwin.m`](../../../docs/poc/webkit-runloop-darwin.m)
|
|
51
|
+
is that measurement, and it fails loudly if either half stops being true.
|
|
52
|
+
|
|
53
|
+
**The widget is built, not embedded.** `WKWebView` is an `NSView` and GTK4 has no
|
|
54
|
+
foreign-window embedding, so the content renders offscreen and is presented as a
|
|
55
|
+
`GdkTexture` in the widget's `snapshot` vfunc. There is no "content changed"
|
|
56
|
+
signal from an out-of-process web view, so the widget pulls: it snapshots on a
|
|
57
|
+
tick while mapped, at most one in flight. Measured at 1024×768 on a GPU-less
|
|
58
|
+
Intel VM, `takeSnapshot` averages 15.8 ms — a ~63 fps ceiling before any GPU is
|
|
59
|
+
involved.
|
|
60
|
+
|
|
61
|
+
## Building
|
|
62
|
+
|
|
63
|
+
Requires macOS, clang, meson and gobject-introspection.
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
gjsify workspace @gjsify/webkit-native run build:prebuilds
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
That runs meson and stages `libgjsifywebkit.dylib` + `WebKit-6.0.typelib` +
|
|
70
|
+
`WebKit-6.0.gir` into `prebuilds/<os>-<arch>/`, from where ADR 0017's per-target
|
|
71
|
+
split publishes them.
|
|
72
|
+
|
|
73
|
+
**In CI this needs the `ci:macos` label on the PR.** `prebuilds.yml`'s
|
|
74
|
+
`build-prebuilds-macos` job is opt-in (macOS runners bill at 10×), so without the
|
|
75
|
+
label every darwin leg skips and nothing here is built or uploaded — a PR can go
|
|
76
|
+
green while this package was never compiled. The label only takes effect on a run
|
|
77
|
+
created *after* it was added: neither workflow declares `types:`, so the default
|
|
78
|
+
`[opened, synchronize, reopened]` applies and `labeled` is not among them. Push a
|
|
79
|
+
commit (or reopen the PR) after labelling.
|
|
80
|
+
|
|
81
|
+
## Input
|
|
82
|
+
|
|
83
|
+
Mouse, scroll and keyboard are forwarded from the GTK widget to the web content:
|
|
84
|
+
`GtkGestureClick`, motion, scroll and key controllers each re-synthesize an
|
|
85
|
+
`NSEvent`. A forwarded click focuses the element under it, so DOM focus follows
|
|
86
|
+
the pointer as it does on Linux, and no `GtkIMContext` is attached because WebKit
|
|
87
|
+
routes `keyDown:` through its own `NSTextInputClient` — which *is* the macOS
|
|
88
|
+
input-method path, so a second IM context would compose the input twice.
|
|
89
|
+
|
|
90
|
+
Two things worth knowing if you touch it, both measured
|
|
91
|
+
(`docs/poc/webkit-input-darwin.m`): the view needs **no window and no responder
|
|
92
|
+
chain** — an offscreen `NSWindow` was built and is indistinguishable except that
|
|
93
|
+
it breaks scrolling — and an `NSEvent`'s location is **bottom-left window space
|
|
94
|
+
even though `WKWebView` is flipped**, so a GTK y is flipped exactly once against
|
|
95
|
+
the widget height. Getting that wrong delivers every event to the wrong element
|
|
96
|
+
and nothing else.
|
|
97
|
+
|
|
98
|
+
Minimum deployment target is **macOS 11**, declared in `meson.build`, because
|
|
99
|
+
script worlds are `WKContentWorld`.
|
|
100
|
+
|
|
101
|
+
## Not implemented
|
|
102
|
+
|
|
103
|
+
Deliberate, and each fails loudly rather than silently:
|
|
104
|
+
|
|
105
|
+
- **`document.hasFocus()` is always `false`,** so `window.onfocus` / `onblur`
|
|
106
|
+
never fire and no caret blinks. It is derived from a responder chain a
|
|
107
|
+
windowless view has no place in; an offscreen window does not fix it, which was
|
|
108
|
+
measured rather than assumed.
|
|
109
|
+
- **The pointer cursor never changes** over links or text — another thing WebKit
|
|
110
|
+
delivers through a window it does not have.
|
|
111
|
+
- **App Sandbox is unanswered.** The **hardened runtime works**
|
|
112
|
+
(`docs/poc/webkit-hardened-runtime-darwin.sh`, with
|
|
113
|
+
`com.apple.security.cs.allow-jit`), so a notarised app has no WebKit-specific
|
|
114
|
+
obstacle. The sandbox case dies before `main` because
|
|
115
|
+
`com.apple.security.app-sandbox` needs a bundled app with an
|
|
116
|
+
`application-identifier` that an ad-hoc signature cannot issue — a statement
|
|
117
|
+
about bare executables, not about WebKit. A `gjs` process also has no bundle
|
|
118
|
+
identifier, which some WebKit features key on.
|
|
119
|
+
- **No CI covers the input path**, on any platform. It is held by two by-hand
|
|
120
|
+
probes that both need a display, and `@gjsify/iframe`'s unit suite instantiates
|
|
121
|
+
no live WebView. See `status/open-todos.md` for the two event-injection routes
|
|
122
|
+
that were tried and are dead ends.
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gjsify/webkit-native",
|
|
3
|
+
"version": "0.30.0",
|
|
4
|
+
"description": "Apple WebKit (WKWebView) behind a GObject API shaped like WebKitGTK 6.0 — the darwin backend for @gjsify/iframe (ADR 0022). Contains no JavaScript; the prebuilt library and typelib arrive through per-target optionalDependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [],
|
|
7
|
+
"gjsify": {
|
|
8
|
+
"platforms": [
|
|
9
|
+
"darwin-x64",
|
|
10
|
+
"darwin-arm64"
|
|
11
|
+
],
|
|
12
|
+
"runtimes": {
|
|
13
|
+
"gjs": "polyfill",
|
|
14
|
+
"node": "none",
|
|
15
|
+
"browser": "none",
|
|
16
|
+
"nativescript": "none"
|
|
17
|
+
},
|
|
18
|
+
"os": {
|
|
19
|
+
"linux": "none",
|
|
20
|
+
"darwin": "supported",
|
|
21
|
+
"win32": "none"
|
|
22
|
+
},
|
|
23
|
+
"osNotes": {
|
|
24
|
+
"linux": "Binds Apple's WebKit.framework, which exists only on macOS. Linux has the real thing: @gjsify/iframe imports gi://WebKit 6.0 there and never reaches this package.",
|
|
25
|
+
"win32": "Binds Apple's WebKit.framework, which exists only on macOS. Windows has no GJS host either (see @gjsify/os win32 notes)."
|
|
26
|
+
},
|
|
27
|
+
"tier": 1
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"clear": "gjsify clear build _install",
|
|
31
|
+
"check": "echo 'no TypeScript in this package'",
|
|
32
|
+
"init:meson": "meson setup build .",
|
|
33
|
+
"init:meson:wipe": "gjsify run init:meson --wipe",
|
|
34
|
+
"build": "echo 'native only — see build:prebuilds'",
|
|
35
|
+
"build:meson": "gjsify run init:meson && meson compile -C build",
|
|
36
|
+
"build:prebuilds": "gjsify run build:meson && node ../../../scripts/stage-prebuild.mjs .",
|
|
37
|
+
"test": "echo 'covered by @gjsify/iframe on darwin'"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"gjs",
|
|
41
|
+
"webkit",
|
|
42
|
+
"wkwebview",
|
|
43
|
+
"darwin",
|
|
44
|
+
"macos"
|
|
45
|
+
],
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/gjsify/gjsify.git",
|
|
50
|
+
"directory": "packages/framework/webkit-native"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/gjsify/gjsify/issues"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/gjsify/gjsify/tree/main/packages/framework/webkit-native#readme",
|
|
56
|
+
"optionalDependencies": {
|
|
57
|
+
"@gjsify/webkit-native-darwin-arm64": "0.30.0",
|
|
58
|
+
"@gjsify/webkit-native-darwin-x64": "0.30.0"
|
|
59
|
+
}
|
|
60
|
+
}
|