Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

chrome.runtime.sendMessage throws exception from content script after reloading Chrome Extension

I send messages from the injected content scripts back to my background script in my Chrome Extension as such:

chrome.runtime.sendMessage({action: "myResult"});

This works fine, until I reload my extension (by going to Settings -> Extensions -> "Reload (Ctrl+R)" for my extension.)

In turn when my background script starts up it repeatedly calls chrome.tabs.executeScript for all open tabs to programmatically re-inject my content script (as I showed in this question.)

But after I do that, if I call that first sendMessage line from my content script, it throws this exception:

Error: Error connecting to extension my_extension_id

Any idea why this happens?

like image 619
c00000fd Avatar asked Sep 15 '14 04:09

c00000fd


People also ask

Why is my Chrome extension throwing this error message?

3 If you're getting this error message while developing a Chrome Extension be sure to disable other Chrome extensions you have installed in your browser. In particular the "AdBlock" extension seemed to interfere with messaging and resulted in my extension throwing this error. Disabling AdBlock resolved this issue for me. Share

Why can't I send messages immediately in chrome?

The problem is if you try to call chrome.runtime.connect() before you have opened a channel on the other end in Background or popup page then you will get that error. What Chrome docs say is that you can send a message immediately. In the past this would just work and the messages would get either delivered or dropped. But now it is failing.

What happens if no handlers are listening to Chrome runtime ReLoad event?

If no handlers are listening for this event, and your extension has a persistent background page, it behaves as if chrome.runtime.reload () is called in response to this event. The callback parameter looks like: (details: object) => void

How do I send a request from the extension to a script?

Sending a request from the extension to a content script looks very similar, except that you need to specify which tab to send it to. This example demonstrates sending a message to the content script in the selected tab. On the receiving end, you need to set up an runtime.onMessage event listener to handle the message.


1 Answers

When the extension runtime is reloaded, which happens in any of the following cases

  • You've called chrome.runtime.reload().
  • You've clicked on Reload extension at chrome://extensions/.
  • The extension was updated.

then the most extension API methods in the content script cease to work (including chrome.runtime.sendMessage which causes the error in the question). There are two ways to work around this problem.

Option 1: Fall back to contentscript-only functionality

If your extension can function perfectly without a background page, then this could be an acceptable solution. E.g. if your content script does nothing else besides modifying the DOM and/or performing cross-origin requests.

I'm using the following snippet in one of my extensions to detect whether the runtime is still valid before invoking any Chrome extension API from my content script.

// It turns out that getManifest() returns undefined when the runtime has been
// reload through chrome.runtime.reload() or after an update.
function isValidChromeRuntime() {
    // It turns out that chrome.runtime.getManifest() returns undefined when the
    // runtime has been reloaded.
    // Note: If this detection method ever fails, try to send a message using
    // chrome.runtime.sendMessage. It will throw an error upon failure.
    return chrome.runtime && !!chrome.runtime.getManifest();
}

// E.g.
if (isValidChromeRuntime()) {
    chrome.runtime.sendMessage( ... );
} else {
    // Fall back to contentscript-only behavior
}

Option 2: Unload the previous content script on content script insertion

When a connection with the background page is important to your content script, then you have to implement a proper unloading routine, and set up some events to unload the previous content script when the content script is inserted back via chrome.tabs.executeScript.

// Content script
function main() {
    // Set up content script
}

function destructor() {
    // Destruction is needed only once
    document.removeEventListener(destructionEvent, destructor);
    // Tear down content script: Unbind events, clear timers, restore DOM, etc.
}

var destructionEvent = 'destructmyextension_' + chrome.runtime.id;
// Unload previous content script if needed
document.dispatchEvent(new CustomEvent(destructionEvent));
document.addEventListener(destructionEvent, destructor);
main();

Note that any page that knows the name of the event can trigger destruction of your content script. This is unavoidable, because after the extension runtime is destroyed, there are no proper ways to securely communicate with the extension any more.

like image 60
Rob W Avatar answered Nov 06 '22 23:11

Rob W