7

I have a content script in a Chrome Extension that's passing messages. Every so often, when the content script calls

chrome.runtime.sendMessage({
  message: 'hello',
});

it throws an error:

Uncaught Error: Extension context invalidated.

What does this error mean? I couldn't find any documentation on it.

It doesn't happen consistently. In fact, it's hard to reproduce. Seems to happen if I just leave the page open for a while in the background.


Another clue: I've written many Chrome Extensions with content scripts that pass messages and I haven't seen this error before. The main difference is that this content script is injected by the background page using

chrome.tabs.executeScript({
  file: 'contentScript.js',
});

Does using executeScript instead of the manifest file somehow change the lifecycle of the content script?

Atav32
  • 1,788
  • 3
  • 23
  • 33
  • Have you tried a long-lived connection? – herodrigues Feb 23 '19 at 01:16
  • @herodrigues yeah, I was going to try [long lived connections](https://developer.chrome.com/apps/messaging#connect) next. Do you think it'll help? – Atav32 Feb 23 '19 at 01:18
  • I think it does. I have never developed an extension without a long lived connection. See the approach I use in my answer. – herodrigues Feb 23 '19 at 01:27
  • 1
    The error means you've reloaded the extension or disabled/enabled it so the old content scripts are orphaned, which they need to detect. There are several existing answers that show how to deal with the problem, try searching. – wOxxOm Feb 23 '19 at 04:43

1 Answers1

3

This is certainly related to the message listener being lost in the middle of the connection between content and background scripts.

I've been using this approach in my extensions, so that I have a single module that I can use in both background and content scripts.

messenger.js

const context = (typeof browser.runtime.getBackgroundPage !== 'function') ? 'content' : 'background'

chrome.runtime.onConnect.addListener(function (port) {
  port.onMessage.addListener(function (request) {
    try {
      const object = window.myGlobalModule[request.class]
      object[request.action].apply(module, request.data)
    } catch () {
      console.error(error)
    }
  })
})

export function postMessage (request) {
  if (context === 'content') {
    const port = chrome.runtime.connect()
    port.postMessage(request)
  }

  if (context === 'background') {
    if (request.allTabs) {
      chrome.tabs.query({}, (tabs) => {
        for (let i = 0; i < tabs.length; ++i) {
          const port = chrome.tabs.connect(tabs[i].id)
          port.postMessage(request)
        }
      })
    } else if (request.tabId) {
      const port = chrome.tabs.connect(request.tabId)
      port.postMessage(request)
    } else if (request.tabDomain) {
      const url = `*://*.${request.tabDomain}/*`
      chrome.tabs.query({ url }, (tabs) => {
        tabs.forEach((tab) => {
          const port = chrome.tabs.connect(tab.id)
          port.postMessage(request)
        })
      })
    } else {
      query({ active: true, currentWindow: true }, (tabs) => {
        const port = chrome.tabs.connect(tabs[0].id)
        port.postMessage(request)
      })
    }
  }
}

export default { postMessage }

Now you'll just need to import this module in both content and background script. If you want to send a message, just do:

messenger.postMessage({
   class: 'someClassInMyGlobalModuçe',
   action: 'someMethodOfThatClass',
   data: [] // any data type you want to send
})

You can specify if you want to send to allTabs: true, a specific domain tabDomain: 'google.com' or a single tab tabId: 12.

herodrigues
  • 948
  • 1
  • 7
  • 11
  • "message listener being lost" - could you clarify? It doesn't sound like a technically possible thing to occur other than a bug in the browser, which needs investigation and confirmation. Also there's nothing wrong in using chrome.runtime.sendMessage for one-time messaging. The usual cause for the OP's error is orphaning of content scripts on extension reload/update. – wOxxOm Feb 23 '19 at 06:57