bunite-core 0.12.0 → 0.14.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/package.json +4 -4
- package/src/host/core/App.ts +17 -1
- package/src/host/core/BrowserView.ts +197 -28
- package/src/host/core/SurfaceBrowserIPC.ts +44 -3
- package/src/host/core/SurfaceManager.ts +260 -28
- package/src/host/core/SurfaceRegistry.ts +9 -1
- package/src/host/core/inputDispatch.ts +147 -0
- package/src/host/events/webviewEvents.ts +8 -1
- package/src/host/native.ts +124 -1
- package/src/native/linux/bunite_linux_ffi.cpp +223 -6
- package/src/native/linux/bunite_linux_internal.h +6 -0
- package/src/native/linux/bunite_linux_runtime.cpp +1 -1
- package/src/native/linux/bunite_linux_utils.cpp +2 -2
- package/src/native/linux/bunite_linux_view.cpp +85 -0
- package/src/native/mac/bunite_mac_ffi.mm +356 -8
- package/src/native/mac/bunite_mac_internal.h +6 -0
- package/src/native/mac/bunite_mac_utils.mm +2 -2
- package/src/native/mac/bunite_mac_view.mm +144 -2
- package/src/native/shared/ffi_exports.h +135 -0
- package/src/native/win/native_host_cef.cpp +86 -3
- package/src/native/win/native_host_ffi.cpp +378 -1
- package/src/native/win/native_host_internal.h +13 -0
- package/src/native/win/native_host_utils.cpp +2 -1
- package/src/native/win/process_helper_win.cpp +54 -27
- package/src/native/win-webview2/bunite_webview2_ffi.cpp +303 -9
- package/src/native/win-webview2/webview2_internal.h +11 -0
- package/src/native/win-webview2/webview2_runtime.cpp +128 -12
- package/src/native/win-webview2/webview2_utils.cpp +30 -12
- package/src/preload/runtime.built.js +1 -1
- package/src/preload/runtime.ts +97 -0
- package/src/rpc/framework.ts +173 -4
- package/src/rpc/index.ts +21 -0
- package/src/webview/native.ts +126 -25
- package/src/webview/polyfill.ts +196 -12
|
@@ -16,6 +16,27 @@ extern "C" {
|
|
|
16
16
|
#endif
|
|
17
17
|
|
|
18
18
|
/** ABI version. Bump on any breaking change to symbol set / signatures.
|
|
19
|
+
* v9 (2026-05): adds `bunite_view_mouse` (move/down/up primitives for drag &
|
|
20
|
+
* hover) + `bunite_view_respond_dialog`. Webview event names
|
|
21
|
+
* expand to include `dialog` (alert/confirm/prompt/beforeunload)
|
|
22
|
+
* and `console-message` (the latter is RPC-pushed by preload,
|
|
23
|
+
* not emitted by native — listed here for the host-side event
|
|
24
|
+
* channel completeness). Capability bits add `MOUSE` (1<<11),
|
|
25
|
+
* `DIALOGS` (1<<12), `CONSOLE` (1<<13). `NATIVE_INPUT_TRUSTED`
|
|
26
|
+
* meaning stretches to include `mouse` (click/type/press/mouse
|
|
27
|
+
* all produce isTrusted=true on supporting backends; scroll and
|
|
28
|
+
* screenshot remain outside that guarantee).
|
|
29
|
+
* v8 (2026-05): `bunite_view_press` gains `action` (down/up/both), `extended`
|
|
30
|
+
* (Win 0xE0 scancode prefix), `location` (DOM KeyboardEvent
|
|
31
|
+
* location, 0/1/2/3) params. Webview event names expand to
|
|
32
|
+
* include `load-start` / `load-finish` / `load-fail`. Capability
|
|
33
|
+
* bit 2 renamed `TITLE_CHANGED` → `SURFACE_EVENTS` — value
|
|
34
|
+
* unchanged but semantic is now "unified surfaceEvents stream
|
|
35
|
+
* supported". A surface fires `load-finish` OR `load-fail` per
|
|
36
|
+
* navigation, never both.
|
|
37
|
+
* v7 (2026-05): adds `bunite_view_screenshot` + `bunite_view_capabilities`
|
|
38
|
+
* + capability bitset (`BuniteCapBit`).
|
|
39
|
+
* v6 (2026-05): adds input dispatch — `bunite_view_click/type/press/scroll`.
|
|
19
40
|
* v5 (2026-05): adds `bunite_view_evaluate` + `evaluate-result` webview event. */
|
|
20
41
|
BUNITE_EXPORT int32_t bunite_abi_version(void);
|
|
21
42
|
BUNITE_EXPORT void bunite_set_log_level(int32_t level);
|
|
@@ -124,6 +145,120 @@ BUNITE_EXPORT void bunite_view_set_anchor(uint32_t view_id, int mode, double ins
|
|
|
124
145
|
BUNITE_EXPORT void bunite_view_go_back(uint32_t view_id);
|
|
125
146
|
BUNITE_EXPORT void bunite_view_reload(uint32_t view_id);
|
|
126
147
|
BUNITE_EXPORT void bunite_view_remove(uint32_t view_id);
|
|
148
|
+
|
|
149
|
+
/* Input dispatch (ABI v6). Coordinates are CSS px, viewport-relative (TS
|
|
150
|
+
* normalizes devicePixelRatio + container offset). Modifier bitmask is
|
|
151
|
+
* Alt=1, Ctrl=2, Meta=4, Shift=8. Backends translate to their native form.
|
|
152
|
+
* No result envelope — `nativeInputTrusted` capability indicates DOM trust. */
|
|
153
|
+
BUNITE_EXPORT void bunite_view_click(
|
|
154
|
+
uint32_t view_id,
|
|
155
|
+
double x,
|
|
156
|
+
double y,
|
|
157
|
+
int32_t button, /* 0=left, 1=middle, 2=right */
|
|
158
|
+
int32_t click_count, /* >=1 */
|
|
159
|
+
uint32_t modifiers
|
|
160
|
+
);
|
|
161
|
+
/** Type UTF-8 text. Each codepoint becomes a CHAR / insertText event — no IME composition. */
|
|
162
|
+
BUNITE_EXPORT void bunite_view_type(uint32_t view_id, const char* text);
|
|
163
|
+
/** Press a key: down + (optional) char + up.
|
|
164
|
+
* `windows_vk_code` is Win32 VK_* for CEF / WebView2 CDP path.
|
|
165
|
+
* `mac_key_code` is the Quartz Event Services hardware key code (kVK_*) — separate
|
|
166
|
+
* from Win VK because DOM `KeyboardEvent.code` is derived from this on WebKit.
|
|
167
|
+
* `key` / `code` are DOM `KeyboardEvent.key` / `.code` strings, passed to CDP so
|
|
168
|
+
* the page sees the correct values. `character` is UTF-8 for the CHAR event;
|
|
169
|
+
* empty = skip char. 0 vk codes = skip the virtual-key down/up.
|
|
170
|
+
* `action`: 0=down only, 1=up only, 2=both (default). For Playwright-style
|
|
171
|
+
* modifier-held wrap (keydown → click → keyup). CHAR follows Playwright rule:
|
|
172
|
+
* emitted with down only when character is non-empty.
|
|
173
|
+
* `extended`: Win scancode 0xE0 prefix — Numpad-Enter AND nav-cluster
|
|
174
|
+
* (Arrow/Home/End/Insert/Delete/PageUp/PageDown/Meta/ContextMenu). Drives CEF
|
|
175
|
+
* `native_key_code`. Other backends ignore.
|
|
176
|
+
* `location`: DOM `KeyboardEvent.location` (0=standard, 1=left mod, 2=right
|
|
177
|
+
* mod, 3=numpad). WV2 CDP forwards as `location`. Most extended keys are
|
|
178
|
+
* location 0 — only NumpadEnter is location 3 here. */
|
|
179
|
+
BUNITE_EXPORT void bunite_view_press(
|
|
180
|
+
uint32_t view_id,
|
|
181
|
+
int32_t windows_vk_code,
|
|
182
|
+
int32_t mac_key_code,
|
|
183
|
+
const char* key,
|
|
184
|
+
const char* code,
|
|
185
|
+
const char* character,
|
|
186
|
+
uint32_t modifiers,
|
|
187
|
+
int32_t action,
|
|
188
|
+
bool extended,
|
|
189
|
+
int32_t location
|
|
190
|
+
);
|
|
191
|
+
/** Scroll at (x, y). dx/dy in CSS px; positive = right/down. */
|
|
192
|
+
BUNITE_EXPORT void bunite_view_scroll(
|
|
193
|
+
uint32_t view_id,
|
|
194
|
+
double dx,
|
|
195
|
+
double dy,
|
|
196
|
+
double x,
|
|
197
|
+
double y,
|
|
198
|
+
uint32_t modifiers
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
/** Raw mouse primitive. `action` 0=move, 1=down, 2=up. `button` 0=left, 1=middle,
|
|
202
|
+
* 2=right (ignored for move). Coordinates are CSS px viewport-relative. Drag is
|
|
203
|
+
* composed = down → move(s) → up. Backends without input support (linux GTK)
|
|
204
|
+
* treat as no-op. `modifiers` per-call atomic — no sticky state. */
|
|
205
|
+
BUNITE_EXPORT void bunite_view_mouse(
|
|
206
|
+
uint32_t view_id,
|
|
207
|
+
int32_t action,
|
|
208
|
+
double x,
|
|
209
|
+
double y,
|
|
210
|
+
int32_t button,
|
|
211
|
+
uint32_t modifiers
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
/** Respond to a page-initiated modal dialog previously announced via the
|
|
215
|
+
* webview event channel as `dialog` (`{requestId, kind, message, defaultPrompt?}`).
|
|
216
|
+
* `accept` decides confirm/beforeunload outcome and gates whether `text` is
|
|
217
|
+
* applied to `prompt`. Backends release the held page execution. */
|
|
218
|
+
BUNITE_EXPORT void bunite_view_respond_dialog(
|
|
219
|
+
uint32_t view_id,
|
|
220
|
+
uint32_t request_id,
|
|
221
|
+
bool accept,
|
|
222
|
+
const char* text
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
/** Per-view automation capability bitset. Each backend returns the bits it
|
|
226
|
+
* actually supports — TS layer decodes to `SurfaceCapabilities` object.
|
|
227
|
+
* Bits are locked at ABI v6; append-only. */
|
|
228
|
+
enum BuniteCapBit {
|
|
229
|
+
BUNITE_CAP_EVALUATE = 1u << 0,
|
|
230
|
+
BUNITE_CAP_CROSS_ORIGIN_EVAL = 1u << 1,
|
|
231
|
+
BUNITE_CAP_SURFACE_EVENTS = 1u << 2,
|
|
232
|
+
BUNITE_CAP_NATIVE_INPUT_TRUSTED = 1u << 3, /* click/type/press/mouse all isTrusted=true */
|
|
233
|
+
BUNITE_CAP_CLICK = 1u << 4,
|
|
234
|
+
BUNITE_CAP_TYPE = 1u << 5,
|
|
235
|
+
BUNITE_CAP_PRESS = 1u << 6,
|
|
236
|
+
BUNITE_CAP_SCROLL = 1u << 7,
|
|
237
|
+
BUNITE_CAP_SCREENSHOT = 1u << 8,
|
|
238
|
+
BUNITE_CAP_FORMAT_PNG = 1u << 9,
|
|
239
|
+
BUNITE_CAP_FORMAT_JPEG = 1u << 10,
|
|
240
|
+
BUNITE_CAP_MOUSE = 1u << 11,
|
|
241
|
+
BUNITE_CAP_DIALOGS = 1u << 12,
|
|
242
|
+
BUNITE_CAP_CONSOLE = 1u << 13,
|
|
243
|
+
};
|
|
244
|
+
BUNITE_EXPORT uint32_t bunite_view_capabilities(uint32_t view_id);
|
|
245
|
+
|
|
246
|
+
/** Capture the visible viewport as a PNG/JPEG image. Async — result reported
|
|
247
|
+
* via the webview event handler as `screenshot-result` with payload
|
|
248
|
+
* { requestId, ok: true, format, mime, dataBase64 }
|
|
249
|
+
* { requestId, ok: false, code, message }
|
|
250
|
+
* Error codes: `not_supported`, `runtime_error`, `timeout`, `black_frame` (CEF
|
|
251
|
+
* compositor surface unreachable). `quality` is 0–100 for JPEG only — ignored
|
|
252
|
+
* for PNG, and ignored entirely on WebView2 (`CapturePreview` has no quality
|
|
253
|
+
* parameter; output uses Edge's default ~80). `dataBase64` is base64-encoded
|
|
254
|
+
* image bytes; TS layer decodes. */
|
|
255
|
+
BUNITE_EXPORT void bunite_view_screenshot(
|
|
256
|
+
uint32_t view_id,
|
|
257
|
+
uint32_t request_id,
|
|
258
|
+
const char* format,
|
|
259
|
+
int32_t quality
|
|
260
|
+
);
|
|
261
|
+
|
|
127
262
|
BUNITE_EXPORT void bunite_view_open_devtools(uint32_t view_id);
|
|
128
263
|
BUNITE_EXPORT void bunite_view_close_devtools(uint32_t view_id);
|
|
129
264
|
BUNITE_EXPORT void bunite_view_toggle_devtools(uint32_t view_id);
|
|
@@ -85,8 +85,11 @@ class BuniteCefClient
|
|
|
85
85
|
public CefRequestHandler,
|
|
86
86
|
public CefResourceRequestHandler,
|
|
87
87
|
public CefPermissionHandler,
|
|
88
|
-
public CefDisplayHandler
|
|
88
|
+
public CefDisplayHandler,
|
|
89
|
+
public CefJSDialogHandler {
|
|
89
90
|
public:
|
|
91
|
+
// BuniteCefClient is constructed 1:1 with a `ViewHost*`; `last_title_` is
|
|
92
|
+
// therefore per-view. OnTitleChange runs on the CEF UI thread (single).
|
|
90
93
|
explicit BuniteCefClient(ViewHost* view)
|
|
91
94
|
: view_(view) {}
|
|
92
95
|
|
|
@@ -95,6 +98,49 @@ public:
|
|
|
95
98
|
CefRefPtr<CefRequestHandler> GetRequestHandler() override { return this; }
|
|
96
99
|
CefRefPtr<CefPermissionHandler> GetPermissionHandler() override { return this; }
|
|
97
100
|
CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }
|
|
101
|
+
CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() override { return this; }
|
|
102
|
+
|
|
103
|
+
bool OnJSDialog(CefRefPtr<CefBrowser>, const CefString& /*origin_url*/,
|
|
104
|
+
JSDialogType dialog_type, const CefString& message_text,
|
|
105
|
+
const CefString& default_prompt_text,
|
|
106
|
+
CefRefPtr<CefJSDialogCallback> callback,
|
|
107
|
+
bool& suppress_message) override {
|
|
108
|
+
CEF_REQUIRE_UI_THREAD();
|
|
109
|
+
const char* kind = (dialog_type == JSDIALOGTYPE_ALERT) ? "alert"
|
|
110
|
+
: (dialog_type == JSDIALOGTYPE_CONFIRM) ? "confirm" : "prompt";
|
|
111
|
+
const uint32_t rid = view_->next_dialog_request_id++;
|
|
112
|
+
view_->pending_dialogs[rid] = callback;
|
|
113
|
+
suppress_message = true; // we control the dialog UX; host sends the answer.
|
|
114
|
+
std::string payload = "{\"requestId\":" + std::to_string(rid) +
|
|
115
|
+
",\"kind\":\"" + kind +
|
|
116
|
+
"\",\"message\":\"" + bunite_win::escapeJsonString(message_text.ToString()) + "\"";
|
|
117
|
+
if (dialog_type == JSDIALOGTYPE_PROMPT) {
|
|
118
|
+
payload += ",\"defaultPrompt\":\"" + bunite_win::escapeJsonString(default_prompt_text.ToString()) + "\"";
|
|
119
|
+
}
|
|
120
|
+
payload += "}";
|
|
121
|
+
bunite_win::emitWebviewEvent(view_->id, "dialog", payload);
|
|
122
|
+
return true; // handled — CEF will not show its own UI.
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
bool OnBeforeUnloadDialog(CefRefPtr<CefBrowser>, const CefString& message_text,
|
|
126
|
+
bool /*is_reload*/,
|
|
127
|
+
CefRefPtr<CefJSDialogCallback> callback) override {
|
|
128
|
+
CEF_REQUIRE_UI_THREAD();
|
|
129
|
+
const uint32_t rid = view_->next_dialog_request_id++;
|
|
130
|
+
view_->pending_dialogs[rid] = callback;
|
|
131
|
+
std::string payload = "{\"requestId\":" + std::to_string(rid) +
|
|
132
|
+
",\"kind\":\"beforeunload\",\"message\":\"" +
|
|
133
|
+
bunite_win::escapeJsonString(message_text.ToString()) + "\"}";
|
|
134
|
+
bunite_win::emitWebviewEvent(view_->id, "dialog", payload);
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
void OnResetDialogState(CefRefPtr<CefBrowser>) override {
|
|
139
|
+
CEF_REQUIRE_UI_THREAD();
|
|
140
|
+
// Tab navigation / reload clears any stuck dialogs. Drop the callbacks —
|
|
141
|
+
// CEF won't deliver them anymore, and the page is already moving on.
|
|
142
|
+
view_->pending_dialogs.clear();
|
|
143
|
+
}
|
|
98
144
|
|
|
99
145
|
bool OnProcessMessageReceived(
|
|
100
146
|
CefRefPtr<CefBrowser> /*browser*/,
|
|
@@ -122,10 +168,25 @@ public:
|
|
|
122
168
|
|
|
123
169
|
void OnTitleChange(CefRefPtr<CefBrowser>, const CefString& title) override {
|
|
124
170
|
CEF_REQUIRE_UI_THREAD();
|
|
125
|
-
std::string
|
|
171
|
+
std::string s = title.ToString();
|
|
172
|
+
// Filter the transients CEF emits during initial / mid-load (blank doc
|
|
173
|
+
// placeholder, momentary URL-as-title) and dedup on the last emitted value.
|
|
174
|
+
if (s.empty()) return;
|
|
175
|
+
if (s == last_title_) return;
|
|
176
|
+
last_title_ = s;
|
|
177
|
+
std::string payload = "{\"title\":\"" + bunite_win::escapeJsonString(s) + "\"}";
|
|
126
178
|
bunite_win::emitWebviewEvent(view_->id, "title-changed", payload);
|
|
127
179
|
}
|
|
128
180
|
|
|
181
|
+
void OnAddressChange(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
|
|
182
|
+
const CefString& url) override {
|
|
183
|
+
CEF_REQUIRE_UI_THREAD();
|
|
184
|
+
if (!frame->IsMain()) return;
|
|
185
|
+
// URL commit point — parity with WV2 SourceChanged / mac didCommitNavigation.
|
|
186
|
+
// Distinct from load-finish (which is OnLoadEnd).
|
|
187
|
+
bunite_win::emitWebviewEvent(view_->id, "did-navigate", url.ToString());
|
|
188
|
+
}
|
|
189
|
+
|
|
129
190
|
void OnBeforeDevToolsPopup(
|
|
130
191
|
CefRefPtr<CefBrowser>,
|
|
131
192
|
CefWindowInfo&,
|
|
@@ -153,6 +214,7 @@ public:
|
|
|
153
214
|
void OnAfterCreated(CefRefPtr<CefBrowser> browser) override {
|
|
154
215
|
CEF_REQUIRE_UI_THREAD();
|
|
155
216
|
view_->browser = browser;
|
|
217
|
+
bunite_win::registerCdpObserverForView(view_);
|
|
156
218
|
{
|
|
157
219
|
std::lock_guard<std::mutex> lock(g_runtime.object_mutex);
|
|
158
220
|
g_runtime.browser_to_view_id[browser->GetIdentifier()] = view_->id;
|
|
@@ -270,6 +332,12 @@ public:
|
|
|
270
332
|
return true;
|
|
271
333
|
}
|
|
272
334
|
|
|
335
|
+
void OnLoadStart(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame, TransitionType) override {
|
|
336
|
+
CEF_REQUIRE_UI_THREAD();
|
|
337
|
+
if (!frame->IsMain()) return;
|
|
338
|
+
bunite_win::emitWebviewEvent(view_->id, "load-start", frame->GetURL().ToString());
|
|
339
|
+
}
|
|
340
|
+
|
|
273
341
|
void OnLoadEnd(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame, int) override {
|
|
274
342
|
CEF_REQUIRE_UI_THREAD();
|
|
275
343
|
if (!frame->IsMain()) {
|
|
@@ -277,10 +345,24 @@ public:
|
|
|
277
345
|
}
|
|
278
346
|
|
|
279
347
|
const std::string url = frame->GetURL().ToString();
|
|
280
|
-
bunite_win::emitWebviewEvent(view_->id, "
|
|
348
|
+
bunite_win::emitWebviewEvent(view_->id, "load-finish", url);
|
|
281
349
|
bunite_win::emitWebviewEvent(view_->id, "dom-ready", url);
|
|
282
350
|
}
|
|
283
351
|
|
|
352
|
+
void OnLoadError(CefRefPtr<CefBrowser>, CefRefPtr<CefFrame> frame,
|
|
353
|
+
ErrorCode errorCode, const CefString& errorText, const CefString& failedUrl) override {
|
|
354
|
+
CEF_REQUIRE_UI_THREAD();
|
|
355
|
+
if (!frame->IsMain()) return;
|
|
356
|
+
// ERR_ABORTED fires on user-initiated navigation cancellation — not a
|
|
357
|
+
// failure from the consumer's perspective. Filter to align with WV2.
|
|
358
|
+
if (errorCode == ERR_ABORTED) return;
|
|
359
|
+
std::string reason = errorText.ToString();
|
|
360
|
+
if (reason.empty()) reason = "ERR_" + std::to_string(static_cast<int>(errorCode));
|
|
361
|
+
std::string payload = "{\"url\":\"" + bunite_win::escapeJsonString(failedUrl.ToString()) +
|
|
362
|
+
"\",\"reason\":\"" + bunite_win::escapeJsonString(reason) + "\"}";
|
|
363
|
+
bunite_win::emitWebviewEvent(view_->id, "load-fail", payload);
|
|
364
|
+
}
|
|
365
|
+
|
|
284
366
|
bool OnShowPermissionPrompt(
|
|
285
367
|
CefRefPtr<CefBrowser>,
|
|
286
368
|
uint64_t,
|
|
@@ -341,6 +423,7 @@ public:
|
|
|
341
423
|
|
|
342
424
|
private:
|
|
343
425
|
ViewHost* view_;
|
|
426
|
+
std::string last_title_;
|
|
344
427
|
|
|
345
428
|
IMPLEMENT_REFCOUNTING(BuniteCefClient);
|
|
346
429
|
};
|