1

I have been building an extension and trying to send a message to content script when a button in the popup is clicked but have been getting this error "Could not establish connection. Receiving end does not exist." and also sometime getting "Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'id')" in popup.js

// manifest.json file

{
  "manifest_version": 3,
  "name": "Extension",
  "version": "1.0",
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [
    {
      "js": ["content.js"],
      "matches": ["https://discord.com/channels/*"]
    }
  ],
  "permissions": ["tabs"],
  "background": {
    "service_worker": "background.js"
  },
  "host_permissions": ["https://discord.com/channels/*"]
}

   
    

// popup.js file.

const sendMessageBtn = document.querySelector("button");

sendMessageBtn.addEventListener("click", async () => {
  console.log("clicked");       // being logged to console
  const [tab] = await chrome.tabs.query({
    active: true,
    lastFocusedWindow: true,
  });
  const response = await chrome.tabs.sendMessage(tab.id, { greeting: "hello" });
  // do something with response here, not outside the function
  console.log(response);
});

When clicking on the button getting error: "Could not establish connection. Receiving end does not exist." in extension error logs

// content.js file

console.log("content script running"); // being logged to console
chrome.runtime.onMessage.addListener(
     async function(request, sender, sendResponse) {
      console.log(sender.tab ?
                  "from a content script:" + sender.tab.url :
                  "from the extension");
                //   console.log(request);
      if (request.greeting === "hello")
         await sendResponse({farewell: "goodbye"});
    }
  );
        

// popup.html file

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Extension</title>
    <link rel="stylesheet" href="popup.css" />
  </head>
  <body>
    <h1>extension</h1>

    <input id="message-field" type="text" placeholder="enter message" />
    <button>Send message</button>

    <script src="popup.js"></script>
  </body>
</html>

1 Answer 1

2

There are several problems.

  • A bug in Chrome when devtools for the popup is focused breaks chrome.tabs.query, more info.

  • chrome.runtime.onMessage listener can't be declared with async in Chrome, more info.

  • When the extension is installed/updated its new content script won't be automatically injected into the currently opened tabs in Chrome/ium, so you'll have to do it explicitly yourself as shown here, but there's a much better alternative in cases like yours where the access to the page is necessary only on demand after the user invoked the extension: programmatic injection via executeScript.

    • remove content.js
    • remove content_scripts from manifest.json
    • remove "tabs" from "permissions" - it's not necessary and now there'll be no warning about observing the browser history.
    • add "scripting" to "permissions".
document.querySelector('button').onclick = async () => {
  // workaround for devtools window https://crbug.com/462939
  const { id: windowId } = await chrome.windows.getCurrent();
  const [tab] = await chrome.tabs.query({ active: true, windowId });
  let result;
  try {
    [{ result }] = await chrome.scripting.executeScript({
      target: { tabId: tab.id },
      // this runs in the web page, so it can't see variables in scope like `tab` 
      func: (param1, param2) => {
        return document.documentElement.innerHTML;
        // return {it: 'can', be: ['an object', 'too']};
      },
      // this is sent to the func's param1 and 2
      args: [123, { foo: 'bar' }],
    });
  } catch (err) {
    // the tab is not injectable
    console.warn(err);
    return;
  }
  console.log(result); // printing in the popup's devtools console
};
Sign up to request clarification or add additional context in comments.

1 Comment

I used standard function and removed async from the listener and now It works and I can pass message from popup to content.js. However if I open devtools for extension it breaks just like you have told in your answer. For now I will go with it. Thanks for the help.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.