diff --git a/app/scripts/offscreen.js b/app/scripts/offscreen.js index ba796874f2fc..cb18f393e11d 100644 --- a/app/scripts/offscreen.js +++ b/app/scripts/offscreen.js @@ -1,3 +1,4 @@ +import { captureException } from '@sentry/browser'; import { OffscreenCommunicationTarget } from '../../shared/constants/offscreen-communication'; /** @@ -9,29 +10,51 @@ import { OffscreenCommunicationTarget } from '../../shared/constants/offscreen-c */ export async function createOffscreen() { const { chrome } = globalThis; - if (!chrome.offscreen || (await chrome.offscreen.hasDocument())) { + if (!chrome.offscreen) { return; } + let offscreenDocumentLoadedListener; const loadPromise = new Promise((resolve) => { - const messageListener = (msg) => { + offscreenDocumentLoadedListener = (msg) => { if ( msg.target === OffscreenCommunicationTarget.extensionMain && msg.isBooted ) { - chrome.runtime.onMessage.removeListener(messageListener); + chrome.runtime.onMessage.removeListener( + offscreenDocumentLoadedListener, + ); resolve(); } }; - chrome.runtime.onMessage.addListener(messageListener); + chrome.runtime.onMessage.addListener(offscreenDocumentLoadedListener); }); - await chrome.offscreen.createDocument({ - url: './offscreen.html', - reasons: ['IFRAME_SCRIPTING'], - justification: - 'Used for Hardware Wallet and Snaps scripts to communicate with the extension.', - }); + try { + await chrome.offscreen.createDocument({ + url: './offscreen.html', + reasons: ['IFRAME_SCRIPTING'], + justification: + 'Used for Hardware Wallet and Snaps scripts to communicate with the extension.', + }); + } catch (error) { + if (offscreenDocumentLoadedListener) { + chrome.runtime.onMessage.removeListener(offscreenDocumentLoadedListener); + } + if ( + error?.message?.startsWith( + 'Only a single offscreen document may be created', + ) + ) { + console.debug('Offscreen document already exists; skipping creation'); + } else { + // Report unrecongized errors without halting wallet initialization + // Failures to create the offscreen document does not compromise wallet data integrity or + // core functionality, it's just needed for specific features. + captureException(error); + } + return; + } // In case we are in a bad state where the offscreen document is not loading, timeout and let execution continue. const timeoutPromise = new Promise((resolve) => {