Detecting if offscreen document already exists

346 views
Skip to first unread message

ComputerWhiz

unread,
May 11, 2023, 12:00:12 PM5/11/23
to Chromium Extensions
I'm using an offscreen document for audio playback. When an event fires in my service worker, I am sending a message to the offscreen document, which contains the audio data that I want it to play.

The issue is that, since only a single offscreen document can be opened at a time, I need to be able to create a new offscreen document (if required) before I send the message. Calling createDocument() will throw an exception if the document already exists.

How can I detect if the offscreen document exists?

Patrick Kettner

unread,
May 11, 2023, 12:07:49 PM5/11/23
to ComputerWhiz, Chromium Extensions
Great question!
Some of our example extensions show how to accomplish this

--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/89560b69-395f-45d8-a155-863c9b156c94n%40chromium.org.

Jackie Han

unread,
May 11, 2023, 12:48:06 PM5/11/23
to Patrick Kettner, ComputerWhiz, Chromium Extensions
We discussed this issue back in February of this year and I gave four possible approaches (see link1 and link2). Later, the official api reference was updated, added an hasOffscreenDocument example, i.e. Clients api.

Oliver Dunk

unread,
May 11, 2023, 3:30:01 PM5/11/23
to Jackie Han, Patrick Kettner, ComputerWhiz, Chromium Extensions
We've also just shared how to test an upcoming runtime.getContexts() API: https://groups.google.com/a/chromium.org/g/chromium-extensions/c/9nzzbr0kErc/m/7EpArNL5AgAJ

This isn't available in stable yet but may be a good approach for you, and you could give it a go in Chrome Canary :)
Oliver Dunk | DevRel, Chrome Extensions | https://developer.chrome.com/ | London, GB


Jackie Han

unread,
May 12, 2023, 5:15:19 AM5/12/23
to Oliver Dunk, Patrick Kettner, ComputerWhiz, Chromium Extensions
Yes, runtime.getContexts() API can do the same thing as Clients API.

let contexts = await chrome.runtime.getContexts({contextTypes: ["OFFSCREEN_DOCUMENT"]});
if (contexts.length === 0) {
  // not exist
} else {
  // already exist
}

However, in practice, whether runtime.getContexts() or Clients api is used, there is still a problem with the sample code given earlier.

// sample code
async function sendMessageToOffscreenDocument(data) {
  // Create an offscreen document if one doesn't exist yet
  if (!(await hasDocument())) {
    await chrome.offscreen.createDocument(...);
  }
  // Now that we have an offscreen document, we can dispatch the message.
  chrome.runtime.sendMessage(data);
}


I think you've guessed that there is a concurrency issue here during creation.
await hasDocument() typically takes 1 ms, an async light work.
await chrome.offscreen.createDocument(...) typically takes more than 100 ms, an async heavy work.

If you call above function multiple times at the same time, e.g.
for (let i = 0; i < 10; i++) {
    sendMessageToOffscreenDocument();
}

It will throw Error: Only a single offscreen document may be created.

There are several ways to avoid this problem. For example,

let creating;
async function setupOffscreen() {
  let contexts = await chrome.runtime.getContexts({contextTypes: ["OFFSCREEN_DOCUMENT"]});
  if (contexts.length === 0) {
    if (creating) {
      console.log('already creating');
      await creating;
    } else {
      console.log('create');
      creating = chrome.offscreen.createDocument(...);
      await creating;
      creating = null;
    }
  } else {
    console.log('it exists');
  }
}

async function sendMessageToOffscreenDocument(data) {
  await setupOffscreen();
  chrome.runtime.sendMessage(data);
}


// test 
for (let i = 0; i < 10; i++) {
  setupOffscreen();
}

Robbi

unread,
May 12, 2023, 7:50:22 AM5/12/23
to Chromium Extensions, Jackie Han, Patrick Kettner, ComputerWhiz, Chromium Extensions, Oliver Dunk
One side effect of these offscreen documents that I've already come across is closing the document at the end of a quick\instant task when a relatively longer\heavy task is still running.
Therefore, proper attention must be paid.
I think this can be addressed in several ways:
  1. I always leave the document open and therefore I don't run the risk of closing it at a crucial moment.
  2. I prepare a lock logic      i.e. I prevent new tasks from running before the current one completes.
  3. I prepare a queue            i.e. I serialize the requests and serve the requests that arrive first (FIFO).
  4. trivially I set a "busy" global variable (or a storage variable) that I will set to "true" when I start that particular task heavier than the others and then reset the same variable to "false" when this task is finished. If and when the generic task arrives, at its end I'll test the "busy" var and if I find it set to "true" then I won't close the off-screen document and go on.
Reply all
Reply to author
Forward
0 new messages