sparkbun 0.1.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/bin/sparkbun.cjs +18 -0
- package/dist-linux-arm64/bsdiff +0 -0
- package/dist-linux-arm64/bspatch +0 -0
- package/dist-linux-arm64/libElectrobunCore.so +0 -0
- package/dist-linux-arm64/libNativeWrapper.so +0 -0
- package/dist-linux-arm64/libasar.so +0 -0
- package/dist-linux-x64/bsdiff +0 -0
- package/dist-linux-x64/bspatch +0 -0
- package/dist-linux-x64/libElectrobunCore.so +0 -0
- package/dist-linux-x64/libNativeWrapper.so +0 -0
- package/dist-linux-x64/libasar.so +0 -0
- package/dist-macos-arm64/bsdiff +0 -0
- package/dist-macos-arm64/bspatch +0 -0
- package/dist-macos-arm64/libElectrobunCore.dylib +0 -0
- package/dist-macos-arm64/libNativeWrapper.dylib +0 -0
- package/dist-macos-arm64/libasar.dylib +0 -0
- package/dist-macos-arm64/libwebgpu_dawn.dylib +0 -0
- package/dist-macos-arm64/preload-full.js +885 -0
- package/dist-macos-arm64/preload-sandboxed.js +111 -0
- package/dist-macos-arm64/process_helper +0 -0
- package/dist-win-x64/ElectrobunCore.dll +0 -0
- package/dist-win-x64/WebView2Loader.dll +0 -0
- package/dist-win-x64/bsdiff.exe +0 -0
- package/dist-win-x64/bspatch.exe +0 -0
- package/dist-win-x64/libNativeWrapper.dll +0 -0
- package/dist-win-x64/zig-asar/arm64/libasar.dll +0 -0
- package/dist-win-x64/zig-asar/x64/libasar.dll +0 -0
- package/package.json +47 -0
- package/scripts/build-and-upload-artifacts.js +207 -0
- package/scripts/gen-webgpu-ffi.mjs +162 -0
- package/scripts/install-windows-deps.ps1 +80 -0
- package/scripts/package-release.js +237 -0
- package/scripts/push-version.js +84 -0
- package/scripts/update-bun-version.ts +122 -0
- package/scripts/update-cef-version.ts +145 -0
- package/src/browser/builtinrpcSchema.ts +19 -0
- package/src/browser/global.d.ts +36 -0
- package/src/browser/index.ts +234 -0
- package/src/browser/webviewtag.ts +88 -0
- package/src/browser/wgputag.ts +48 -0
- package/src/bun/SparkBunConfig.ts +497 -0
- package/src/bun/__tests__/ffi-contract.test.ts +105 -0
- package/src/bun/core/ApplicationMenu.ts +70 -0
- package/src/bun/core/BrowserView.ts +416 -0
- package/src/bun/core/BrowserWindow.ts +396 -0
- package/src/bun/core/BuildConfig.ts +71 -0
- package/src/bun/core/ContextMenu.ts +75 -0
- package/src/bun/core/GpuWindow.ts +289 -0
- package/src/bun/core/Paths.ts +5 -0
- package/src/bun/core/Socket.ts +22 -0
- package/src/bun/core/Tray.ts +197 -0
- package/src/bun/core/Updater.ts +1131 -0
- package/src/bun/core/Utils.ts +487 -0
- package/src/bun/core/WGPUView.ts +167 -0
- package/src/bun/core/menuRoles.ts +181 -0
- package/src/bun/events/ApplicationEvents.ts +22 -0
- package/src/bun/events/event.ts +27 -0
- package/src/bun/events/eventEmitter.ts +45 -0
- package/src/bun/events/trayEvents.ts +11 -0
- package/src/bun/events/webviewEvents.ts +39 -0
- package/src/bun/events/windowEvents.ts +23 -0
- package/src/bun/index.ts +120 -0
- package/src/bun/preload/.generated/compiled.ts +2 -0
- package/src/bun/preload/build.ts +65 -0
- package/src/bun/preload/dragRegions.ts +41 -0
- package/src/bun/preload/encryption.ts +86 -0
- package/src/bun/preload/events.ts +171 -0
- package/src/bun/preload/globals.d.ts +45 -0
- package/src/bun/preload/index-sandboxed.ts +28 -0
- package/src/bun/preload/index.ts +77 -0
- package/src/bun/preload/internalRpc.ts +80 -0
- package/src/bun/preload/overlaySync.ts +107 -0
- package/src/bun/preload/webviewTag.ts +451 -0
- package/src/bun/preload/wgpuTag.ts +246 -0
- package/src/bun/proc/linux.md +43 -0
- package/src/bun/proc/native.ts +3253 -0
- package/src/bun/webGPU.ts +346 -0
- package/src/bun/webgpuAdapter.ts +3011 -0
- package/src/cli/bun.lockb +0 -0
- package/src/cli/index.ts +4653 -0
- package/src/cli/package-lock.json +81 -0
- package/src/cli/package.json +11 -0
- package/src/cli/templates/embedded.ts +2 -0
- package/src/core/build.zig +16 -0
- package/src/core/main.zig +3378 -0
- package/src/extractor/build.zig +22 -0
- package/src/installer/installer-template.ts +216 -0
- package/src/launcher/main.ts +221 -0
- package/src/native/build/libNativeWrapper.so +0 -0
- package/src/native/linux/build/nativeWrapper.o +0 -0
- package/src/native/linux/cef_loader.cpp +110 -0
- package/src/native/linux/cef_loader.h +28 -0
- package/src/native/linux/cef_process_helper_linux.cpp +160 -0
- package/src/native/linux/nativeWrapper.cpp +11768 -0
- package/src/native/macos/cef_process_helper_mac.cc +160 -0
- package/src/native/macos/nativeWrapper.mm +9172 -0
- package/src/native/shared/accelerator_parser.h +72 -0
- package/src/native/shared/app_paths.h +110 -0
- package/src/native/shared/asar.h +35 -0
- package/src/native/shared/cache_migration.h +244 -0
- package/src/native/shared/callbacks.h +57 -0
- package/src/native/shared/cef_response_filter.h +189 -0
- package/src/native/shared/chromium_flags.h +181 -0
- package/src/native/shared/config.h +66 -0
- package/src/native/shared/download_event.h +197 -0
- package/src/native/shared/ffi_helpers.h +139 -0
- package/src/native/shared/glob_match.h +59 -0
- package/src/native/shared/json_menu_parser.h +223 -0
- package/src/native/shared/mime_types.h +101 -0
- package/src/native/shared/navigation_rules.h +98 -0
- package/src/native/shared/partition_context.h +137 -0
- package/src/native/shared/pending_resize_queue.h +45 -0
- package/src/native/shared/permissions.h +118 -0
- package/src/native/shared/permissions_cef.h +74 -0
- package/src/native/shared/preload_script.h +71 -0
- package/src/native/shared/shutdown_guard.h +134 -0
- package/src/native/shared/thread_safe_map.h +138 -0
- package/src/native/shared/webview_storage.h +91 -0
- package/src/native/win/cef_process_helper_win.cpp +143 -0
- package/src/native/win/dcomp_compositor.h +352 -0
- package/src/native/win/nativeWrapper.cpp +12434 -0
- package/src/npmbin/index.js +34 -0
- package/src/shared/bun-version.ts +3 -0
- package/src/shared/cef-version.ts +5 -0
- package/src/shared/naming.test.ts +327 -0
- package/src/shared/naming.ts +188 -0
- package/src/shared/platform.ts +48 -0
- package/src/shared/rpc.ts +541 -0
- package/src/shared/sparkbun-version.ts +2 -0
- package/src/types/three.d.ts +1 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { ffi } from "../proc/native";
|
|
2
|
+
import sparkBunEventEmitter from "../events/eventEmitter";
|
|
3
|
+
import {
|
|
4
|
+
type SparkBunRPCSchema,
|
|
5
|
+
type SparkBunRPCConfig,
|
|
6
|
+
type RPCWithTransport,
|
|
7
|
+
defineSparkBunRPC,
|
|
8
|
+
} from "../../shared/rpc.js";
|
|
9
|
+
import { BuildConfig } from "./BuildConfig";
|
|
10
|
+
import {
|
|
11
|
+
sendMessageToWebviewViaSocket,
|
|
12
|
+
removeSocketForWebview,
|
|
13
|
+
} from "./Socket";
|
|
14
|
+
import { randomBytes } from "crypto";
|
|
15
|
+
import { type Pointer } from "bun:ffi";
|
|
16
|
+
|
|
17
|
+
const BrowserViewMap: {
|
|
18
|
+
[id: number]: BrowserView<any>;
|
|
19
|
+
} = {};
|
|
20
|
+
|
|
21
|
+
export type BrowserViewOptions<T = undefined> = {
|
|
22
|
+
url: string | null;
|
|
23
|
+
html: string | null;
|
|
24
|
+
preload: string | null;
|
|
25
|
+
viewsRoot: string | null;
|
|
26
|
+
renderer: "native" | "cef";
|
|
27
|
+
partition: string | null;
|
|
28
|
+
frame: {
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
};
|
|
34
|
+
rpc: T;
|
|
35
|
+
hostWebviewId: number;
|
|
36
|
+
autoResize: boolean;
|
|
37
|
+
windowId: number;
|
|
38
|
+
navigationRules: string | null;
|
|
39
|
+
// Sandbox mode: when true, disables RPC and only allows event emission
|
|
40
|
+
// Use for untrusted content (remote URLs) to prevent malicious sites from
|
|
41
|
+
// accessing internal APIs, creating OOPIFs, or communicating with Bun
|
|
42
|
+
sandbox: boolean;
|
|
43
|
+
// Set transparent on the AbstractView at creation (before first paint)
|
|
44
|
+
startTransparent: boolean;
|
|
45
|
+
// Set passthrough on the AbstractView at creation (before first paint)
|
|
46
|
+
startPassthrough: boolean;
|
|
47
|
+
// renderer:
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const buildConfig = BuildConfig.getSync();
|
|
51
|
+
|
|
52
|
+
const defaultOptions: Partial<BrowserViewOptions> = {
|
|
53
|
+
url: null,
|
|
54
|
+
html: null,
|
|
55
|
+
preload: null,
|
|
56
|
+
viewsRoot: null,
|
|
57
|
+
renderer: buildConfig.defaultRenderer,
|
|
58
|
+
frame: {
|
|
59
|
+
x: 0,
|
|
60
|
+
y: 0,
|
|
61
|
+
width: 800,
|
|
62
|
+
height: 600,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
export class BrowserView<T extends RPCWithTransport = RPCWithTransport> {
|
|
66
|
+
id = 0;
|
|
67
|
+
hostWebviewId?: number;
|
|
68
|
+
windowId!: number;
|
|
69
|
+
renderer!: "cef" | "native";
|
|
70
|
+
url: string | null = null;
|
|
71
|
+
html: string | null = null;
|
|
72
|
+
preload: string | null = null;
|
|
73
|
+
viewsRoot: string | null = null;
|
|
74
|
+
partition: string | null = null;
|
|
75
|
+
autoResize: boolean = true;
|
|
76
|
+
frame: {
|
|
77
|
+
x: number;
|
|
78
|
+
y: number;
|
|
79
|
+
width: number;
|
|
80
|
+
height: number;
|
|
81
|
+
} = {
|
|
82
|
+
x: 0,
|
|
83
|
+
y: 0,
|
|
84
|
+
width: 800,
|
|
85
|
+
height: 600,
|
|
86
|
+
};
|
|
87
|
+
secretKey!: Uint8Array;
|
|
88
|
+
rpc?: T;
|
|
89
|
+
rpcHandler?: (msg: unknown) => void;
|
|
90
|
+
navigationRules: string | null = null;
|
|
91
|
+
// Sandbox mode disables RPC and only allows event emission (for untrusted content)
|
|
92
|
+
sandbox: boolean = false;
|
|
93
|
+
startTransparent: boolean = false;
|
|
94
|
+
startPassthrough: boolean = false;
|
|
95
|
+
isRemoved: boolean = false;
|
|
96
|
+
|
|
97
|
+
get ptr(): Pointer | null {
|
|
98
|
+
if (this.isRemoved) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return ffi.request.getWebviewPointer({ id: this.id }) as Pointer | null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
constructor(options: Partial<BrowserViewOptions<T>> = defaultOptions) {
|
|
105
|
+
// const rpc = options.rpc;
|
|
106
|
+
|
|
107
|
+
this.url = options.url || defaultOptions.url || null;
|
|
108
|
+
this.html = options.html || defaultOptions.html || null;
|
|
109
|
+
this.preload = options.preload || defaultOptions.preload || null;
|
|
110
|
+
this.viewsRoot = options.viewsRoot || defaultOptions.viewsRoot || null;
|
|
111
|
+
this.frame = {
|
|
112
|
+
x: options.frame?.x ?? defaultOptions.frame!.x,
|
|
113
|
+
y: options.frame?.y ?? defaultOptions.frame!.y,
|
|
114
|
+
width: options.frame?.width ?? defaultOptions.frame!.width,
|
|
115
|
+
height: options.frame?.height ?? defaultOptions.frame!.height,
|
|
116
|
+
};
|
|
117
|
+
this.rpc = options.rpc;
|
|
118
|
+
this.secretKey = new Uint8Array(randomBytes(32));
|
|
119
|
+
this.partition = options.partition || null;
|
|
120
|
+
this.hostWebviewId = options.hostWebviewId;
|
|
121
|
+
this.windowId = options.windowId ?? 0;
|
|
122
|
+
this.autoResize = options.autoResize === false ? false : true;
|
|
123
|
+
this.navigationRules = options.navigationRules || null;
|
|
124
|
+
this.renderer = options.renderer ?? defaultOptions.renderer ?? "native";
|
|
125
|
+
this.sandbox = options.sandbox ?? false;
|
|
126
|
+
this.startTransparent = options.startTransparent ?? false;
|
|
127
|
+
this.startPassthrough = options.startPassthrough ?? false;
|
|
128
|
+
|
|
129
|
+
this.id = this.init() as number;
|
|
130
|
+
BrowserViewMap[this.id] = this;
|
|
131
|
+
|
|
132
|
+
// If HTML content was provided, load it after webview creation.
|
|
133
|
+
if (this.html) {
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
this.loadHTML(this.html!);
|
|
136
|
+
}, 100);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
init() {
|
|
141
|
+
this.initializeRpcTransport();
|
|
142
|
+
|
|
143
|
+
return ffi.request.createWebview({
|
|
144
|
+
windowId: this.windowId,
|
|
145
|
+
hostWebviewId: this.hostWebviewId ?? null,
|
|
146
|
+
renderer: this.renderer,
|
|
147
|
+
// todo: consider sending secretKey as base64
|
|
148
|
+
secretKey: this.secretKey.toString(),
|
|
149
|
+
partition: this.partition,
|
|
150
|
+
// Only pass URL if no HTML content is provided to avoid conflicts
|
|
151
|
+
url: this.html ? null : this.url,
|
|
152
|
+
preload: this.preload,
|
|
153
|
+
viewsRoot: this.viewsRoot,
|
|
154
|
+
frame: {
|
|
155
|
+
width: this.frame.width,
|
|
156
|
+
height: this.frame.height,
|
|
157
|
+
x: this.frame.x,
|
|
158
|
+
y: this.frame.y,
|
|
159
|
+
},
|
|
160
|
+
autoResize: this.autoResize,
|
|
161
|
+
navigationRules: this.navigationRules,
|
|
162
|
+
sandbox: this.sandbox,
|
|
163
|
+
startTransparent: this.startTransparent,
|
|
164
|
+
startPassthrough: this.startPassthrough,
|
|
165
|
+
// transparent is looked up from parent window in native.ts
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
initializeRpcTransport() {
|
|
170
|
+
if (!this.rpc) {
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
|
+
this.rpc = BrowserView.defineRPC({
|
|
173
|
+
handlers: { requests: {}, messages: {} },
|
|
174
|
+
}) as any;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.rpc!.setTransport(this.createTransport());
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
sendHostMessageToWebviewViaExecute(jsonMessage: unknown) {
|
|
181
|
+
const stringifiedMessage =
|
|
182
|
+
typeof jsonMessage === "string"
|
|
183
|
+
? jsonMessage
|
|
184
|
+
: JSON.stringify(jsonMessage);
|
|
185
|
+
const wrappedMessage = `window.__sparkbun.receiveMessageFromHost(${stringifiedMessage})`;
|
|
186
|
+
this.executeJavascript(wrappedMessage);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
sendInternalHostMessageViaExecute(jsonMessage: unknown) {
|
|
190
|
+
const stringifiedMessage =
|
|
191
|
+
typeof jsonMessage === "string"
|
|
192
|
+
? jsonMessage
|
|
193
|
+
: JSON.stringify(jsonMessage);
|
|
194
|
+
const wrappedMessage = `window.__sparkbun.receiveInternalMessageFromHost(${stringifiedMessage})`;
|
|
195
|
+
this.executeJavascript(wrappedMessage);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Note: the OS has a buffer limit on named pipes. If we overflow it
|
|
199
|
+
// it won't trigger the kevent for zig to read the pipe and we'll be stuck.
|
|
200
|
+
// so we have to chunk it
|
|
201
|
+
// TODO: is this still needed after switching from named pipes
|
|
202
|
+
executeJavascript(js: string) {
|
|
203
|
+
if (!this.ptr || this.isRemoved) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
ffi.request.evaluateJavascriptWithNoCompletion({ id: this.id, js });
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
loadURL(url: string) {
|
|
210
|
+
this.url = url;
|
|
211
|
+
ffi.request.loadURLInWebView({ id: this.id, url: this.url });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
loadHTML(html: string) {
|
|
215
|
+
this.html = html;
|
|
216
|
+
|
|
217
|
+
if (this.renderer === "cef") {
|
|
218
|
+
// For CEF, store HTML content in native map and use scheme handler
|
|
219
|
+
ffi.request.setWebviewHTMLContent({ id: this.id, html });
|
|
220
|
+
this.loadURL("views://internal/index.html");
|
|
221
|
+
} else {
|
|
222
|
+
// For WKWebView, load HTML content directly
|
|
223
|
+
ffi.request.loadHTMLInWebView({ id: this.id, html });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
setNavigationRules(rules: string[]) {
|
|
228
|
+
this.navigationRules = JSON.stringify(rules);
|
|
229
|
+
const rulesJson = JSON.stringify(rules);
|
|
230
|
+
ffi.request.setWebviewNavigationRules({ id: this.id, rulesJson });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
findInPage(
|
|
234
|
+
searchText: string,
|
|
235
|
+
options?: { forward?: boolean; matchCase?: boolean },
|
|
236
|
+
) {
|
|
237
|
+
const forward = options?.forward ?? true;
|
|
238
|
+
const matchCase = options?.matchCase ?? false;
|
|
239
|
+
ffi.request.webviewFindInPage({
|
|
240
|
+
id: this.id,
|
|
241
|
+
searchText,
|
|
242
|
+
forward,
|
|
243
|
+
matchCase,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
stopFindInPage() {
|
|
248
|
+
ffi.request.webviewStopFind({ id: this.id });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
openDevTools() {
|
|
252
|
+
ffi.request.webviewOpenDevTools({ id: this.id });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
closeDevTools() {
|
|
256
|
+
ffi.request.webviewCloseDevTools({ id: this.id });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
toggleDevTools() {
|
|
260
|
+
ffi.request.webviewToggleDevTools({ id: this.id });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Set the page zoom level (WebKit only, similar to browser zoom).
|
|
265
|
+
* @param zoomLevel - The zoom level (1.0 = 100%, 1.5 = 150%, etc.)
|
|
266
|
+
*/
|
|
267
|
+
setPageZoom(zoomLevel: number) {
|
|
268
|
+
ffi.request.webviewSetPageZoom({ id: this.id, zoomLevel });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Get the current page zoom level.
|
|
273
|
+
* @returns The current zoom level (1.0 = 100%)
|
|
274
|
+
*/
|
|
275
|
+
getPageZoom(): number {
|
|
276
|
+
return ffi.request.webviewGetPageZoom({ id: this.id }) as number;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// name should only allow browserView events
|
|
280
|
+
// Note: normalize event names to willNavigate instead of ['will-navigate'] to save
|
|
281
|
+
// 5 characters per usage and allow minification to be more effective.
|
|
282
|
+
on(
|
|
283
|
+
name:
|
|
284
|
+
| "will-navigate"
|
|
285
|
+
| "did-navigate"
|
|
286
|
+
| "did-navigate-in-page"
|
|
287
|
+
| "did-commit-navigation"
|
|
288
|
+
| "dom-ready"
|
|
289
|
+
| "download-started"
|
|
290
|
+
| "download-progress"
|
|
291
|
+
| "download-completed"
|
|
292
|
+
| "download-failed",
|
|
293
|
+
handler: (event: unknown) => void,
|
|
294
|
+
) {
|
|
295
|
+
const specificName = `${name}-${this.id}`;
|
|
296
|
+
sparkBunEventEmitter.on(specificName, handler);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
createTransport = () => {
|
|
300
|
+
const that = this;
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
send(message: any) {
|
|
304
|
+
if (!that.ptr || that.isRemoved) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const sentOverSocket = sendMessageToWebviewViaSocket(that.id, message);
|
|
308
|
+
|
|
309
|
+
if (!sentOverSocket) {
|
|
310
|
+
try {
|
|
311
|
+
const messageString = JSON.stringify(message);
|
|
312
|
+
that.sendHostMessageToWebviewViaExecute(messageString);
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error("host: failed to serialize message to webview", error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
registerHandler(handler: (msg: unknown) => void) {
|
|
319
|
+
if (that.isRemoved) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
that.rpcHandler = handler;
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
remove() {
|
|
328
|
+
if (this.isRemoved) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
this.isRemoved = true;
|
|
332
|
+
// Drop JS-side references first so late callbacks cannot target a stale view.
|
|
333
|
+
delete BrowserViewMap[this.id];
|
|
334
|
+
removeSocketForWebview(this.id);
|
|
335
|
+
this.rpc?.setTransport({
|
|
336
|
+
send() {},
|
|
337
|
+
registerHandler() {},
|
|
338
|
+
unregisterHandler() {},
|
|
339
|
+
});
|
|
340
|
+
this.rpcHandler = undefined;
|
|
341
|
+
try {
|
|
342
|
+
ffi.request.webviewRemove({ id: this.id });
|
|
343
|
+
} catch (error) {
|
|
344
|
+
console.error(`Error removing webview ${this.id}:`, error);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
static getById(id: number) {
|
|
349
|
+
return BrowserViewMap[id];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Core can create webviews before Bun has constructed a JS wrapper for them.
|
|
353
|
+
// Use this in native/runtime paths that need to ensure a wrapper exists.
|
|
354
|
+
static ensureWrapped<T extends RPCWithTransport = RPCWithTransport>(
|
|
355
|
+
id: number,
|
|
356
|
+
options: Partial<BrowserViewOptions<T>> = {},
|
|
357
|
+
) {
|
|
358
|
+
return (
|
|
359
|
+
(BrowserViewMap[id] as BrowserView<T> | undefined) ??
|
|
360
|
+
BrowserView.adoptExisting(id, options)
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
static adoptExisting<T extends RPCWithTransport = RPCWithTransport>(
|
|
365
|
+
id: number,
|
|
366
|
+
options: Partial<BrowserViewOptions<T>> = {},
|
|
367
|
+
) {
|
|
368
|
+
const existing = BrowserViewMap[id] as BrowserView<T> | undefined;
|
|
369
|
+
if (existing) {
|
|
370
|
+
return existing;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const ptr = ffi.request.getWebviewPointer({ id }) as Pointer | null;
|
|
374
|
+
if (!ptr) {
|
|
375
|
+
return undefined;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const view = Object.create(BrowserView.prototype) as BrowserView<T>;
|
|
379
|
+
view.id = id;
|
|
380
|
+
view.hostWebviewId = options.hostWebviewId;
|
|
381
|
+
view.windowId = options.windowId ?? 0;
|
|
382
|
+
view.renderer = options.renderer ?? defaultOptions.renderer ?? "native";
|
|
383
|
+
view.url = options.url ?? defaultOptions.url ?? null;
|
|
384
|
+
view.html = options.html ?? defaultOptions.html ?? null;
|
|
385
|
+
view.preload = options.preload ?? defaultOptions.preload ?? null;
|
|
386
|
+
view.viewsRoot = options.viewsRoot ?? defaultOptions.viewsRoot ?? null;
|
|
387
|
+
view.partition = options.partition ?? null;
|
|
388
|
+
view.frame = {
|
|
389
|
+
x: options.frame?.x ?? defaultOptions.frame!.x,
|
|
390
|
+
y: options.frame?.y ?? defaultOptions.frame!.y,
|
|
391
|
+
width: options.frame?.width ?? defaultOptions.frame!.width,
|
|
392
|
+
height: options.frame?.height ?? defaultOptions.frame!.height,
|
|
393
|
+
};
|
|
394
|
+
view.secretKey = new Uint8Array(0);
|
|
395
|
+
view.rpc = options.rpc;
|
|
396
|
+
view.rpcHandler = undefined;
|
|
397
|
+
view.autoResize = options.autoResize === false ? false : true;
|
|
398
|
+
view.navigationRules = options.navigationRules ?? null;
|
|
399
|
+
view.sandbox = options.sandbox ?? false;
|
|
400
|
+
view.startTransparent = options.startTransparent ?? false;
|
|
401
|
+
view.startPassthrough = options.startPassthrough ?? false;
|
|
402
|
+
view.isRemoved = false;
|
|
403
|
+
BrowserViewMap[id] = view as BrowserView<any>;
|
|
404
|
+
return view;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
static getAll() {
|
|
408
|
+
return Object.values(BrowserViewMap);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
static defineRPC<Schema extends SparkBunRPCSchema>(
|
|
412
|
+
config: SparkBunRPCConfig<Schema, "bun">,
|
|
413
|
+
) {
|
|
414
|
+
return defineSparkBunRPC("bun", config);
|
|
415
|
+
}
|
|
416
|
+
}
|