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
|
@@ -34,9 +34,11 @@
|
|
|
34
34
|
#include "include/cef_app.h"
|
|
35
35
|
#include "include/cef_browser.h"
|
|
36
36
|
#include "include/cef_client.h"
|
|
37
|
+
#include "include/cef_devtools_message_observer.h"
|
|
37
38
|
#include "include/cef_command_line.h"
|
|
38
39
|
#include "include/cef_parser.h"
|
|
39
40
|
#include "include/cef_permission_handler.h"
|
|
41
|
+
#include "include/cef_jsdialog_handler.h"
|
|
40
42
|
#include "include/cef_resource_handler.h"
|
|
41
43
|
#include "include/cef_resource_request_handler.h"
|
|
42
44
|
#include "include/cef_scheme.h"
|
|
@@ -97,6 +99,27 @@ struct ViewHost {
|
|
|
97
99
|
std::atomic<bool> closing = false;
|
|
98
100
|
CefRefPtr<CefBrowser> browser;
|
|
99
101
|
CefRefPtr<CefClient> client;
|
|
102
|
+
// DevTools observer registration — kept alive for the life of the view so
|
|
103
|
+
// CDP method results (screenshot etc.) can route back to bunite handlers.
|
|
104
|
+
CefRefPtr<CefRegistration> devtools_registration;
|
|
105
|
+
|
|
106
|
+
// Page-initiated dialog callbacks held until host responds via
|
|
107
|
+
// `respondToDialogRequest`. CEF holds the page execution while we wait.
|
|
108
|
+
std::unordered_map<uint32_t, CefRefPtr<CefJSDialogCallback>> pending_dialogs;
|
|
109
|
+
uint32_t next_dialog_request_id = 1;
|
|
110
|
+
|
|
111
|
+
// Download policy: 0=auto, 1=ask, 2=block. Default block.
|
|
112
|
+
std::atomic<int32_t> download_policy{2};
|
|
113
|
+
std::string download_dir;
|
|
114
|
+
|
|
115
|
+
// True for ViewHosts minted by OnBeforePopup; popup_accept binds them to a
|
|
116
|
+
// user window, popup_dismiss destroys them.
|
|
117
|
+
bool is_popup_pending = false;
|
|
118
|
+
// If popup_accept arrives before OnAfterCreated, stash parameters; the
|
|
119
|
+
// browser-create completion applies them.
|
|
120
|
+
struct PendingPopupAccept { uint32_t host_window_id; double x, y, w, h; };
|
|
121
|
+
std::optional<PendingPopupAccept> pending_popup_accept;
|
|
122
|
+
bool popup_dismiss_requested = false;
|
|
100
123
|
|
|
101
124
|
// Pending state: applied in OnAfterCreated when browser HWND becomes available.
|
|
102
125
|
bool pending_visible = true;
|
|
@@ -104,6 +127,12 @@ struct ViewHost {
|
|
|
104
127
|
bool pending_passthrough = false;
|
|
105
128
|
bool has_pending_bounds = false;
|
|
106
129
|
RECT pending_bounds{0, 0, 0, 0};
|
|
130
|
+
|
|
131
|
+
// OOPIF input dispatch — populated by Target.attachedToTarget events after
|
|
132
|
+
// lazy Target.setAutoAttach. frameId → sessionId for flatten:true routing.
|
|
133
|
+
std::atomic<bool> oopif_autoattach_armed = false;
|
|
134
|
+
std::mutex oopif_sessions_mutex;
|
|
135
|
+
std::unordered_map<std::string, std::string> oopif_sessions; // frameId → sessionId
|
|
107
136
|
};
|
|
108
137
|
|
|
109
138
|
struct WindowHost {
|
|
@@ -198,6 +227,9 @@ bool shouldAllowNavigation(const ViewHost* view, const std::string& url);
|
|
|
198
227
|
void emitWindowEvent(uint32_t window_id, const char* event_name, const std::string& payload = {});
|
|
199
228
|
void emitWebviewEvent(uint32_t view_id, const char* event_name, const std::string& payload = {});
|
|
200
229
|
|
|
230
|
+
// Bind a popup ViewHost (created by OnBeforePopup) to a user window + bounds.
|
|
231
|
+
void applyPopupAccept(ViewHost* view, uint32_t host_window_id, double x, double y, double w, double h);
|
|
232
|
+
|
|
201
233
|
std::string normalizeAppResPath(const std::string& url);
|
|
202
234
|
std::string getMimeType(const std::filesystem::path& file_path);
|
|
203
235
|
std::string getAppResRootForView(uint32_t view_id);
|
|
@@ -214,6 +246,9 @@ void finalizeWindowHost(WindowHost* window); // [UI thread]
|
|
|
214
246
|
void openDevToolsForView(ViewHost* view); // [CEF UI thread]
|
|
215
247
|
void closeDevToolsForView(ViewHost* view); // [CEF UI thread]
|
|
216
248
|
void toggleDevToolsForView(ViewHost* view); // [CEF UI thread]
|
|
249
|
+
void registerCdpObserverForView(ViewHost* view); // [CEF UI thread]
|
|
250
|
+
void respondToDialogRequest(ViewHost* view, uint32_t request_id,
|
|
251
|
+
bool accept, const std::string& text); // [CEF UI thread]
|
|
217
252
|
bool initializeCefOnUiThread(); // [UI thread]
|
|
218
253
|
void shutdownCefOnUiThread(); // [UI thread]
|
|
219
254
|
void cancelPendingPermissionRequestsOnUiThread(); // [CEF UI thread]
|
|
@@ -211,7 +211,8 @@ std::vector<std::string> parseNavigationRulesJson(const std::string& rules_json)
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
bool shouldAlwaysAllowNavigationUrl(const std::string& url) {
|
|
214
|
-
|
|
214
|
+
// Exact-match — prefix would let `../../evil` style paths bypass scrutiny.
|
|
215
|
+
return url == "about:blank" || url == "appres://app.internal/internal/index.html";
|
|
215
216
|
}
|
|
216
217
|
|
|
217
218
|
uint32_t cefPermissionsToBuniteKind(uint32_t cef_bits) {
|
|
@@ -48,10 +48,10 @@ public:
|
|
|
48
48
|
std::string script = args->GetString(1).ToString();
|
|
49
49
|
|
|
50
50
|
auto context = frame->GetV8Context();
|
|
51
|
+
auto reply = CefProcessMessage::Create("bunite.evaluate.result");
|
|
52
|
+
auto rl = reply->GetArgumentList();
|
|
53
|
+
rl->SetInt(0, static_cast<int>(request_id));
|
|
51
54
|
if (!context) {
|
|
52
|
-
auto reply = CefProcessMessage::Create("bunite.evaluate.result");
|
|
53
|
-
auto rl = reply->GetArgumentList();
|
|
54
|
-
rl->SetInt(0, static_cast<int>(request_id));
|
|
55
55
|
rl->SetBool(1, false);
|
|
56
56
|
rl->SetString(2, "runtime_error");
|
|
57
57
|
rl->SetString(3, "no V8 context");
|
|
@@ -59,37 +59,64 @@ public:
|
|
|
59
59
|
return true;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// Same wrapper as WebView2/mac/linux: try/catch returning a JSON envelope
|
|
63
|
+
// string. SecurityError is detected locale-independently inside the JS.
|
|
64
|
+
std::string wrapped =
|
|
65
|
+
"(function(){try{return JSON.stringify({__bunite_ok:true,value:(" + script +
|
|
66
|
+
")})}catch(e){var c=(e&&e.name===\"SecurityError\")?\"cross_origin\":\"runtime_error\";"
|
|
67
|
+
"return JSON.stringify({__bunite_ok:false,code:c,"
|
|
68
|
+
"message:(e&&e.message)?e.message:String(e),"
|
|
69
|
+
"name:(e&&e.name)||\"\"})}})()";
|
|
70
|
+
|
|
62
71
|
context->Enter();
|
|
63
72
|
CefRefPtr<CefV8Value> retval;
|
|
64
73
|
CefRefPtr<CefV8Exception> exception;
|
|
65
|
-
bool ok = context->Eval(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
bool ok = context->Eval(wrapped, "bunite://evaluate", 0, retval, exception);
|
|
75
|
+
|
|
76
|
+
if (ok && retval && retval->IsString()) {
|
|
77
|
+
std::string inner = retval->GetStringValue().ToString();
|
|
78
|
+
if (inner.find("\"__bunite_ok\":true") != std::string::npos) {
|
|
79
|
+
static const std::string prefix = "{\"__bunite_ok\":true,\"value\":";
|
|
80
|
+
std::string value_json = "null";
|
|
81
|
+
if (inner.compare(0, prefix.size(), prefix) == 0 &&
|
|
82
|
+
inner.size() > prefix.size() + 1) {
|
|
83
|
+
value_json = inner.substr(prefix.size(), inner.size() - prefix.size() - 1);
|
|
84
|
+
}
|
|
85
|
+
rl->SetBool(1, true);
|
|
86
|
+
rl->SetString(2, value_json);
|
|
87
|
+
rl->SetString(3, "");
|
|
88
|
+
} else {
|
|
89
|
+
// Anchor at the envelope prefix — user-controlled e.message could
|
|
90
|
+
// otherwise inject a fake "code" via the substring scan above.
|
|
91
|
+
static const std::string codePrefix = "{\"__bunite_ok\":false,\"code\":\"";
|
|
92
|
+
std::string code = "runtime_error";
|
|
93
|
+
std::string msg = "script threw";
|
|
94
|
+
if (inner.compare(0, codePrefix.size(), codePrefix) == 0) {
|
|
95
|
+
size_t start = codePrefix.size();
|
|
96
|
+
size_t end = start;
|
|
97
|
+
while (end < inner.size() && inner[end] != '"') ++end;
|
|
98
|
+
if (end > start) code = inner.substr(start, end - start);
|
|
99
|
+
static const std::string msgKey = "\",\"message\":\"";
|
|
100
|
+
if (end + msgKey.size() <= inner.size() &&
|
|
101
|
+
inner.compare(end, msgKey.size(), msgKey) == 0) {
|
|
102
|
+
size_t mstart = end + msgKey.size();
|
|
103
|
+
size_t mend = mstart;
|
|
104
|
+
while (mend < inner.size()) {
|
|
105
|
+
if (inner[mend] == '"' && (mend == mstart || inner[mend - 1] != '\\')) break;
|
|
106
|
+
++mend;
|
|
107
|
+
}
|
|
108
|
+
if (mend > mstart) msg = inner.substr(mstart, mend - mstart);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
rl->SetBool(1, false);
|
|
112
|
+
rl->SetString(2, code);
|
|
113
|
+
rl->SetString(3, msg);
|
|
80
114
|
}
|
|
81
|
-
if (json_str.empty()) json_str = "null";
|
|
82
|
-
rl->SetBool(1, true);
|
|
83
|
-
rl->SetString(2, json_str);
|
|
84
|
-
rl->SetString(3, "");
|
|
85
115
|
} else {
|
|
116
|
+
// Wrapper itself failed (syntax error in user script, or V8 internal).
|
|
86
117
|
std::string msg = exception ? exception->GetMessage().ToString() : "eval failed";
|
|
87
|
-
// SecurityError typically arises from cross-origin reach.
|
|
88
|
-
std::string code = (msg.find("SecurityError") != std::string::npos ||
|
|
89
|
-
msg.find("cross-origin") != std::string::npos)
|
|
90
|
-
? "cross_origin" : "runtime_error";
|
|
91
118
|
rl->SetBool(1, false);
|
|
92
|
-
rl->SetString(2,
|
|
119
|
+
rl->SetString(2, "runtime_error");
|
|
93
120
|
rl->SetString(3, msg);
|
|
94
121
|
}
|
|
95
122
|
context->Exit();
|