bunite-core 0.5.0 → 0.8.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 -2
- package/src/bun/core/App.ts +35 -34
- package/src/bun/core/BrowserView.ts +3 -9
- package/src/bun/core/singleInstanceLock.ts +91 -0
- package/src/bun/index.ts +5 -6
- package/src/bun/preload/inline.ts +1 -2
- package/src/bun/proc/native.ts +54 -78
- package/src/native/linux/bunite_linux_appres.cpp +173 -0
- package/src/native/linux/bunite_linux_ffi.cpp +263 -0
- package/src/native/linux/bunite_linux_internal.h +148 -0
- package/src/native/linux/bunite_linux_preload.cpp +1 -0
- package/src/native/linux/bunite_linux_runtime.cpp +120 -0
- package/src/native/linux/bunite_linux_utils.cpp +114 -0
- package/src/native/linux/bunite_linux_view.cpp +244 -0
- package/src/native/linux/bunite_linux_window.cpp +101 -0
- package/src/native/mac/bunite_mac_appres.mm +163 -0
- package/src/native/mac/bunite_mac_ffi.mm +470 -0
- package/src/native/mac/bunite_mac_internal.h +151 -0
- package/src/native/mac/bunite_mac_runtime.mm +15 -0
- package/src/native/mac/bunite_mac_utils.mm +121 -0
- package/src/native/mac/bunite_mac_view.mm +279 -0
- package/src/native/mac/bunite_mac_window.mm +187 -0
- package/src/native/shared/ffi_exports.h +13 -13
- package/src/native/shared/permissions.h +14 -0
- package/src/native/shared/webview_storage.h +6 -3
- package/src/native/win/native_host_cef.cpp +4 -8
- package/src/native/win/native_host_ffi.cpp +76 -123
- package/src/native/win/native_host_internal.h +5 -3
- package/src/native/win/native_host_runtime.cpp +2 -6
- package/src/native/win/native_host_utils.cpp +23 -52
- package/src/native/win/process_helper_win.cpp +1 -3
- package/src/preload/runtime.ts +1 -3
- package/src/preload/tsconfig.tsbuildinfo +1 -1
- package/src/preload/webviewElement.ts +3 -8
- package/src/shared/paths.ts +35 -44
- package/src/shared/platform.ts +1 -2
- package/src/shared/rpc.ts +4 -13
- package/src/shared/rpcDemux.ts +47 -2
- package/src/shared/webRpcHandler.ts +1 -3
- package/src/shared/webviewPolyfill.ts +64 -6
- package/src/bun/core/Utils.ts +0 -301
|
@@ -1,5 +1,22 @@
|
|
|
1
|
-
// Iframe
|
|
2
|
-
|
|
1
|
+
// Iframe fallback for web (no-op if native already registered). HTMLElement deref'd lazily so module is import-safe in Node/Bun.
|
|
2
|
+
|
|
3
|
+
// Default sandbox omits allow-same-origin / allow-top-navigation / allow-modals /
|
|
4
|
+
// allow-popups-to-escape-sandbox — popup escape stays opt-in so a sandboxed page
|
|
5
|
+
// can't launch unsandboxed auxiliary contexts by default.
|
|
6
|
+
const DEFAULT_SANDBOX = "allow-scripts allow-forms allow-popups";
|
|
7
|
+
const BLOCKED_SCHEME_RE = /^(javascript|data|vbscript|file|about):/i;
|
|
8
|
+
|
|
9
|
+
// WHATWG URL parsing strips embedded ASCII tab/LF/CR and leading C0/space
|
|
10
|
+
// before scheme detection. Mirror that so embedded controls (e.g. `java\nscript:`)
|
|
11
|
+
// can't bypass the scheme guard.
|
|
12
|
+
function normalizeForSchemeCheck(src: string): string {
|
|
13
|
+
return src.replace(/[\t\n\r]/g, "").replace(/^[\x00-\x20]+/, "");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isBlockedSrc(src: string | null | undefined): boolean {
|
|
17
|
+
if (typeof src !== "string") return false;
|
|
18
|
+
return BLOCKED_SCHEME_RE.test(normalizeForSchemeCheck(src));
|
|
19
|
+
}
|
|
3
20
|
|
|
4
21
|
let cachedClass: CustomElementConstructor | null = null;
|
|
5
22
|
|
|
@@ -7,18 +24,38 @@ function definePolyfillClass(): CustomElementConstructor {
|
|
|
7
24
|
if (cachedClass) return cachedClass;
|
|
8
25
|
|
|
9
26
|
class BuniteWebviewPolyfill extends HTMLElement {
|
|
10
|
-
static observedAttributes = ["src"];
|
|
27
|
+
static observedAttributes = ["src", "sandbox", "unsandboxed"];
|
|
11
28
|
|
|
12
29
|
private _iframe: HTMLIFrameElement | null = null;
|
|
13
30
|
|
|
31
|
+
private applySandbox(iframe: HTMLIFrameElement) {
|
|
32
|
+
if (this.hasAttribute("unsandboxed")) {
|
|
33
|
+
iframe.removeAttribute("sandbox");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const override = this.getAttribute("sandbox");
|
|
37
|
+
iframe.setAttribute("sandbox", override ?? DEFAULT_SANDBOX);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private dispatchBlocked(url: string) {
|
|
41
|
+
this.dispatchEvent(new CustomEvent("did-fail-load", { detail: { url, reason: "blocked-scheme" } }));
|
|
42
|
+
}
|
|
43
|
+
|
|
14
44
|
connectedCallback() {
|
|
15
45
|
if (this._iframe) return;
|
|
16
46
|
|
|
17
47
|
const iframe = document.createElement("iframe");
|
|
18
48
|
iframe.style.cssText = "display:block;width:100%;height:100%;border:0;background:inherit;";
|
|
49
|
+
iframe.referrerPolicy = "no-referrer";
|
|
50
|
+
this.applySandbox(iframe);
|
|
51
|
+
|
|
19
52
|
const src = this.getAttribute("src");
|
|
20
53
|
if (src) {
|
|
21
|
-
|
|
54
|
+
if (isBlockedSrc(src)) {
|
|
55
|
+
this.dispatchBlocked(src);
|
|
56
|
+
} else {
|
|
57
|
+
iframe.src = src;
|
|
58
|
+
}
|
|
22
59
|
}
|
|
23
60
|
|
|
24
61
|
iframe.addEventListener("load", () => {
|
|
@@ -26,7 +63,9 @@ function definePolyfillClass(): CustomElementConstructor {
|
|
|
26
63
|
try {
|
|
27
64
|
url = iframe.contentWindow?.location.href ?? url;
|
|
28
65
|
} catch {}
|
|
29
|
-
|
|
66
|
+
// Suppress the spurious about:blank load that fires after a blocked
|
|
67
|
+
// navigation (or before any explicit navigate).
|
|
68
|
+
if (isBlockedSrc(url)) return;
|
|
30
69
|
this.dispatchEvent(new CustomEvent("did-navigate", { detail: { url } }));
|
|
31
70
|
});
|
|
32
71
|
|
|
@@ -40,8 +79,16 @@ function definePolyfillClass(): CustomElementConstructor {
|
|
|
40
79
|
}
|
|
41
80
|
|
|
42
81
|
attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null) {
|
|
43
|
-
if (
|
|
82
|
+
if (!this._iframe) return;
|
|
83
|
+
if (name === "src") {
|
|
84
|
+
if (newValue && isBlockedSrc(newValue)) {
|
|
85
|
+
this.dispatchBlocked(newValue);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
44
88
|
this._iframe.src = newValue ?? "";
|
|
89
|
+
} else if (name === "sandbox" || name === "unsandboxed") {
|
|
90
|
+
// Sandbox token changes take effect on the next navigation per HTML spec.
|
|
91
|
+
this.applySandbox(this._iframe);
|
|
45
92
|
}
|
|
46
93
|
}
|
|
47
94
|
|
|
@@ -81,6 +128,17 @@ function definePolyfillClass(): CustomElementConstructor {
|
|
|
81
128
|
* environments and when the native CEF preload has already registered the element.
|
|
82
129
|
* `BuniteView` calls this automatically on construction; call directly only when
|
|
83
130
|
* using `<bunite-webview>` markup without instantiating `BuniteView`.
|
|
131
|
+
*
|
|
132
|
+
* Defaults (web fallback only — native paths bypass these):
|
|
133
|
+
* - `sandbox="allow-scripts allow-forms allow-popups"` (popup-escape stays opt-in).
|
|
134
|
+
* - `referrerpolicy="no-referrer"`.
|
|
135
|
+
* - `javascript:` / `data:` / `vbscript:` / `file:` / `about:` schemes blocked
|
|
136
|
+
* (with WHATWG URL-style normalization to defeat embedded-control bypass);
|
|
137
|
+
* navigation attempt dispatches `did-fail-load` with `detail.reason === "blocked-scheme"`.
|
|
138
|
+
*
|
|
139
|
+
* Opt-out attributes on `<bunite-webview>` (observed — mutations re-apply):
|
|
140
|
+
* - `sandbox="..."` — override the default sandbox token string verbatim.
|
|
141
|
+
* - `unsandboxed` — remove the sandbox attribute entirely (trusted-content escape hatch).
|
|
84
142
|
*/
|
|
85
143
|
export function registerBuniteWebviewPolyfill() {
|
|
86
144
|
if (typeof customElements === "undefined") return;
|
package/src/bun/core/Utils.ts
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
import { log } from "../../shared/log";
|
|
2
|
-
import { ensureNativeRuntime, showNativeMessageBox } from "../proc/native";
|
|
3
|
-
import { BrowserView } from "./BrowserView";
|
|
4
|
-
import { BrowserWindow, getLastFocusedWindowId } from "./BrowserWindow";
|
|
5
|
-
|
|
6
|
-
export type MessageBoxOptions = {
|
|
7
|
-
windowId?: number;
|
|
8
|
-
type?: "none" | "info" | "warning" | "error" | "question";
|
|
9
|
-
title?: string;
|
|
10
|
-
message?: string;
|
|
11
|
-
detail?: string;
|
|
12
|
-
buttons?: string[];
|
|
13
|
-
defaultId?: number;
|
|
14
|
-
cancelId?: number;
|
|
15
|
-
browser?: boolean;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export type MessageBoxResponse = {
|
|
19
|
-
response: number;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Pending request tracking
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
let nextRequestId = 1;
|
|
27
|
-
|
|
28
|
-
type PendingMessageBox = {
|
|
29
|
-
viewId: number;
|
|
30
|
-
fallbackResponse: number;
|
|
31
|
-
resolve: (response: number) => void;
|
|
32
|
-
timeoutId: ReturnType<typeof setTimeout>;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const pendingMessageBoxes = new Map<number, PendingMessageBox>();
|
|
36
|
-
|
|
37
|
-
export function handleMessageBoxResponse(requestId: number, response: number): boolean {
|
|
38
|
-
const pending = pendingMessageBoxes.get(requestId);
|
|
39
|
-
if (!pending) {
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
clearTimeout(pending.timeoutId);
|
|
43
|
-
pendingMessageBoxes.delete(requestId);
|
|
44
|
-
pending.resolve(typeof response === "number" && response >= 0 ? response : pending.fallbackResponse);
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function cancelPendingMessageBoxesForView(viewId: number): void {
|
|
49
|
-
for (const [requestId, pending] of pendingMessageBoxes) {
|
|
50
|
-
if (pending.viewId === viewId) {
|
|
51
|
-
clearTimeout(pending.timeoutId);
|
|
52
|
-
pendingMessageBoxes.delete(requestId);
|
|
53
|
-
pending.resolve(pending.fallbackResponse);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ---------------------------------------------------------------------------
|
|
59
|
-
// Preferred view selection
|
|
60
|
-
// ---------------------------------------------------------------------------
|
|
61
|
-
|
|
62
|
-
function getPreferredMessageBoxView(): BrowserView | null {
|
|
63
|
-
const allViews = BrowserView.getAll();
|
|
64
|
-
if (allViews.length === 0) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const focusedWindowId = getLastFocusedWindowId();
|
|
69
|
-
if (focusedWindowId != null) {
|
|
70
|
-
const view = allViews.find(v => v.windowId === focusedWindowId);
|
|
71
|
-
if (view) return view;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const allWindows = BrowserWindow.getAll();
|
|
75
|
-
for (const win of allWindows) {
|
|
76
|
-
const view = allViews.find(v => v.windowId === win.id);
|
|
77
|
-
if (view) return view;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return allViews[0] ?? null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
// Dialog script builder
|
|
85
|
-
// ---------------------------------------------------------------------------
|
|
86
|
-
|
|
87
|
-
function escapeJs(value: string): string {
|
|
88
|
-
return value
|
|
89
|
-
.replace(/\\/g, "\\\\")
|
|
90
|
-
.replace(/"/g, '\\"')
|
|
91
|
-
.replace(/\n/g, "\\n")
|
|
92
|
-
.replace(/\r/g, "\\r")
|
|
93
|
-
.replace(/\t/g, "\\t");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function buildBrowserMessageBoxScript(
|
|
97
|
-
requestId: number,
|
|
98
|
-
options: Required<Pick<MessageBoxOptions, "type">> & MessageBoxOptions
|
|
99
|
-
): string {
|
|
100
|
-
const buttons = options.buttons && options.buttons.length > 0 ? options.buttons : ["OK"];
|
|
101
|
-
const defaultId = Math.max(0, Math.min(options.defaultId ?? 0, buttons.length - 1));
|
|
102
|
-
const cancelId = options.cancelId != null && options.cancelId >= 0
|
|
103
|
-
? Math.max(0, Math.min(options.cancelId, buttons.length - 1))
|
|
104
|
-
: options.cancelId ?? -1;
|
|
105
|
-
|
|
106
|
-
const buttonsJson = JSON.stringify(buttons);
|
|
107
|
-
|
|
108
|
-
return `(() => {
|
|
109
|
-
const spec = {
|
|
110
|
-
requestId: ${requestId},
|
|
111
|
-
type: "${escapeJs(options.type ?? "info")}",
|
|
112
|
-
title: "${escapeJs(options.title ?? "")}",
|
|
113
|
-
message: "${escapeJs(options.message ?? "")}",
|
|
114
|
-
detail: "${escapeJs(options.detail ?? "")}",
|
|
115
|
-
buttons: ${buttonsJson},
|
|
116
|
-
defaultId: ${defaultId},
|
|
117
|
-
cancelId: ${cancelId}
|
|
118
|
-
};
|
|
119
|
-
const rootId = \`__bunite_message_box_\${spec.requestId}\`;
|
|
120
|
-
if (document.getElementById(rootId)) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const submit = (response) => {
|
|
125
|
-
void (typeof bunite !== "undefined" && bunite.invoke
|
|
126
|
-
? bunite.invoke("__bunite:messageBoxResponse", { requestId: spec.requestId, response }).catch(() => {})
|
|
127
|
-
: Promise.resolve());
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const mount = () => {
|
|
131
|
-
const host = document.body ?? document.documentElement;
|
|
132
|
-
if (!host) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const overlay = document.createElement("div");
|
|
137
|
-
overlay.id = rootId;
|
|
138
|
-
overlay.dataset.buniteMessageBox = "true";
|
|
139
|
-
overlay.dataset.buniteMessageBoxRequestId = String(spec.requestId);
|
|
140
|
-
overlay.tabIndex = -1;
|
|
141
|
-
overlay.style.cssText = [
|
|
142
|
-
"position:fixed",
|
|
143
|
-
"inset:0",
|
|
144
|
-
"display:flex",
|
|
145
|
-
"align-items:center",
|
|
146
|
-
"justify-content:center",
|
|
147
|
-
"padding:24px",
|
|
148
|
-
"background:rgba(15,23,42,0.42)",
|
|
149
|
-
"backdrop-filter:blur(6px)",
|
|
150
|
-
"z-index:2147483647",
|
|
151
|
-
"font-family:Segoe UI, Arial, sans-serif"
|
|
152
|
-
].join(";");
|
|
153
|
-
|
|
154
|
-
const panel = document.createElement("div");
|
|
155
|
-
panel.style.cssText = [
|
|
156
|
-
"width:min(480px, calc(100vw - 48px))",
|
|
157
|
-
"border-radius:16px",
|
|
158
|
-
"border:1px solid rgba(15,23,42,0.10)",
|
|
159
|
-
"background:#ffffff",
|
|
160
|
-
"box-shadow:0 24px 80px rgba(15,23,42,0.28)",
|
|
161
|
-
"padding:20px 20px 18px",
|
|
162
|
-
"color:#0f172a"
|
|
163
|
-
].join(";");
|
|
164
|
-
|
|
165
|
-
const accent = document.createElement("div");
|
|
166
|
-
const accentColor =
|
|
167
|
-
spec.type === "error" ? "#dc2626" :
|
|
168
|
-
spec.type === "warning" ? "#d97706" :
|
|
169
|
-
spec.type === "question" ? "#2563eb" :
|
|
170
|
-
"#0f766e";
|
|
171
|
-
accent.style.cssText = \`width:48px;height:4px;border-radius:999px;background:\${accentColor};margin-bottom:14px;\`;
|
|
172
|
-
panel.appendChild(accent);
|
|
173
|
-
|
|
174
|
-
if (spec.title) {
|
|
175
|
-
const heading = document.createElement("h1");
|
|
176
|
-
heading.textContent = spec.title;
|
|
177
|
-
heading.style.cssText = "margin:0 0 8px;font-size:20px;line-height:1.25;font-weight:700;";
|
|
178
|
-
panel.appendChild(heading);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (spec.message) {
|
|
182
|
-
const body = document.createElement("p");
|
|
183
|
-
body.textContent = spec.message;
|
|
184
|
-
body.style.cssText = "margin:0;font-size:14px;line-height:1.55;white-space:pre-wrap;";
|
|
185
|
-
panel.appendChild(body);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (spec.detail) {
|
|
189
|
-
const detail = document.createElement("p");
|
|
190
|
-
detail.textContent = spec.detail;
|
|
191
|
-
detail.style.cssText = "margin:10px 0 0;font-size:12px;line-height:1.55;color:#475569;white-space:pre-wrap;";
|
|
192
|
-
panel.appendChild(detail);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const buttonRow = document.createElement("div");
|
|
196
|
-
buttonRow.style.cssText = "display:flex;justify-content:flex-end;gap:10px;flex-wrap:wrap;margin-top:18px;";
|
|
197
|
-
spec.buttons.forEach((label, index) => {
|
|
198
|
-
const button = document.createElement("button");
|
|
199
|
-
button.type = "button";
|
|
200
|
-
button.textContent = label;
|
|
201
|
-
button.dataset.buniteMessageBoxButtonIndex = String(index);
|
|
202
|
-
button.style.cssText =
|
|
203
|
-
index === spec.defaultId
|
|
204
|
-
? "appearance:none;border:0;border-radius:999px;background:#111827;color:#ffffff;padding:10px 16px;font:600 13px Segoe UI, Arial, sans-serif;cursor:pointer;"
|
|
205
|
-
: "appearance:none;border:1px solid rgba(15,23,42,0.14);border-radius:999px;background:#f8fafc;color:#0f172a;padding:10px 16px;font:600 13px Segoe UI, Arial, sans-serif;cursor:pointer;";
|
|
206
|
-
button.addEventListener("click", () => {
|
|
207
|
-
overlay.remove();
|
|
208
|
-
submit(index);
|
|
209
|
-
});
|
|
210
|
-
buttonRow.appendChild(button);
|
|
211
|
-
});
|
|
212
|
-
panel.appendChild(buttonRow);
|
|
213
|
-
overlay.appendChild(panel);
|
|
214
|
-
host.appendChild(overlay);
|
|
215
|
-
|
|
216
|
-
overlay.addEventListener("click", (event) => {
|
|
217
|
-
if (event.target !== overlay) {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
overlay.remove();
|
|
221
|
-
submit(spec.cancelId >= 0 ? spec.cancelId : spec.defaultId);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
overlay.addEventListener("keydown", (event) => {
|
|
225
|
-
if (event.key !== "Escape") {
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
event.preventDefault();
|
|
229
|
-
overlay.remove();
|
|
230
|
-
submit(spec.cancelId >= 0 ? spec.cancelId : spec.defaultId);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
requestAnimationFrame(() => {
|
|
234
|
-
overlay.focus();
|
|
235
|
-
const defaultButton = overlay.querySelector(\`[data-bunite-message-box-button-index="\${spec.defaultId}"]\`);
|
|
236
|
-
if (defaultButton instanceof HTMLButtonElement) {
|
|
237
|
-
defaultButton.focus();
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
if (document.readyState === "loading") {
|
|
243
|
-
document.addEventListener("DOMContentLoaded", mount, { once: true });
|
|
244
|
-
} else {
|
|
245
|
-
mount();
|
|
246
|
-
}
|
|
247
|
-
})();`;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// ---------------------------------------------------------------------------
|
|
251
|
-
// Public API
|
|
252
|
-
// ---------------------------------------------------------------------------
|
|
253
|
-
|
|
254
|
-
export async function showMessageBox(
|
|
255
|
-
options: MessageBoxOptions = {}
|
|
256
|
-
): Promise<MessageBoxResponse> {
|
|
257
|
-
ensureNativeRuntime();
|
|
258
|
-
|
|
259
|
-
const windowId = options.windowId ?? 0;
|
|
260
|
-
|
|
261
|
-
if (options.browser) {
|
|
262
|
-
const view = getPreferredMessageBoxView();
|
|
263
|
-
if (view) {
|
|
264
|
-
const requestId = nextRequestId++;
|
|
265
|
-
const fallbackResponse = options.cancelId ?? options.defaultId ?? 0;
|
|
266
|
-
const script = buildBrowserMessageBoxScript(requestId, {
|
|
267
|
-
type: options.type ?? "info",
|
|
268
|
-
...options
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
view.bringToFront();
|
|
272
|
-
|
|
273
|
-
const response = await new Promise<number>((resolve) => {
|
|
274
|
-
const timeoutId = setTimeout(() => {
|
|
275
|
-
pendingMessageBoxes.delete(requestId);
|
|
276
|
-
resolve(fallbackResponse);
|
|
277
|
-
}, 15_000);
|
|
278
|
-
|
|
279
|
-
pendingMessageBoxes.set(requestId, {
|
|
280
|
-
viewId: view.id,
|
|
281
|
-
fallbackResponse,
|
|
282
|
-
resolve,
|
|
283
|
-
timeoutId
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
view.executeJavaScript(script);
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
return { response };
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
return { response: showNativeMessageBox(windowId, options) };
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
export function showMessageBoxSync(
|
|
297
|
-
options: MessageBoxOptions = {}
|
|
298
|
-
): MessageBoxResponse {
|
|
299
|
-
ensureNativeRuntime();
|
|
300
|
-
return { response: showNativeMessageBox(options.windowId ?? 0, options) };
|
|
301
|
-
}
|