My extension use chrome.offscreen API. The main target is run offscreen at startup browser. Seldom and random at startup browser occurs error Only a single offscreen document may be created
. The extension in such case stop working (content script dont inject elements to site and no error in site console). Only reloading the extension or browser fix problem.
I have check that offscreen document created early 3 ways: with global isOffscreenCreating
promise, with chrome.offscreen.hasDocument()
native function and custom hasOffscreenDocument()
. In fact, the custom hasOffscreenDocument()
always return false. I dont know why, but it from documentation example.
Do you have suggestion why error happen?
// background.js
chrome.runtime.onStartup.addListener(setupOffscreenDocument)
setupOffscreenDocument()
const OFFSCREEN_DOCUMENT_PATH = 'assets/html/offscreen.html'
let isOffscreenCreating
export async function setupOffscreenDocument() {
if (isOffscreenCreating) {
await isOffscreenCreating;
} else if ((chrome.offscreen.hasDocument && !(await chrome.offscreen.hasDocument())) || !(await hasOffscreenDocument())) {
try {
isOffscreenCreating = chrome.offscreen.createDocument({
url: OFFSCREEN_DOCUMENT_PATH,
reasons: [chrome.offscreen.Reason.IFRAME_SCRIPTING],
justification: 'Listening WebSocket messages',
})
await isOffscreenCreating;
} catch (error) {
console.error(error)
} finally {
isOffscreenCreating = null
}
}
}
async function hasOffscreenDocument() {
const matchedClients = await clients.matchAll();
for (const client of matchedClients) {
if (client.url.endsWith(OFFSCREEN_DOCUMENT_PATH)) {
return true;
}
}
return false;
}
There are several problems: your code throws because there's no chrome.offscreen.hasDocument anymore, you can't use typescript directly as Promise<boolean>
without compiling the code, and the example in the documentation is unnecessarily complicated.
All you need is a trivial try/catch:
async function setupOffscreenDocument() {
try {
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: ['IFRAME_SCRIPTING'],
justification: 'Listening to WebSocket messages',
});
} catch (error) {
if (!error.message.startsWith('Only a single offscreen'))
throw error;
}
}