Search code examples
javascriptgoogle-chrome-extensionsendmessagecontent-script

chrome.runtime.onMessage.addListener() getting triggered multiple times


The chrome.runtime.onMessage.addListener() function is being invoked once for the first message received, but subsequently, it's being invoked multiple times, increasing with each subsequent message. An example for the issue is in the this image - click here to view the image

The extension targets a specific Chrome tab playing a local video file, with actions intended solely for that tab.

The code that's causing the issue:

content.js

// content.js
console.log("--- Start of Content.js --");

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {

    console.log("Entered onMessage.addListener")
    var video = document.querySelector("video");
    
    if (!video) return;

    switch (request.action) {
        case "play":
            video.play();
            break;
        case "pause":
            video.pause();
            break;
        case "forward":
            video.currentTime += 5;
            break;
        case "backward":
            video.currentTime -= 5;
            break;
    }
});

background.js

// background.js
console.log("Background.js");

const urlTest = 'https://api-endpoint.com/get';

function checkAndDoAction() {
    fetch(urlTest)
        .then(response => response.text())
        .then(data => {
            if (data !== "None") {

                const action_ = data.trim();
                
                queryInfo = {'url': 'file:///Users/username/*'};

                chrome.tabs.query(queryInfo, function(tabs) {
                    chrome.scripting.executeScript({
                        target: { tabId: tabs[0].id },
                        files: ["content.js"]
                    }).then(() => {
                        console.log("sending msg to content" )
                        chrome.tabs.sendMessage(tabs[0].id, { action: action_ });
                    }).catch((error) => {
                        console.error("Error executing script:", error);
                    });
                });

            }
        })
        .catch(error => console.error("Error:", error));
}

setInterval(checkAndDoAction, 500);

manifest.json

{
    "manifest_version": 3,
    "name": "Video Controller - extension",
    "version": "4.0",
    "description": "Browser extension to control video playback.",
    "permissions": ["scripting", "tabs", "storage" ],
    "host_permissions": ["file:///Users/username/*"],
    "action": {
      "default_popup": "popup.html",
      "default_icon": {
        "16": "icons/playback-16.png",
        "48": "icons/playback-48.png",
        "128": "icons/playback-128.png"
      }
    },
    "icons": {
      "16": "icons/playback-16.png",
      "48": "icons/playback-48.png",
      "128": "icons/playback-128.png"
    },
    "background": {
      "service_worker": "background.js"
    },
    "content_scripts": [
      {
        "matches": ["<all_urls>"],
        "js": ["content.js"]
      }
    ]
  }

I tried asking chatgpt, refering multiple forums and non of them worked. I need to figure out a way to make sure the function gets triggered only once every call.


Solution

  • You reinject the same file multiple times, it runs and adds a new listener each time.

    Solution: send a message first, inject only if failed.

    chrome.tabs.query({url: '...'}, tabs => tabs.forEach(t => send(t.id, {action: '...'})));
    
    async function send(tabId, msg) {
      for (let i = 0; i < 2; i++) {
        try {
          return await chrome.tabs.sendMessage(tabId, msg);
        } catch (err) {
          await chrome.scripting.executeScript({
            target: { tabId },
            files: ['content.js'],
          });
        }
      }
    }