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>
CodePudding user response:
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
};