'chrome.runtime.sendMessage in content script doesn't send message

I have the following files (gist for easy access):

manifest.json

{
  "name": "testmessage",
  "version": "0.1",
  "manifest_version": 2,
  "externally_connectable": {
    "matches": ["*://www.google.com/*"]
  },
  "background": {
    "scripts": ["background.js"],
    "persistent": true
  },
  "content_scripts": [
    {
      "matches": ["*://www.google.com/*"],
      "js": ["content.js"]
    }
  ]
}

content.js

chrome.runtime.sendMessage(
    "eldkfaboijfanbdjdkohlfpoffdiehnb", // PUT YOUR EXTENSION ID HERE
    "foo",
    function (response) {
        console.log(response);
    }
);
console.log("this is content.js reporting for duty");

background.js

chrome.runtime.onMessageExternal.addListener(
    function(request, sender, sendResponse) {
        console.log("background.js got a message")
        console.log(request);
        console.log(sender);
        sendResponse("bar");
    }
);
console.log("this is background.js reporting for duty");

I can see both "... reporting for duty" messages in the respective consoles. But background.js doesn't receive a message when http://www.google.com loads. Line 5 in content.js prints undefined in the console for google.com.

When I run chrome.runtime.sendMessage("eldkfaboijfanbdjdkohlfpoffdiehnb", "foo"); in the google.com console it shows up in the background.js console.

What am I doing wrong?



Solution 1:[1]

What you're doing wrong is over-complicating it. Twice.

First, you don't need to declare to be externally connectable, since you're sending a message from a content script and not the webpage itself.

Second, it's not an external message either. External messages are for connecting different extensions, not messages within one extension.

Your code should look like this:

content.js

chrome.runtime.sendMessage(
    "foo",
    function (response) {
        console.log(response);
    }
);

background.js

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        console.log("background.js got a message")
        console.log(request);
        console.log(sender);
        sendResponse("bar");
    }
);

Solution 2:[2]

ASYNC CALLBACKS NEEDS THIS

onMessage pattern for async callback:

chrome.runtime.onMessage.addListener(async (message, sender, sendResponse)=>{

  // Do domething with the received message
  ...

  // You must return true even if you don't intend to send a response
  return true;

});

Sending a message is quite straighforward as shown below.

sendMessage pattern:

chrome.runtime.sendMessage(message, response =>{

  /* 1. You have to Check if the was an error for the message that was sent out.
     2. This also means you know that there will be no response sent by the receiving end.
     3. If the receing end is meant to send a response then you can work with the response with this check.
     4. In this example it's assumed that there will be no message sent by the receiving end. 
     5. But we still have to check for the error to avoid it being reported as an error rather we just comment it out.
     6. If you ignore this check , you will get an error.*/

  // if you want to log the error 
  if(!response)
    console.log('message error: ', chrome.runtime.lastError.message);

  // or avoid logging
  let ignorelasterror = chrome.runtime.lastError;

});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Xan
Solution 2