27

I am trying to send a variable from a background script to a content script that is associated with an HTML page. The content script updates the HTML content with the variable received from the background script.

The problem is that I am getting this error message:

Error: Could not establish connection. Receiving end does not exist.

The background script main.js:

var target = "<all_urls>";
function logError(responseDetails) {
  errorTab = responseDetails.tabId;
  console.log("Error tab: "+errorTab);

  errorURL = responseDetails.url;
  console.log("Error URL: "+errorURL);

  //send errorURL variable to content script
  var sending = browser.tabs.sendMessage(errorTab, {url: errorURL})
    .then(response => {
      console.log("Message from the content script:");
      console.log(response.response);
    }).catch(onError);

  //direct to HTML page
  browser.tabs.update(errorTab,{url: "data/error.html"});
}//end function

browser.webRequest.onErrorOccurred.addListener(
  logError,
  {urls: [target],
  types: ["main_frame"]}
);

The error.html is:

<html>
<head>
  <meta charset="UTF-8">
</head>
<body>
  The error received is <span id="error-id"></span>
  <script src="content-script.js"></script>
</body>
</html>

The content-script.js:

//listen to errorURL from the background script.
browser.runtime.onMessage.addListener(request => {
  console.log("Message from the background script:");
  console.log(request.url);
  return Promise.resolve({response: "url received"});
}); //end onMessage.addListener

//update the HTML <span> tag with the error
document.getElementById("error-id").innerHTML = request.url;

The manifest.json:

{
  "manifest_version": 2,
  "name": "test",
  "version": "1.0",
  "background": {
    "scripts": ["main.js"]
  },

  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["webextension/data/content-script.js"]
    }
  ],

  "permissions": [
    "<all_urls>",
    "activeTab",
    "tabs",
    "storage",
    "webRequest"
  ]
}
Makyen
  • 31,849
  • 12
  • 86
  • 121
user6875880
  • 651
  • 1
  • 7
  • 17

5 Answers5

23

You get the error:

Error: Could not establish connection. Receiving end does not exist.

when you attempt to communicate (e.g. tabs.sendMessage(), tabs.connect()) to a tab where a content script is not listening for messages. This includes times when a content script does not exist in the tab.

You can not inject content scripts into about:* URLs

For your issue, you are getting this error because no content script is injected in the tab. By the time you are trying to send the message, in a main_frame webRequest.onErrorOccurred event, the URL for the tab is already about:neterror?[much more, including the URL where the error occurred]. You can not inject content scripts into about:* URLs. Thus, there is no content script in the tab listening for your message.

Specifically, you have used the <all_urls> match pattern in your manifest.json content_scripts entry. <all_urls> matches:

The special value "<all_urls>" matches all URLs under any of the supported schemes: that is, "http", "https", "file", "ftp", "app".

It does not match about:* URLs.

For a bit more discussion of the URL used when Firefox gets a webRequest.onErrorOccurred event in a main_frame, see "Injecting into navigation error page gets: Error: No window matching {“matchesHost”:[“”]}"

Makyen
  • 31,849
  • 12
  • 86
  • 121
  • is there any alternatives to achieve customized error page so that I send the error id I get from the background script to my HTML content script? I could update the tab content with a customized HTML. This HTML contains a content script. Some functions do work (like listening to a button click) but I need to send the error data to my HTML. What can I do? – user6875880 Jun 21 '17 at 19:01
  • @user6875880, As I've mentioned elsewhere, you can't run content scripts in HTML pages sourced from within your extension. You can run normal JavaScript by including it in the page using an HTML ` – Makyen Jun 21 '17 at 19:10
  • That's what I am doing. If you look at my HTML, I have `src="content-script.js"`. So, all what I need is to remove that from the manifest content script?? Then, can I send the error code from the background to the HTML's script (the HTML is shown in the error tab using `browser.tabs.update(errorTab,{url: "data/error.html"});` ?? – user6875880 Jun 21 '17 at 19:15
  • 1
    You certainly should remove it from your *manifest.json* `content_scripts`. But, as described in the link I provided, you need to use `runtime.sendMessage()` (used for destinations that are in the background context) not `tabs.sendMessage()` (used for destinations that are content scripts). However, given that there is a delay between when you call `tabs.update()` and when the HTML page and JavaScript are actually loaded and operational, you will probably want to use one of the methods which allows you to pull the data into the HTML page, rather than push it there from the background page. – Makyen Jun 21 '17 at 19:21
  • Thanks. Reference to your last reply: `you will probably want to use one of the methods which allows you to pull the data into the HTML page, rather than push it there from the background page`. Can you specify the method that allow me to pull the data? or link for an example or so. My apology, I'm not getting what you mean. – user6875880 Jun 21 '17 at 19:29
  • @user6875880, You can store it in `storage.local`, just in a normal variable, or have a function in the background page that returns the data. Any of those possibilities are directly usable by the script in your HTML page. Alternately, have your HTML script send a message requesting the data, with the data in the response. The point is that it is *much* easier for the HTML script to know when it is ready to get the data than for the background page to know when the HTML page is ready to receive it. – Makyen Jun 22 '17 at 00:56
  • if the extension already have a storage for other data. Can I create another storage for the errors? Can one extension has two storage? – user6875880 Jun 22 '17 at 18:34
  • You can create as many key/value pairs in `storage.local` as you desire. However, storing *huge* amounts of data is discouraged due to performance issues. – Makyen Jun 22 '17 at 18:39
  • so how do you check if something is listening? – john k Jul 23 '18 at 01:43
  • @johnktejik The only way is to try to send something. If you get the error discussed here, then there isn't anything listening – Makyen Jul 23 '18 at 02:47
  • @Makyen Do you know how do I catch the error? try-catch doesn't seem to be working, maybe because its an asyncronous call? – john k Apr 29 '20 at 00:50
  • @johnktejik You can catch the error just like any other asynchronous error. What that looks like exactly depends on if you're using the `chrome.*` or `browser.*` namespace. – Makyen Apr 29 '20 at 01:17
12

For extension developers: If you reload your extension (as a normal part of the development loop) it severs all connections to content scripts.

You must remember to also reload the page for the content script to re-listen correctly.

Offirmo
  • 18,962
  • 12
  • 76
  • 97
2

I also had this same error.

My problem and solution was different but I'm adding it in case it helps.

In my case my content.js script initially did not have a browser.runtime.onMessage.addListener() function (FireFox).

When I later added this listener to the content.js script I did not reload the temporary extension in the "about:debugging" page in FireFox. I got the above error.

After clicking "reload" in the "about:debugging" tab the content script received the message.

d-_-b
  • 21,536
  • 40
  • 150
  • 256
1

I desided same task in next way:

I was making the context menu and have a like problem.

browser.contextMenus.onClicked.addListener((info, tab) => {
       if (info.menuItemId === "mymenu") {
       // some code
       browser.tabs.sendMessage(tabs[0].id, {greeting: "Hi from background script"});
       }
        });

I get error:

Could not establish connection. Receiving end does not exist

I add a function and a listener:

browser.contextMenus.onClicked.addListener((info, tab) => {
       if (info.menuItemId === "mymenu") {
       // some code
       browser.tabs.sendMessage(tabs[0].id, {greeting: "Hi from background script"});
       }
        });
   // -----function and Listener ------
   function connectToContent() {
      browser.tabs.query({ currentWindow: true, active: true
      }).then((tabs) => {
       browser.tabs.sendMessage(tabs[0].id, {greeting: "Activate Tab"});
       });
    }

    browser.tabs.onActivated.addListener(connectToContent);

Now it is working.browser.tabs.onActivated.addListener does and keeps in connect.

Matthijs
  • 2,483
  • 5
  • 22
  • 33
0

Firefox does not run content scripts on PDF pages which means that if the extension's background script is exectued on a PDF page and tries to send a message to the content script, it will fail with this error.

ted
  • 13,596
  • 9
  • 65
  • 107