bunite-core 0.12.1 → 0.16.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 +19 -2
- package/src/host/core/BrowserView.ts +515 -38
- package/src/host/core/SurfaceBrowserIPC.ts +53 -3
- package/src/host/core/SurfaceManager.ts +603 -30
- package/src/host/core/SurfaceRegistry.ts +9 -1
- package/src/host/core/inputDispatch.ts +147 -0
- package/src/host/events/webviewEvents.ts +25 -1
- package/src/host/log.ts +6 -1
- package/src/host/native.ts +263 -1
- package/src/host/preloadBundle.ts +7 -2
- package/src/native/linux/bunite_linux_ffi.cpp +427 -6
- package/src/native/linux/bunite_linux_internal.h +18 -0
- package/src/native/linux/bunite_linux_runtime.cpp +6 -1
- package/src/native/linux/bunite_linux_utils.cpp +2 -2
- package/src/native/linux/bunite_linux_view.cpp +296 -5
- package/src/native/mac/bunite_mac_ffi.mm +630 -8
- package/src/native/mac/bunite_mac_internal.h +19 -0
- package/src/native/mac/bunite_mac_utils.mm +2 -2
- package/src/native/mac/bunite_mac_view.mm +371 -9
- package/src/native/shared/ffi_exports.h +200 -2
- package/src/native/win/native_host_cef.cpp +186 -11
- package/src/native/win/native_host_ffi.cpp +1194 -1
- package/src/native/win/native_host_internal.h +35 -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 +1023 -12
- package/src/native/win-webview2/webview2_internal.h +25 -0
- package/src/native/win-webview2/webview2_runtime.cpp +403 -34
- 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 +340 -8
- package/src/rpc/index.ts +32 -0
- package/src/webview/native.ts +253 -51
- package/src/webview/polyfill.ts +283 -22
|
@@ -10,6 +10,10 @@ namespace {
|
|
|
10
10
|
|
|
11
11
|
constexpr const char* kViewIdKey = "bunite-view-id";
|
|
12
12
|
|
|
13
|
+
// Forward decls — on_create references these to wire popup-minted views.
|
|
14
|
+
gboolean on_decide_policy(WebKitWebView*, WebKitPolicyDecision*, WebKitPolicyDecisionType, gpointer);
|
|
15
|
+
void on_title_changed(GObject*, GParamSpec*, gpointer);
|
|
16
|
+
|
|
13
17
|
void emit_url(uint32_t view_id, const char* name, WebKitWebView* wv) {
|
|
14
18
|
const char* uri = webkit_web_view_get_uri(wv);
|
|
15
19
|
emitWebviewEvent(view_id, name, uri ? std::string(uri) : std::string{});
|
|
@@ -18,11 +22,15 @@ void emit_url(uint32_t view_id, const char* name, WebKitWebView* wv) {
|
|
|
18
22
|
void on_load_changed(WebKitWebView* wv, WebKitLoadEvent event, gpointer user_data) {
|
|
19
23
|
const uint32_t view_id = GPOINTER_TO_UINT(user_data);
|
|
20
24
|
switch (event) {
|
|
25
|
+
case WEBKIT_LOAD_STARTED:
|
|
26
|
+
emit_url(view_id, "load-start", wv);
|
|
27
|
+
break;
|
|
21
28
|
case WEBKIT_LOAD_COMMITTED:
|
|
22
29
|
emit_url(view_id, "did-navigate", wv);
|
|
23
30
|
queueViewRedraw(wv);
|
|
24
31
|
break;
|
|
25
32
|
case WEBKIT_LOAD_FINISHED:
|
|
33
|
+
emit_url(view_id, "load-finish", wv);
|
|
26
34
|
emit_url(view_id, "dom-ready", wv);
|
|
27
35
|
queueViewRedraw(wv);
|
|
28
36
|
break;
|
|
@@ -30,14 +38,107 @@ void on_load_changed(WebKitWebView* wv, WebKitLoadEvent event, gpointer user_dat
|
|
|
30
38
|
}
|
|
31
39
|
}
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
gboolean on_load_failed(WebKitWebView* /*wv*/, WebKitLoadEvent /*ev*/, const char* failing_uri,
|
|
42
|
+
GError* error, gpointer user_data) {
|
|
43
|
+
const uint32_t view_id = GPOINTER_TO_UINT(user_data);
|
|
44
|
+
std::string payload = "{\"url\":\"" + escapeJsonString(failing_uri ? failing_uri : "") +
|
|
45
|
+
"\",\"reason\":\"" + escapeJsonString(error && error->message ? error->message : "") + "\"}";
|
|
46
|
+
emitWebviewEvent(view_id, "load-fail", payload);
|
|
47
|
+
return FALSE; // let WebKit show its default error page
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
gboolean on_load_failed_tls(WebKitWebView* /*wv*/, const char* failing_uri,
|
|
51
|
+
GTlsCertificate* /*cert*/, GTlsCertificateFlags /*errors*/,
|
|
52
|
+
gpointer user_data) {
|
|
53
|
+
const uint32_t view_id = GPOINTER_TO_UINT(user_data);
|
|
54
|
+
std::string payload = "{\"url\":\"" + escapeJsonString(failing_uri ? failing_uri : "") +
|
|
55
|
+
"\",\"reason\":\"tls-certificate-error\"}";
|
|
56
|
+
emitWebviewEvent(view_id, "load-fail", payload);
|
|
57
|
+
return FALSE; // do not override default certificate failure behavior
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
gboolean on_script_dialog(WebKitWebView* /*wv*/, WebKitScriptDialog* dialog, gpointer user_data) {
|
|
34
61
|
const uint32_t view_id = GPOINTER_TO_UINT(user_data);
|
|
62
|
+
auto* v = findView(view_id);
|
|
63
|
+
if (!v) return FALSE;
|
|
64
|
+
WebKitScriptDialogType type = webkit_script_dialog_get_dialog_type(dialog);
|
|
65
|
+
const char* kind = nullptr;
|
|
66
|
+
switch (type) {
|
|
67
|
+
case WEBKIT_SCRIPT_DIALOG_ALERT: kind = "alert"; break;
|
|
68
|
+
case WEBKIT_SCRIPT_DIALOG_CONFIRM: kind = "confirm"; break;
|
|
69
|
+
case WEBKIT_SCRIPT_DIALOG_PROMPT: kind = "prompt"; break;
|
|
70
|
+
case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: kind = "beforeunload"; break;
|
|
71
|
+
default: return FALSE;
|
|
72
|
+
}
|
|
73
|
+
// Defer the dialog so the page execution stays paused until host responds.
|
|
74
|
+
webkit_script_dialog_ref(dialog);
|
|
75
|
+
const uint32_t rid = v->next_dialog_request_id++;
|
|
76
|
+
v->pending_dialogs[rid] = dialog;
|
|
77
|
+
const char* message = webkit_script_dialog_get_message(dialog);
|
|
78
|
+
std::string payload = "{\"requestId\":" + std::to_string(rid) +
|
|
79
|
+
",\"kind\":\"" + kind +
|
|
80
|
+
"\",\"message\":\"" + escapeJsonString(message ? message : "") + "\"";
|
|
81
|
+
if (type == WEBKIT_SCRIPT_DIALOG_PROMPT) {
|
|
82
|
+
const char* def = webkit_script_dialog_prompt_get_default_text(dialog);
|
|
83
|
+
payload += ",\"defaultPrompt\":\"" + escapeJsonString(def ? def : "") + "\"";
|
|
84
|
+
}
|
|
85
|
+
payload += "}";
|
|
86
|
+
emitWebviewEvent(view_id, "dialog", payload);
|
|
87
|
+
return TRUE; // we handled it
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
GtkWidget* on_create(WebKitWebView* wv, WebKitNavigationAction* action, gpointer user_data) {
|
|
91
|
+
const uint32_t opener_view_id = GPOINTER_TO_UINT(user_data);
|
|
35
92
|
WebKitURIRequest* req = webkit_navigation_action_get_request(action);
|
|
36
93
|
const char* uri = webkit_uri_request_get_uri(req);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
94
|
+
if (g_runtime.popup_blocking) {
|
|
95
|
+
std::string payload = "{\"url\":\"" + escapeJsonString(uri ? uri : "") + "\"}";
|
|
96
|
+
emitWebviewEvent(opener_view_id, "new-window-open", payload);
|
|
97
|
+
return nullptr;
|
|
98
|
+
}
|
|
99
|
+
static std::atomic<uint32_t> g_popup_seq{0x80000000u};
|
|
100
|
+
const uint32_t new_view_id = g_popup_seq.fetch_add(1);
|
|
101
|
+
// Share network-session + user-content-manager so cookies/preload-injection
|
|
102
|
+
// carry across the opener boundary.
|
|
103
|
+
WebKitWebView* popup = WEBKIT_WEB_VIEW(g_object_new(
|
|
104
|
+
WEBKIT_TYPE_WEB_VIEW,
|
|
105
|
+
"network-session", webkit_web_view_get_network_session(wv),
|
|
106
|
+
"user-content-manager", webkit_web_view_get_user_content_manager(wv),
|
|
107
|
+
nullptr));
|
|
108
|
+
g_object_ref_sink(popup);
|
|
109
|
+
g_object_set_data(G_OBJECT(popup), kViewIdKey, GUINT_TO_POINTER(new_view_id));
|
|
110
|
+
g_signal_connect(popup, "load-changed", G_CALLBACK(on_load_changed), GUINT_TO_POINTER(new_view_id));
|
|
111
|
+
g_signal_connect(popup, "load-failed", G_CALLBACK(on_load_failed), GUINT_TO_POINTER(new_view_id));
|
|
112
|
+
g_signal_connect(popup, "load-failed-with-tls-errors", G_CALLBACK(on_load_failed_tls), GUINT_TO_POINTER(new_view_id));
|
|
113
|
+
g_signal_connect(popup, "decide-policy", G_CALLBACK(on_decide_policy), GUINT_TO_POINTER(new_view_id));
|
|
114
|
+
g_signal_connect(popup, "create", G_CALLBACK(on_create), GUINT_TO_POINTER(new_view_id));
|
|
115
|
+
g_signal_connect(popup, "notify::title", G_CALLBACK(on_title_changed), GUINT_TO_POINTER(new_view_id));
|
|
116
|
+
g_signal_connect(popup, "script-dialog", G_CALLBACK(on_script_dialog), GUINT_TO_POINTER(new_view_id));
|
|
117
|
+
{
|
|
118
|
+
std::lock_guard<std::mutex> lock(g_runtime.object_mutex);
|
|
119
|
+
g_runtime.parked_popups[new_view_id] = popup;
|
|
120
|
+
auto& st = g_runtime.views[new_view_id];
|
|
121
|
+
st.webview = popup;
|
|
122
|
+
st.window_id = 0; // bound on adoption
|
|
123
|
+
st.container = GTK_WIDGET(popup);
|
|
124
|
+
}
|
|
125
|
+
if (g_runtime.popup_parent) {
|
|
126
|
+
GtkWidget* box = gtk_window_get_child(g_runtime.popup_parent);
|
|
127
|
+
if (box) gtk_box_append(GTK_BOX(box), GTK_WIDGET(popup));
|
|
128
|
+
}
|
|
129
|
+
std::string payload = "{\"newSurfaceId\":" + std::to_string(new_view_id) +
|
|
130
|
+
",\"url\":\"" + escapeJsonString(uri ? uri : "") +
|
|
131
|
+
"\",\"disposition\":\"popup\"}";
|
|
132
|
+
emitWebviewEvent(opener_view_id, "popup-requested", payload);
|
|
133
|
+
return GTK_WIDGET(popup);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
void on_title_changed(GObject* source, GParamSpec* /*pspec*/, gpointer user_data) {
|
|
137
|
+
const uint32_t view_id = GPOINTER_TO_UINT(user_data);
|
|
138
|
+
WebKitWebView* wv = WEBKIT_WEB_VIEW(source);
|
|
139
|
+
const char* title = webkit_web_view_get_title(wv);
|
|
140
|
+
std::string payload = "{\"title\":\"" + escapeJsonString(title ? title : "") + "\"}";
|
|
141
|
+
emitWebviewEvent(view_id, "title-changed", payload);
|
|
41
142
|
}
|
|
42
143
|
|
|
43
144
|
// WebKitGTK fires nav-action for sub-frames with no main-frame discriminator — iframes hit nav rules.
|
|
@@ -59,8 +160,126 @@ gboolean on_decide_policy(WebKitWebView* wv, WebKitPolicyDecision* decision,
|
|
|
59
160
|
return TRUE;
|
|
60
161
|
}
|
|
61
162
|
|
|
163
|
+
constexpr const char* kDownloadIdKey = "bunite-download-id";
|
|
164
|
+
std::atomic<uint64_t> g_download_seq{1};
|
|
165
|
+
|
|
166
|
+
uint32_t viewIdForDownload(WebKitDownload* download) {
|
|
167
|
+
WebKitWebView* wv = webkit_download_get_web_view(download);
|
|
168
|
+
if (!wv) return 0;
|
|
169
|
+
return GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(wv), kViewIdKey));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
void on_received_data(WebKitDownload* download, guint64 /*data_length*/, gpointer /*user_data*/) {
|
|
173
|
+
const uint32_t view_id = viewIdForDownload(download);
|
|
174
|
+
if (!view_id) return;
|
|
175
|
+
const uint64_t id = GPOINTER_TO_SIZE(g_object_get_data(G_OBJECT(download), kDownloadIdKey));
|
|
176
|
+
const guint64 received = webkit_download_get_received_data_length(download);
|
|
177
|
+
WebKitURIResponse* resp = webkit_download_get_response(download);
|
|
178
|
+
const guint64 total = resp ? webkit_uri_response_get_content_length(resp) : 0;
|
|
179
|
+
std::string payload = "{\"kind\":\"progress\",\"id\":\"linux-" + std::to_string(id) +
|
|
180
|
+
"\",\"receivedBytes\":" + std::to_string(received);
|
|
181
|
+
if (total > 0) payload += ",\"totalBytes\":" + std::to_string(total);
|
|
182
|
+
payload += "}";
|
|
183
|
+
emitWebviewEvent(view_id, "download-event", payload);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
void on_download_finished(WebKitDownload* download, gpointer /*user_data*/) {
|
|
187
|
+
const uint32_t view_id = viewIdForDownload(download);
|
|
188
|
+
if (!view_id) return;
|
|
189
|
+
const uint64_t id = GPOINTER_TO_SIZE(g_object_get_data(G_OBJECT(download), kDownloadIdKey));
|
|
190
|
+
const char* dest_uri = webkit_download_get_destination(download);
|
|
191
|
+
std::string dest = dest_uri ? dest_uri : "";
|
|
192
|
+
if (dest.rfind("file://", 0) == 0) dest = dest.substr(7);
|
|
193
|
+
std::string payload = "{\"kind\":\"completed\",\"id\":\"linux-" + std::to_string(id) +
|
|
194
|
+
"\",\"localPath\":\"" + escapeJsonString(dest) + "\"}";
|
|
195
|
+
emitWebviewEvent(view_id, "download-event", payload);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
void on_download_failed(WebKitDownload* download, GError* error, gpointer /*user_data*/) {
|
|
199
|
+
const uint32_t view_id = viewIdForDownload(download);
|
|
200
|
+
if (!view_id) return;
|
|
201
|
+
const uint64_t id = GPOINTER_TO_SIZE(g_object_get_data(G_OBJECT(download), kDownloadIdKey));
|
|
202
|
+
std::string reason = error && error->message ? error->message : "unknown";
|
|
203
|
+
std::string payload = "{\"kind\":\"failed\",\"id\":\"linux-" + std::to_string(id) +
|
|
204
|
+
"\",\"reason\":\"" + escapeJsonString(reason) + "\"}";
|
|
205
|
+
emitWebviewEvent(view_id, "download-event", payload);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// `decide-destination` is the gate: emits `started` / `blocked` and sets path.
|
|
209
|
+
// Returning TRUE tells WebKit we resolved (or cancelled) destination.
|
|
210
|
+
gboolean on_decide_destination(WebKitDownload* download, const gchar* suggested, gpointer /*user_data*/) {
|
|
211
|
+
const uint32_t view_id = viewIdForDownload(download);
|
|
212
|
+
if (!view_id) return FALSE;
|
|
213
|
+
ViewState* st = nullptr;
|
|
214
|
+
{
|
|
215
|
+
std::lock_guard<std::mutex> lk(g_runtime.object_mutex);
|
|
216
|
+
auto it = g_runtime.views.find(view_id);
|
|
217
|
+
if (it != g_runtime.views.end()) st = &it->second;
|
|
218
|
+
}
|
|
219
|
+
if (!st) return FALSE;
|
|
220
|
+
const uint64_t id = GPOINTER_TO_SIZE(g_object_get_data(G_OBJECT(download), kDownloadIdKey));
|
|
221
|
+
WebKitURIRequest* req = webkit_download_get_request(download);
|
|
222
|
+
const std::string url = (req && webkit_uri_request_get_uri(req)) ? webkit_uri_request_get_uri(req) : "";
|
|
223
|
+
|
|
224
|
+
const int32_t policy = st->download_policy.load();
|
|
225
|
+
if (policy != 0) {
|
|
226
|
+
const char* reason = (policy == 1) ? "ask-not-implemented" : "host-policy";
|
|
227
|
+
std::string payload = "{\"kind\":\"blocked\",\"id\":\"linux-" + std::to_string(id) +
|
|
228
|
+
"\",\"url\":\"" + escapeJsonString(url) +
|
|
229
|
+
"\",\"reason\":\"" + reason + "\"}";
|
|
230
|
+
emitWebviewEvent(view_id, "download-event", payload);
|
|
231
|
+
webkit_download_cancel(download);
|
|
232
|
+
return TRUE;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const std::string sug = suggested ? suggested : "download";
|
|
236
|
+
WebKitURIResponse* resp = webkit_download_get_response(download);
|
|
237
|
+
const std::string mime = (resp && webkit_uri_response_get_mime_type(resp)) ? webkit_uri_response_get_mime_type(resp) : "";
|
|
238
|
+
const guint64 total = resp ? webkit_uri_response_get_content_length(resp) : 0;
|
|
239
|
+
std::string started = "{\"kind\":\"started\",\"id\":\"linux-" + std::to_string(id) +
|
|
240
|
+
"\",\"url\":\"" + escapeJsonString(url) +
|
|
241
|
+
"\",\"suggestedFilename\":\"" + escapeJsonString(sug) +
|
|
242
|
+
"\",\"mimeType\":\"" + escapeJsonString(mime) + "\"";
|
|
243
|
+
if (total > 0) started += ",\"sizeBytes\":" + std::to_string(total);
|
|
244
|
+
started += "}";
|
|
245
|
+
emitWebviewEvent(view_id, "download-event", started);
|
|
246
|
+
|
|
247
|
+
std::string dir = st->download_dir;
|
|
248
|
+
if (dir.empty()) {
|
|
249
|
+
const char* d = g_get_user_special_dir(G_USER_DIRECTORY_DOWNLOAD);
|
|
250
|
+
dir = d ? d : "/tmp";
|
|
251
|
+
}
|
|
252
|
+
std::string path = dir + "/" + sug;
|
|
253
|
+
std::string uri = "file://" + path;
|
|
254
|
+
webkit_download_set_destination(download, uri.c_str());
|
|
255
|
+
return TRUE;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
void on_download_started(WebKitNetworkSession* /*session*/, WebKitDownload* download, gpointer /*user_data*/) {
|
|
259
|
+
const uint32_t view_id = viewIdForDownload(download);
|
|
260
|
+
if (!view_id) {
|
|
261
|
+
webkit_download_cancel(download);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const uint64_t id = g_download_seq.fetch_add(1);
|
|
265
|
+
g_object_set_data(G_OBJECT(download), kDownloadIdKey, GSIZE_TO_POINTER(id));
|
|
266
|
+
g_signal_connect(download, "decide-destination", G_CALLBACK(on_decide_destination), nullptr);
|
|
267
|
+
g_signal_connect(download, "received-data", G_CALLBACK(on_received_data), nullptr);
|
|
268
|
+
g_signal_connect(download, "finished", G_CALLBACK(on_download_finished), nullptr);
|
|
269
|
+
g_signal_connect(download, "failed", G_CALLBACK(on_download_failed), nullptr);
|
|
270
|
+
}
|
|
271
|
+
|
|
62
272
|
} // namespace
|
|
63
273
|
|
|
274
|
+
void wireDownloadHandlers() {
|
|
275
|
+
static bool wired = false;
|
|
276
|
+
if (wired) return;
|
|
277
|
+
WebKitNetworkSession* session = webkit_network_session_get_default();
|
|
278
|
+
if (!session) return;
|
|
279
|
+
g_signal_connect(session, "download-started", G_CALLBACK(on_download_started), nullptr);
|
|
280
|
+
wired = true;
|
|
281
|
+
}
|
|
282
|
+
|
|
64
283
|
ViewState* findView(uint32_t view_id) {
|
|
65
284
|
std::lock_guard<std::mutex> lock(g_runtime.object_mutex);
|
|
66
285
|
auto it = g_runtime.views.find(view_id);
|
|
@@ -72,6 +291,26 @@ uint32_t viewIdForWebView(WebKitWebView* wv) {
|
|
|
72
291
|
return GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(wv), kViewIdKey));
|
|
73
292
|
}
|
|
74
293
|
|
|
294
|
+
void respondToDialogRequest(uint32_t view_id, uint32_t request_id, bool accept,
|
|
295
|
+
const std::string& text) {
|
|
296
|
+
auto* v = findView(view_id);
|
|
297
|
+
if (!v) return;
|
|
298
|
+
auto it = v->pending_dialogs.find(request_id);
|
|
299
|
+
if (it == v->pending_dialogs.end()) return;
|
|
300
|
+
WebKitScriptDialog* dialog = it->second;
|
|
301
|
+
v->pending_dialogs.erase(it);
|
|
302
|
+
if (!dialog) return;
|
|
303
|
+
WebKitScriptDialogType type = webkit_script_dialog_get_dialog_type(dialog);
|
|
304
|
+
if (type == WEBKIT_SCRIPT_DIALOG_CONFIRM || type == WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM) {
|
|
305
|
+
webkit_script_dialog_confirm_set_confirmed(dialog, accept ? TRUE : FALSE);
|
|
306
|
+
} else if (type == WEBKIT_SCRIPT_DIALOG_PROMPT) {
|
|
307
|
+
if (accept) webkit_script_dialog_prompt_set_text(dialog, text.c_str());
|
|
308
|
+
else webkit_script_dialog_prompt_set_text(dialog, nullptr);
|
|
309
|
+
}
|
|
310
|
+
webkit_script_dialog_close(dialog);
|
|
311
|
+
webkit_script_dialog_unref(dialog);
|
|
312
|
+
}
|
|
313
|
+
|
|
75
314
|
bool createView(uint32_t view_id, uint32_t window_id,
|
|
76
315
|
const char* url, const char* html, const char* preload, const char* appres_root,
|
|
77
316
|
const char* navigation_rules_json, const char* preload_origins_json,
|
|
@@ -86,6 +325,8 @@ bool createView(uint32_t view_id, uint32_t window_id,
|
|
|
86
325
|
return false;
|
|
87
326
|
}
|
|
88
327
|
|
|
328
|
+
wireDownloadHandlers();
|
|
329
|
+
|
|
89
330
|
WebKitUserContentManager* ucm = webkit_user_content_manager_new();
|
|
90
331
|
|
|
91
332
|
// Origin-gate the preload so http(s) navigations don't inherit the RPC bridge.
|
|
@@ -122,8 +363,12 @@ bool createView(uint32_t view_id, uint32_t window_id,
|
|
|
122
363
|
registerAppresScheme(webkit_web_view_get_context(wv));
|
|
123
364
|
|
|
124
365
|
g_signal_connect(wv, "load-changed", G_CALLBACK(on_load_changed), GUINT_TO_POINTER(view_id));
|
|
366
|
+
g_signal_connect(wv, "load-failed", G_CALLBACK(on_load_failed), GUINT_TO_POINTER(view_id));
|
|
367
|
+
g_signal_connect(wv, "load-failed-with-tls-errors", G_CALLBACK(on_load_failed_tls), GUINT_TO_POINTER(view_id));
|
|
125
368
|
g_signal_connect(wv, "decide-policy", G_CALLBACK(on_decide_policy), GUINT_TO_POINTER(view_id));
|
|
126
369
|
g_signal_connect(wv, "create", G_CALLBACK(on_create), GUINT_TO_POINTER(view_id));
|
|
370
|
+
g_signal_connect(wv, "notify::title", G_CALLBACK(on_title_changed), GUINT_TO_POINTER(view_id));
|
|
371
|
+
g_signal_connect(wv, "script-dialog", G_CALLBACK(on_script_dialog), GUINT_TO_POINTER(view_id));
|
|
127
372
|
|
|
128
373
|
GtkWidget* container = GTK_WIDGET(wv);
|
|
129
374
|
if (auto_resize) {
|
|
@@ -206,6 +451,52 @@ void applyViewBounds(uint32_t view_id, double x, double y, double w, double h) {
|
|
|
206
451
|
queueViewRedraw(v->webview);
|
|
207
452
|
}
|
|
208
453
|
|
|
454
|
+
bool acceptParkedPopup(uint32_t new_view_id, uint32_t host_window_id, double x, double y, double w, double h) {
|
|
455
|
+
WebKitWebView* popup = nullptr;
|
|
456
|
+
{
|
|
457
|
+
std::lock_guard<std::mutex> lock(g_runtime.object_mutex);
|
|
458
|
+
auto it = g_runtime.parked_popups.find(new_view_id);
|
|
459
|
+
if (it == g_runtime.parked_popups.end()) return false;
|
|
460
|
+
popup = it->second;
|
|
461
|
+
g_runtime.parked_popups.erase(it);
|
|
462
|
+
}
|
|
463
|
+
auto* host = findWindow(host_window_id);
|
|
464
|
+
if (!host || !host->host) return false;
|
|
465
|
+
gtk_widget_unparent(GTK_WIDGET(popup));
|
|
466
|
+
GtkWidget* container = gtk_fixed_new();
|
|
467
|
+
gtk_widget_set_halign(container, GTK_ALIGN_START);
|
|
468
|
+
gtk_widget_set_valign(container, GTK_ALIGN_START);
|
|
469
|
+
gtk_widget_set_overflow(container, GTK_OVERFLOW_HIDDEN);
|
|
470
|
+
gtk_fixed_put(GTK_FIXED(container), GTK_WIDGET(popup), 0, 0);
|
|
471
|
+
gtk_overlay_add_overlay(host->host, container);
|
|
472
|
+
{
|
|
473
|
+
std::lock_guard<std::mutex> lock(g_runtime.object_mutex);
|
|
474
|
+
auto& st = g_runtime.views[new_view_id];
|
|
475
|
+
st.window_id = host_window_id;
|
|
476
|
+
st.container = container;
|
|
477
|
+
st.webview = popup;
|
|
478
|
+
}
|
|
479
|
+
applyViewBounds(new_view_id, x, y, w, h);
|
|
480
|
+
// Re-emit view-ready so TS BrowserView.adopt resolves its waiter — the
|
|
481
|
+
// initial `did-navigate` fired before the adopter registered.
|
|
482
|
+
emitWebviewEvent(new_view_id, "view-ready", std::string{});
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
void dismissParkedPopup(uint32_t new_view_id) {
|
|
487
|
+
WebKitWebView* popup = nullptr;
|
|
488
|
+
{
|
|
489
|
+
std::lock_guard<std::mutex> lock(g_runtime.object_mutex);
|
|
490
|
+
auto it = g_runtime.parked_popups.find(new_view_id);
|
|
491
|
+
if (it == g_runtime.parked_popups.end()) return;
|
|
492
|
+
popup = it->second;
|
|
493
|
+
g_runtime.parked_popups.erase(it);
|
|
494
|
+
g_runtime.views.erase(new_view_id);
|
|
495
|
+
}
|
|
496
|
+
gtk_widget_unparent(GTK_WIDGET(popup));
|
|
497
|
+
g_object_unref(popup);
|
|
498
|
+
}
|
|
499
|
+
|
|
209
500
|
void detachViewSideState(uint32_t view_id) {
|
|
210
501
|
bunite::WebviewContentStorage::instance().remove(view_id);
|
|
211
502
|
std::vector<uint32_t> request_ids;
|