Chrome extension messages firing multiple times
Asked Answered
M

4

6

I'm making my first chrome extension and noticed that messages being sent from my popup.html page were getting duplicated in my content.js message event listener. I've console logged "sending message" before every message send and "message received" before every message, and I don't understand how the messages are being duplicated. I've also checked the chrome dev docs for sendMessage and onMessage and it specifies that the onMessage listener should only be fired once per sendMessage event.

Any help would be appreciated.

popup.html

<!DOCTYPE html>
<html>
<head>
    <title>Messaging Practice</title>
</head>
<body>
    <h1>Messaging Practice</h1>

    <input type="button" id="send-message" value="Button">
    <script src="popup.js"></script>
</body>
</html>

content.js

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        console.log('message received')
        console.log(request);
    }
);

popup.js

var messageButton = document.querySelector("#send-message");

messageButton.onclick = function() {
    chrome.tabs.query({currentWindow: true, active: true},
    function(tabs) {
        chrome.tabs.executeScript(
            tabs[0].id, {file: "content.js"}
        )
        console.log("sending message")
        chrome.tabs.sendMessage(tabs[0].id, "string message")
    });
}

background.js

chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
  chrome.declarativeContent.onPageChanged.addRules([{
    conditions: [new chrome.declarativeContent.PageStateMatcher({
      pageUrl: {hostEquals: 'stackoverflow.com'},
    })],
        actions: [new chrome.declarativeContent.ShowPageAction()]
  }]);
});

manifest.json

{
    "name": "Send Messages Practice",
    "version": "1.0",
    "manifest_version": 2,
    "description": "Simple messaging practice with the chrome api",
    "permissions": ["declarativeContent", "activeTab"],
    "background": {
        "scripts": ["background.js"],
        "persistant": true
    },

    "page_action": {
        "default_popup": "popup.html",
        "scripts": ["content.js"],
        "matches": ["http://*/*","https://*/*"]
    }

}
Mensch answered 17/4, 2018 at 3:2 Comment(3)
You reinsert the content script on each click and the content script registers a new function reference each time in onMessage. The simplest solution is to check window.init === true in the content script code and do nothing, otherwise set this property and register the listener.Accrual
@wOxxOm Great explanation! This worked perfectly. I've been banging my head on my computer for days because of this. ThanksMensch
A comment on another question provided the fix for me, recommending that all_frames be set to false in the manifest file.Hoy
A
4

Adding "return true;" to event handlers will prevent your code from firing again and again:

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
  if (request.action == "show_alert") {
    alert(request.alert);
    return true;
  }
});
Alithea answered 23/3, 2020 at 15:45 Comment(1)
this isn't workingJotham
T
3

Get the same problem when I run my extension with multiple frames. @Wayne Smallman solution works for me:

recommending that all_frames be set to false in the manifest file

Tephra answered 22/9, 2020 at 4:12 Comment(0)
L
0

Please make sure to inject the script only once—the script that contains chrome.runtime.onMessage.addListener().

The mistake I was making was that every time I sent a message, I was also injecting the script repeatedly, like this:

ChromeService.execInActiveTab((tab) => {
  // @ts-ignore
  chrome.scripting.executeScript({
    target: {tabId: tab.id},
    files: ["assets/js/autofill.js"],
  }, () => {
    // @ts-ignore
    chrome.tabs.sendMessage(tab.id, {setDropdownValue: {fieldId: fieldId, fieldValue: value}});
  });
});

Simply separating the logic for script injection and message sending resolved the issue for me.

Latialatices answered 22/9, 2023 at 7:46 Comment(0)
F
0

I think this happens because the webpage into which you injected content.js has iframes, to which content.js will also be injected. To solve this, all you need to do is listen for messages only in the main window.


// Check if the current window is top-level and not in an iframe
if (window.self === window.top && window.frameElement === null) {
    // listen to messages from background
    chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {

        // Handle the received message
    });
}
Fike answered 22/4 at 20:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.