aboutsummaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-25 13:08:22 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-25 13:26:32 +0100
commitfaeceb71f3303dd36e8ac506cf6a9aaf25fa5862 (patch)
treee889845d53ff87e262bf03a29abba060bb14399a /packages
parentfeat: Fuse the electron binary (diff)
downloadsophie-faeceb71f3303dd36e8ac506cf6a9aaf25fa5862.tar.gz
sophie-faeceb71f3303dd36e8ac506cf6a9aaf25fa5862.tar.zst
sophie-faeceb71f3303dd36e8ac506cf6a9aaf25fa5862.zip
refactor: Fetch service inject asynchronously
Since we don't plan to shim any APIs that must be present immediately when the service loads, we might as well switch to asynchronous IPC for fetching the script to inject into the main world.
Diffstat (limited to 'packages')
-rw-r--r--packages/main/src/index.ts7
-rw-r--r--packages/service-preload/src/index.ts41
2 files changed, 36 insertions, 12 deletions
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts
index 4a51518..b134002 100644
--- a/packages/main/src/index.ts
+++ b/packages/main/src/index.ts
@@ -219,8 +219,8 @@ function createWindow(): Promise<unknown> {
219 webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); 219 webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch);
220 }); 220 });
221 221
222 ipcMain.on(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => { 222 ipcMain.handle(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => {
223 event.returnValue = event.sender.id == browserView.webContents.id 223 return event.sender.id == browserView.webContents.id
224 ? serviceInject 224 ? serviceInject
225 : null; 225 : null;
226 }); 226 });
@@ -229,7 +229,8 @@ function createWindow(): Promise<unknown> {
229 try { 229 try {
230 switch (channel) { 230 switch (channel) {
231 case ServiceToMainIpcMessage.ApiExposedInMainWorld: 231 case ServiceToMainIpcMessage.ApiExposedInMainWorld:
232 // Synchronous message must be handled with `ipcMain.on` 232 // Asynchronous message with reply must be handled in `ipcMain.handle`,
233 // otherwise electron emits a no handler registered warning.
233 break; 234 break;
234 case ServiceToMainIpcMessage.SetUnreadCount: 235 case ServiceToMainIpcMessage.SetUnreadCount:
235 console.log('Unread count:', unreadCount.parse(args[0])); 236 console.log('Unread count:', unreadCount.parse(args[0]));
diff --git a/packages/service-preload/src/index.ts b/packages/service-preload/src/index.ts
index e42c406..d1ea13c 100644
--- a/packages/service-preload/src/index.ts
+++ b/packages/service-preload/src/index.ts
@@ -27,13 +27,36 @@ if (webFrame.parent === null) {
27 webFrame.insertCSS('html { background-color: #fff; }'); 27 webFrame.insertCSS('html { background-color: #fff; }');
28} 28}
29 29
30const injectSource = webSource.safeParse(ipcRenderer.sendSync(ServiceToMainIpcMessage.ApiExposedInMainWorld)); 30/**
31if (injectSource.success) { 31 * Fetches and executes the service inject script in the isolated world.
32 webFrame.executeJavaScriptInIsolatedWorld(0, [ 32 *
33 injectSource.data, 33 * The service inject script relies on exposed APIs, so this function can only
34 ]).catch((err) => { 34 * be called after APIs have been exposed via `contextBridge` to the main world.
35 console.log('Failed to inject source:', err); 35 *
36 }); 36 * We have to call `executeJavaScriptInIsolatedWorld` from the service preload script,
37} else { 37 * beause there is no way currently (electron 16) to execute a script on a
38 console.log('Invalid source to inject:', injectSource.error); 38 * `WebFrameMain` in the main process by specifying a `WebSource`.
39 * Calling `executeJavaScriptInInsolatedWorld` on a `WebContents` in the main process
40 * will always inject the script into the _top-level_ frame, but here we
41 * are injecting into the _current_ frame instead.
42 * As a tradeoff, the promise returned by `executeJavaScriptInIsolatedWorld`
43 * will resolve to `unknown` (instead of rejecting) even if the injected script fails,
44 * because chromium doesn't dispatch main world errors to isolated worlds.
45 *
46 * @return A promise that only rejects if we fail to fetch the inject script.
47 * @see https://www.electronjs.org/docs/latest/api/web-frame#webframeexecutejavascriptinisolatedworldworldid-scripts-usergesture-callback
48 * @see https://www.electronjs.org/docs/latest/api/web-frame-main#frameexecutejavascriptcode-usergesture
49 * @see https://www.electronjs.org/docs/latest/api/web-contents#contentsexecutejavascriptinisolatedworldworldid-scripts-usergesture
50 */
51async function fetchAndExecuteInjectScript(): Promise<void> {
52 const apiExposedResponse = await ipcRenderer.invoke(
53 ServiceToMainIpcMessage.ApiExposedInMainWorld,
54 );
55 const injectSource = webSource.parse(apiExposedResponse);
56 // Isolated world 0 is the main world.
57 return webFrame.executeJavaScriptInIsolatedWorld(0, [injectSource]);
39} 58}
59
60fetchAndExecuteInjectScript().catch((err) => {
61 console.log('Failed to fetch inject source:', err);
62});