I'm currently playing around with Google Chrome extensions for a side project and came across an issue which I can't find a clear answer for. In the Google Chrome Extension developer documentation for manifest V3, it talks about using service workers instead of background pages. Hunting through a bit further I have found a couple of partial samples but no definitive example.
So I've plugged all the parts together, but I'm not able to get a working example. Starting from their complete example available from the getting started page, I already notice the first problem. When I load the unpacked extension into chrome, I get the warning:
Service worker registration failed
This is likely the cause of later issues, but I'll mention them below as well.
From there, I proceed to look at their messaging documentation, finding a few examples talking about messaging to and from content scripts. My understanding is such that the same logic should apply when messaging from a popup to the background service worker, as when you want to message from a content script.
The code below is based from the complete example linked above, but it should only require icons to work beyond what I have provided. Their example also provides an options page which I have not used as part of my test below. When I click the button in the popup I get the following errors, the first of which I strongly suspect is caused by the earlier warning about the service worker.
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
Error handling response: TypeError: Cannot read property 'farewell' of undefined at chrome-extension://cjoabhbajedbfdlfhlgahfeebgkpefhl/popup.js:17:30
manifest.json
{
"name": "Getting Started Example",
"description": "Build an Extension!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["storage", "activeTab", "scripting"],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
}
},
"icons": {
"16": "/images/get_started16.png",
"32": "/images/get_started32.png",
"48": "/images/get_started48.png",
"128": "/images/get_started128.png"
}
}
background.js
let color = "#3aa757";
chrome.runtime.onInstalled.addListener(() => {
chrome.storage.sync.set({ color });
console.log("Default background color set to %cgreen", `color: ${color}`);
});
chrome.runtime.onMessage.addListener((request, sender, reply) => {
console.log(
sender.tab
? "from a content script:" + sender.tab.url
: "from the extension"
);
if (request.greeting == "hello") reply({ farewell: "goodbye" });
return true;
});
popup.html
<!DOCTYPE html>
<html>
<head>
<!-- I left this stylesheet reference in for minimised changes from the example, but it can be commented out without any dramas. -->
<link rel="stylesheet" href="button.css">
</head>
<body>
<button id="changeColor"></button>
<script src="popup.js"></script>
</body>
</html>
popup.js
// Initialize butotn with users's prefered color
let changeColor = document.getElementById("changeColor");
chrome.storage.sync.get("color", ({ color }) => {
changeColor.style.backgroundColor = color;
});
// When the button is clicked, inject setPageBackgroundColor into current page
changeColor.addEventListener("click", async () => {
//let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(
tabs[0].id,
{ greeting: "hello" },
function (response) {
console.log(response.farewell);
}
);
});
//chrome.scripting.executeScript({
// target: { tabId: tab.id },
// function: setPageBackgroundColor,
//});
});
// The body of this function will be execuetd as a content script inside the
// current page
function setPageBackgroundColor() {
chrome.storage.sync.get("color", ({ color }) => {
document.body.style.backgroundColor = color;
});
}
Hunting around the internet, I don't seem to find a lot of answers about these sorts of issues. I have seen a number of articles/SO posts talking about how to do things the old V2 way, but I feel like I would be wasting my time learning the V2 way. Ultimately I feel like the issue here is that there is no good example provided by google which indicates how to correctly configure the messaging system within an extension from the popup or content level to the background service worker. So any advice or pointers on that front would be the answer I am looking for.
EDIT:
After some comments by wOxxOm below, popup.js's code has changed slightly to use chrome.runtime.sendMessage instead of the chrome.tabs.sendMessage shown above. e.g.
popup.js
// Initialize butotn with users's prefered color
let changeColor = document.getElementById("changeColor");
// When the button is clicked, inject setPageBackgroundColor into current page
changeColor.addEventListener("click", async () => {
console.log("button clicked!");
chrome.runtime.sendMessage({ greeting: "hello" }, function (response) {
console.log(response.farewell);
});
});