From faeceb71f3303dd36e8ac506cf6a9aaf25fa5862 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 25 Dec 2021 13:08:22 +0100 Subject: 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. --- packages/main/src/index.ts | 7 +++--- packages/service-preload/src/index.ts | 41 +++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 12 deletions(-) (limited to 'packages') 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 { webContents.send(MainToRendererIpcMessage.SharedStorePatch, patch); }); - ipcMain.on(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => { - event.returnValue = event.sender.id == browserView.webContents.id + ipcMain.handle(ServiceToMainIpcMessage.ApiExposedInMainWorld, (event) => { + return event.sender.id == browserView.webContents.id ? serviceInject : null; }); @@ -229,7 +229,8 @@ function createWindow(): Promise { try { switch (channel) { case ServiceToMainIpcMessage.ApiExposedInMainWorld: - // Synchronous message must be handled with `ipcMain.on` + // Asynchronous message with reply must be handled in `ipcMain.handle`, + // otherwise electron emits a no handler registered warning. break; case ServiceToMainIpcMessage.SetUnreadCount: 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) { webFrame.insertCSS('html { background-color: #fff; }'); } -const injectSource = webSource.safeParse(ipcRenderer.sendSync(ServiceToMainIpcMessage.ApiExposedInMainWorld)); -if (injectSource.success) { - webFrame.executeJavaScriptInIsolatedWorld(0, [ - injectSource.data, - ]).catch((err) => { - console.log('Failed to inject source:', err); - }); -} else { - console.log('Invalid source to inject:', injectSource.error); +/** + * Fetches and executes the service inject script in the isolated world. + * + * The service inject script relies on exposed APIs, so this function can only + * be called after APIs have been exposed via `contextBridge` to the main world. + * + * We have to call `executeJavaScriptInIsolatedWorld` from the service preload script, + * beause there is no way currently (electron 16) to execute a script on a + * `WebFrameMain` in the main process by specifying a `WebSource`. + * Calling `executeJavaScriptInInsolatedWorld` on a `WebContents` in the main process + * will always inject the script into the _top-level_ frame, but here we + * are injecting into the _current_ frame instead. + * As a tradeoff, the promise returned by `executeJavaScriptInIsolatedWorld` + * will resolve to `unknown` (instead of rejecting) even if the injected script fails, + * because chromium doesn't dispatch main world errors to isolated worlds. + * + * @return A promise that only rejects if we fail to fetch the inject script. + * @see https://www.electronjs.org/docs/latest/api/web-frame#webframeexecutejavascriptinisolatedworldworldid-scripts-usergesture-callback + * @see https://www.electronjs.org/docs/latest/api/web-frame-main#frameexecutejavascriptcode-usergesture + * @see https://www.electronjs.org/docs/latest/api/web-contents#contentsexecutejavascriptinisolatedworldworldid-scripts-usergesture + */ +async function fetchAndExecuteInjectScript(): Promise { + const apiExposedResponse = await ipcRenderer.invoke( + ServiceToMainIpcMessage.ApiExposedInMainWorld, + ); + const injectSource = webSource.parse(apiExposedResponse); + // Isolated world 0 is the main world. + return webFrame.executeJavaScriptInIsolatedWorld(0, [injectSource]); } + +fetchAndExecuteInjectScript().catch((err) => { + console.log('Failed to fetch inject source:', err); +}); -- cgit v1.2.3-54-g00ecf