4

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);
  });
});
Eunoseer
  • 95
  • 3
  • 10
  • You're sending a message to the tab but there's nothing that listens to it. You need a [content script](https://developer.chrome.com/extensions/content_scripts) with [onMessage listener](https://developer.chrome.com/extensions/messaging). Note that there's no essential difference between MV2 and MV3 so you can learn MV2 and then read the migration article without paying any attention to the mostly irrelevant promotion of service workers as they're essentially the same as the non-persistent background scripts minus DOM and a few other features. See also [this](/a/66408379). – wOxxOm May 03 '21 at 04:13
  • 1
    My comment above assumed you want to send a message directly to the content script because this is what chrome.tabs.sendMessage does (you don't even need the background worker for this task). If you want to send to the background worker then use chrome.runtime.sendMessage, see [message passing](https://developer.chrome.com/extensions/messaging) for more info. – wOxxOm May 03 '21 at 04:18
  • @wOxxOm A step in the right direction! No more errors when I try and send a message from the popup, but no console logs are being reported? However I still have the issue with the "Service worker registration failed" warning? It seems strange that Google's example has that warning out of the box?! – Eunoseer May 03 '21 at 22:30
  • To open devtools of the service worker click its label in chrome://extensions page. See also [how to deal with reg failure](https://stackoverflow.com/a/66408379). – wOxxOm May 04 '21 at 03:28

0 Answers0