Search code examples
google-chromegoogle-chrome-extension

Why Chrome extension works only when the service worker inspector is open


I have a simple extension that opens a new tab once the last tab is closed. It works as expected when the extension's service worker inspector AKA console is open and not working when the console is not open.

Why is that?

manifest.json

{
  "name": "Last tab",
  "description": "Last tab",
  "version": "1.0",
  "manifest_version": 3,
  "background": {
    "service_worker": "background.js"
  },
  "permissions": [
    "tabs"
  ]
}

background.js:

chrome.tabs.onRemoved.addListener(async function() {

    const tabs = await chrome.tabs.query({ lastFocusedWindow: true });

    if (!tabs.length) {
        chrome.tabs.create({ url: 'chrome://newtab/' });
    }
})


Solution

  • It's a combination of a bug in Chrome and an incorrect condition in your code.

    When devtools window is active it is mistakenly processed by chrome.tabs.query and since it doesn't have any tabs the result is 0, then your code successfully proceeds to opening a tab.

    When devtools window is closed, the actual browser window is correctly processed and an actual number of tabs is returned, which is not 0, so !tabs.length is false.

    In Chrome the situation you check for cannot even happen because when the last tab is closed, the entire window is closed so lastFocusedWindow: true will use a different window. If it was the last window, the entire browser terminates.

    The solution would be to check for closing of the last window and using the special background permission that makes your extension run in the system tray even after the last browser window is closed.

    manifest.json:

      "permissions": [
        "background"
      ]
    

    background.js:

    chrome.windows.onRemoved.addListener(async () => {
      if (!(await chrome.tabs.query({})).length) {
        chrome.windows.create({});
      }
    });
    

    It would make sense to keep the last window's position:

    chrome.windows.onCreated.addListener(savePos);
    chrome.windows.onBoundsChanged.addListener(savePos);
    chrome.runtime.onStartup.addListener(savePos);
    async function savePos() {
      chrome.storage.session.set({
        pos: (await chrome.windows.getAll({windowTypes: ['normal']}))
          .map(w => [w.left, w.top, w.width, w.height]),
      })
    }
    

    Reading/using it is up to the reader.