// Creating the offscreen document at most once, without betting sign-in on an // API whose availability we cannot pin down. // // `chrome.offscreen.hasDocument()` has existed since the offscreen API shipped // in Chrome 109, but it was marked `[nodoc]` in the Chromium IDL and only // appeared in the published reference with Chrome 150 — so the "Chrome 150+" // annotation is the date it was documented, not the date it became callable. // Verified directly: on Chrome 149 it is a function, returns false before // creation and true after. The reason it stayed undocumented is that the team // was not committed to it (a per-extension existence check does not survive // multiple offscreen documents, crbug.com/1339382), and removal has been // discussed on chromium-extensions. // // So it is unsafe in both directions: unverifiable below 149, and a candidate // for removal above it. This module therefore prefers the documented // `chrome.runtime.getContexts()` (Chrome 116), falls back to `hasDocument()` // for the 110-115 window the manifest still supports, and treats // `createDocument`'s own "already exists" failure as the final authority when // neither is usable. Sign-in is the one path that must not break on a version // difference — it already did once, in v1.2.1. /** Chrome's message when a second offscreen document is requested. */ const ALREADY_EXISTS = /single offscreen document/i /** * Whether an offscreen document exists. `undefined` means neither existence * API was available, so the caller must not treat the answer as "no". */ export async function hasOffscreenDocument(): Promise { if (typeof chrome.runtime.getContexts === 'function') { const contexts = await chrome.runtime.getContexts({ contextTypes: [chrome.runtime.ContextType.OFFSCREEN_DOCUMENT], }) return contexts.length > 0 } if (typeof chrome.offscreen.hasDocument === 'function') { return await chrome.offscreen.hasDocument() } return undefined } /** * Create the offscreen document unless one is already there. A lost race, or * an existence check we could not run, both surface as the same * createDocument error — which means the document exists, which is what the * caller wanted. Any other failure is real and propagates. */ export async function ensureOffscreenDocument( parameters: chrome.offscreen.CreateParameters, ): Promise { if (await hasOffscreenDocument()) return try { await chrome.offscreen.createDocument(parameters) } catch (err) { if (!ALREADY_EXISTS.test(String(err))) throw err console.debug('[substandard] offscreen document already existed', err) } }