From 27eeb589c84f92f10bd200df08e7e1cc28235cad Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Thu, 14 May 2026 09:35:59 +0200 Subject: [PATCH] Refactor browser extension background init to prevent init warnings (#2004) --- .../src/entrypoints/background.ts | 93 ++++++++++++------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/apps/browser-extension/src/entrypoints/background.ts b/apps/browser-extension/src/entrypoints/background.ts index 73f2e742b..04a87a039 100644 --- a/apps/browser-extension/src/entrypoints/background.ts +++ b/apps/browser-extension/src/entrypoints/background.ts @@ -32,8 +32,41 @@ browser.alarms.onAlarm.addListener(handleAutoLockAlarm); export default defineBackground({ /** * This is the main entry point for the background script. + * + * IMPORTANT: This function MUST remain synchronous (no async/await directly in + * the body). MV3 service workers can be terminated when idle and woken up by + * events; only listeners registered synchronously during script evaluation are + * guaranteed to be ready when the next event fires. Any asynchronous setup must + * run as a fire-and-forget IIFE so this function returns synchronously. */ - async main() { + main() { + /* + * Register any synchronous event listeners first, before any await, + * so they're attached synchronously on service-worker wake-up. + */ + browser.commands.onCommand.addListener(async (command) => { + if (command !== "show-autofill-popup") { + return; + } + try { + const [tab] = await browser.tabs.query({ active: true, currentWindow: true }); + if (!tab?.id) { + return; + } + + const results = await browser.scripting.executeScript({ + target: { tabId: tab.id }, + func: getActiveElementIdentifier, + }); + const elementIdentifier = results[0]?.result; + if (elementIdentifier) { + sendMessage('OPEN_AUTOFILL_POPUP', { elementIdentifier }, `content-script@${tab.id}`); + } + } catch (error) { + console.error('Error handling show-autofill-popup command:', error); + } + }); + // Listen for messages using webext-bridge onMessage('CHECK_AUTH_STATUS', () => handleCheckAuthStatus()); @@ -130,44 +163,32 @@ export default defineBackground({ onMessage('PASSKEY_POPUP_RESPONSE', ({ data }) => handlePasskeyPopupResponse(data)); onMessage('GET_REQUEST_DATA', ({ data }) => handleGetRequestData(data)); - // Setup context menus - const isContextMenuEnabled = await LocalPreferencesService.getGlobalContextMenuEnabled(); - if (isContextMenuEnabled) { - await setupContextMenus(); - } - /* - * Initialize auto-lock alarm system. - * This ensures the alarm is restored if the service worker was terminated. - * Note: The alarm listener is registered at top-level scope (see above). + * Async setup (context menus, alarm restoration) runs in a fire-and-forget + * IIFE so main() returns synchronously. Listener registrations above are + * already synchronous and complete before this runs. */ - await initializeAutoLockAlarm(); - - // Listen for custom commands - try { - browser.commands.onCommand.addListener(async (command) => { - if (command === "show-autofill-popup") { - // Get the currently active tab - const [tab] = await browser.tabs.query({ active: true, currentWindow: true }); - if (!tab?.id) { - return; - } - - // Execute script in the active tab - await browser.scripting.executeScript({ - target: { tabId: tab.id }, - func: getActiveElementIdentifier, - }).then((results) => { - const elementIdentifier = results[0]?.result; - if (elementIdentifier) { - sendMessage('OPEN_AUTOFILL_POPUP', { elementIdentifier }, `content-script@${tab.id}`); - } - }).catch(console.error); + (async () : Promise => { + try { + const isContextMenuEnabled = await LocalPreferencesService.getGlobalContextMenuEnabled(); + if (isContextMenuEnabled) { + await setupContextMenus(); } - }); - } catch (error) { - console.error('Error setting up command listener:', error); - } + } catch (error) { + console.error('Error setting up context menus:', error); + } + + try { + /* + * Initialize auto-lock alarm system. + * This ensures the alarm is restored if the service worker was terminated. + * Note: The alarm listener is registered at top-level scope (see above). + */ + await initializeAutoLockAlarm(); + } catch (error) { + console.error('Error initializing auto-lock alarm:', error); + } + })(); } });