Home > Blockchain >  Chrome Extension :: Trying to understand syntax of chrome.runtime.sendmessage to chrome.runtime.onMe
Chrome Extension :: Trying to understand syntax of chrome.runtime.sendmessage to chrome.runtime.onMe

Time:10-15

I'm having some issues understanding the syntax of how chrome.runtime.sendmessage sends data from my Content.js file to my chrome.runtime.onMessage Background.js file. I'm trying to inject a script to look at webpage metrics and display them in a Chrome extension popup when navigating to a new page. I'm unable to see the variables sent from my content.js script to my background.js script. I don't have any need to cache data between sessions, and only want my script running in the current tab that the user is on.

My Content.js script should be executing with the "run_at": "document_idle" located in the Manifest.js.

Content.js Runs > setBadgeAction shows metric on icon in Background.js > user can open report in extension Popup

Background.js:

chrome.runtime.onMessage.addListener((request, sender, sendResponse){
    chrome.browserAction.setBadgeText({
                        text: request.apple,
                        tabId: sender.tab.id
                    });
                    chrome.browserAction.setPopup({
                        tabId: sender.tab.id,
                        popup: "popup.html"
                    });
               });

content.js:

function webFunction(){

var apple = 0;
var orange = 3;
var kiwi = 5;
var banana = 13;

chrome.runtime.sendMessage({
    apple: apple,
    orange: orange,
    kiwi: kiwi,
    banana: banana,    
            });
}

popup.js

chrome.tabs.query({
        active: true,
        currentWindow: true
    });
chrome.runtime.onMessage.addListener((request,sender) =>{
            
document.getElementById("appleTag").innerHTML = request.apple;
document.getElementById("orangeTag").innerHTML = request.orange;
document.getElementById("kiwiTag").innerHTML = request.kiwi;
document.getElementById("bananaTag").innerHTML = request.banana;
});

popup.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="popup.css">
<script src="popup.js"></script>
</head>
<body>

<p id ="appleTag">Apple Score</p>
<p id ="orangeTag">Orange Score</p>
<p id ="kiwiTag">Kiwi Score</p>
<p id ="bananaTag">Banana Score</p>
</body>
</html>

manifest.js

{
  "name": "",
  "description": "",
  "version": "1.0",
  "manifest_version": 3,
  "action":{
        "default_popup": "popup.html",
        "default_icon": {
            "16": "/images/16.png",
            "32": "/images/32.png",
            "48": "/images/48.png",
            "128": "/images/128.png"
        }
    },
  "content_scripts": [
        {
            "matches": [
                "*://*/*"
            ],
            "run_at": "document_idle",
            "js": [
                "content.js"
            ]
        }
      ],
  "options_page": "popup.html", 
  "background": {
    "service_worker": "background.js"
},
// Permissions for the Extension.    
  "permissions": [
      "storage", 
    "activeTab" 
],
"icons": {
      "16": "/images/16.png",
      "32": "/images/32.png",
      "48": "/images/48.png",
      "128": "/images/128.png"
    }
}

CodePudding user response:

There are a couple of small issues, which I will address first:

background.js:

  • chrome.runtime.onMessage.addListener is missing => or function, depending on your style
  • chrome.browserAction in manifest version 3 has been renamed to chrome.action
  • when setting badge text the text property must be string type
  • chrome.action.setPopup is not necessary because the popup is already specified in the manifest (action.default_popup); use setPopup if/when you want to dynamically change the popup template at runtime to different templates.

content.js

  • function webFunction is never called, so sendMessage does not get called either

There is another conceptual matter, related to the flow of data between parts of the extension, and when the different parts will be active/inactive and able to receive messages. It appears the intended flow is to send data from content script to background and popup more or less simultaneously.

The background service worker can be used to receive the message from content, then update the badge text, and it will work correctly after addressing the syntax issues listed above.

The popup is active when the user has physically clicked on the extension icon and the popup is open. After the popup loses focus, it will get recycled and reloads next time user clicks extension icon. When user is focused on the tab and content script runs, the popup cannot be open simultaneously, and therefore cannot receive the message.

In order to pass data to the popup you will need some intermediary to receive it from content and then provide it to popup, when popup is loaded and ready to receive it. In manifest V2 it would have (maybe) been reasonable to use the background for this purpose since it is not intended to be stored for a long time, but with MV3, the background service worker is short-lived, and after a while it will become inactive. It will not work as a reliable intermediary for holding data, even temporarily, for this reason. You can find some official discussion about this topic here.

To achieve the intended behavior, I suggest using the storage API. This will allow capturing the data in the content script, and recovering the same data in the popup whenever the popup is ready to be loaded. This was perhaps already the intent, since the storage is included in the manifest permissions, which is needed. You may want to consider using some lookup key to get data from storage for each specific tab, if the counts will vary in that regard, then clear the storage data when tab/window/browser closes.

Here is an edited version with the suggested changes

content.js

// removed webFunction declaration
 
var apple = 0;
var orange = 3;
var kiwi = 5;
var banana = 13;
var counts = {apple, orange, kiwi, banana}

chrome.storage.local.set(counts);   // persist data
chrome.runtime.sendMessage(counts); // send to background

background.js

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.apple !== undefined) // null check before setting badge
        chrome.action.setBadgeText({
            text: request.apple.toString(),
            tabId: sender.tab.id
        });
});

popup.js

// get counts from storage; null means "get everything"
chrome.storage.local.get(null, request => {
    document.getElementById("appleTag").innerHTML = request.apple;
    document.getElementById("orangeTag").innerHTML = request.orange;
    document.getElementById("kiwiTag").innerHTML = request.kiwi;
    document.getElementById("bananaTag").innerHTML = request.banana;
});

In popup.html I would move <script src="popup.js"></script> after the <p> tags to the end of the body, since it looks for elements by id, just to be sure those ids exist.

  • Related