0

I am trying to inject content script on context menu click in an extension manifest version 3. I need to check if it is already injected or not. If it is not injected , inject the content script. This condition has to be satisfied. Can anyone help me with this?

We can use

ALREADY_INJECTED_FLAG

but this can be checked only in the content script, so this approach will not work as expected.

payload.js(content script)

function extract() {
    
    htmlInnerText = document.documentElement.innerText;
    url_exp = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi;
    regex =  new RegExp(url_exp)
    list_url = htmlInnerText.match(url_exp)

    ip_exp = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
    list_ip = htmlInnerText.match(ip_exp)

    hash_exp = /\b[A-Fa-f0-9]{32}\b|\b[A-Fa-f0-9]{40}\b|\b[A-Fa-f0-9]{64}\b/g
    list_hash = htmlInnerText.match(hash_exp)

    chrome.storage.local.set({ list_url: list_url, list_ip: list_ip, list_hash: list_hash });

}

chrome.runtime.sendMessage( extract());

background.js

genericOnClick = async () => {

    // Inject the payload.js script into the current tab after the backdround has loaded
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        chrome.scripting.executeScript({
            target: { tabId: tabs[0].id },
            files: ["payload.js"]
        },() => chrome.runtime.lastError);
    });
    

    // Listen to messages from the payload.js script and create output.
    chrome.runtime.onMessage.addListener(async (message) => {
    
        chrome.storage.local.get("list_url", function (data) {
            if (typeof data.list_url != "undefined") {
                urls = data.list_url
            }
        });
        chrome.storage.local.get("list_ip", function (data) {
            if (typeof data.list_ip != "undefined") {
                ips = data.list_ip
            }
        });
        chrome.storage.local.get("list_hash", function (data) {
            if (typeof data.list_hash != "undefined") {
                hashes = data.list_hash;
            }
        });

        
        if ( hashes.length>0 || urls.length>0 || ips.length>0 ){
            chrome.windows.create({url: "output.html", type: "popup", height:1000, width:1000});
        }
    });
}
  • 1
    You can check it inside the content script as [shown here](https://stackoverflow.com/questions/34528785/chrome-extension-checking-if-content-script-has-been-injected-or-not). – wOxxOm Nov 30 '22 at 09:23
  • The output html is called from background after getting a message from content script. on my first context menu click I get the output html once. Second time I click, I get the output html twice likewise. If I use this condition mentioned, the output screen will come only once for a webpage . second time you click the context menu , no output. – tonia j sebastian Nov 30 '22 at 10:41
  • 1
    It means you're not using the example correctly. Please show your code. – wOxxOm Nov 30 '22 at 11:34
  • Code added. Please review – tonia j sebastian Nov 30 '22 at 12:05

1 Answers1

1

on my first context menu click I get the output html once. Second time I click, I get the output html twice likewise.

This behavior is caused by a combination of two factors.

First factor

You're calling chrome.runtime.onMessage.addListener() inside genericOnClick(). So every time the user clicks the context menu item, the code adds a new onMessage listener. That wouldn't be a problem if you passed a named function to chrome.runtime.onMessage.addListener(), because a named function can only be registered once for an event.

function on_message(message, sender, sendResponse) {
    console.log("bg.on_message");
    sendResponse("from bg");
}

chrome.runtime.onMessage.addListener(on_message);

Second factor

But you're not registering a named function as the onMessage handler. You're registering an anonymous function. Every click on the context menu item creates and registers a new anonymous function. So after the Nth click on the context menu item, there will be N different onMessage handlers, and each one will open a new window.

Solution

  1. Define the onMessage handler as a named function, as shown above.
  2. Call chrome.runtime.onMessage.addListener() outside of a function.

You don't have to do both 1 and 2. Doing either will solve your problem. But I recommend doing both, because it's cleaner.

Thomas Mueller
  • 514
  • 1
  • 4
  • 10
  • It is working. I have been behind this for days. Thank you – tonia j sebastian Nov 30 '22 at 14:48
  • One more query. When we open a webpage, the output.html is opened without context menu click. What can I do restrict output.html coming on load? – tonia j sebastian Dec 01 '22 at 07:09
  • @toniajsebastian I learned the hard way that I must know when to stop helping. If I don't, it'll be bad for my mental health. When I read "One more query", followed by very little information, I knew that this point had been reached. Some friendly advice: Don't use the comments to ask additional questions, because your options for formatting text are very limited. Create a new question instead. – Thomas Mueller Dec 01 '22 at 11:54
  • @toniajsebastian In your new question, provide an [MCVE](https://stackoverflow.com/help/minimal-reproducible-example). If you give people something they can actually run on their computer, you'll drastically increase your chances of getting a helpful answer. I will not answer your new question, but if you put enough time and effort into it, I'm sure someone else will. See also [How do I ask a good question?](https://stackoverflow.com/help/how-to-ask) – Thomas Mueller Dec 01 '22 at 11:55