Chrome extension: Checking if content script has been injected or not

我的梦境 提交于 2019-12-28 13:34:11

问题


I'm developing a Chrome extension. Instead of using manifest.json to match content script for all URLs, I lazily inject the content script by calling chrome.tabs.executeScript when user do click the extension icon.

What I'm trying is to avoid executing the script more than once. So I have following code in my content script:

if (!window.ALREADY_INJECTED_FLAG) {
    window.ALREADY_INJECTED_FLAG = true
    init() // <- All side effects go here
}

Question #1, is this safe enough to naively call chrome.tabs.executeScript every time the extension icon got clicked? In other words, is this idempotent?

Question #2, is there a similar method for chrome.tabs.insertCSS?

It seems impossible to check the content script inject status in the backgroud script since it can not access the DOM of web page. I've tried a ping/pong method for checking any content script instance is alive. But this introduces an overhead and complexity of designing the ping-timeout.

Question #3, any better method for background script to check the inject status of content script, so I can just prevent calling chrome.tabs.executeScript every time when user clicked the icon?

Thanks in advance!


回答1:


is this safe enough to naively call chrome.tabs.executeScript every time the extension icon got clicked? In other words, is this idempotent?

  1. Yes, unless your content script modifies the page's DOM AND the extension is reloaded (either by reloading it via the settings page, via an update, etc.). In this scenario, your old content script will no longer run in the extension's context, so it cannot use extension APIs, nor communicate directly with your extension.

is there a similar method for chrome.tabs.insertCSS?

  1. No, there is no kind of inclusion guard for chrome.tabs.insertCSS. But inserting the same stylesheet again does not change the appearance of the page because all rules have the same CSS specificity, and the last stylesheet takes precedence in this case. But if the stylesheet is tightly coupled with your extension, then you can simply inject the script using executeScript, check whether it was injected for the first time, and if so, insert the stylesheet (see below for an example).

any better method for background script to check the inject status of content script, so I can just prevent calling chrome.tabs.executeScript every time when user clicked the icon?

  1. You could send a message to the tab (chrome.tabs.sendMessage), and if you don't get a reply, assume that there was no content script in the tab and insert the content script.

Code sample for 2

In your popup / background script:

chrome.tabs.executeScript(tabId, {
    file: 'contentscript.js',
}, function(results) {
    if (chrome.runtime.lastError || !results || !results.length) {
        return;  // Permission error, tab closed, etc.
    }
    if (results[0] !== true) {
        // Not already inserted before, do your thing, e.g. add your CSS:
        chrome.tabs.insertCSS(tabId, { file: 'yourstylesheet.css' });
    }
});

contentscript.js:

// Wrapping in a function to not leak/modify variables if the script
// was already inserted before.
(function() {
    if (window.hasRun === true)
        return true;  // Will ultimately be passed back to executeScript
    window.hasRun = true;
    // rest of code ... 
    // No return value here, so the return value is "undefined" (without quotes).
})(); // <-- Invoke function. The return value is passed back to executeScript

Note, it's important to check window.hasRun for the value explicitly (true in the example above), otherwise it can be an auto-created global variable for a DOM element with id="hasRun" attribute, see Is there a spec that the id of elements should be made global variable?




回答2:


Rob W's option 3 worked great for me. Basically the background script pings the content script and if there's no response it will add all the necessary files. I only do this when a tab is activated to avoid complications of having to add to every single open tab in the background:

background.js

chrome.tabs.onActivated.addListener(function(activeInfo){
  tabId = activeInfo.tabId

  chrome.tabs.sendMessage(tabId, {text: "are_you_there_content_script?"}, function(msg) {
    msg = msg || {};
    if (msg.status != 'yes') {
      chrome.tabs.insertCSS(tabId, {file: "css/mystyle.css"});
      chrome.tabs.executeScript(tabId, {file: "js/content.js"});
    }
  });
});

content.js

chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
    if (msg.text === 'are_you_there_content_script?') {
      sendResponse({status: "yes"});
    }
});



回答3:


Just a side note to the great answer from Rob.

I've found the Chrome extension from Pocket is using a similar method. In their dynamic injected script:

if (window.thePKT_BM)
    window.thePKT_BM.save();
else {
    var PKT_BM_OVERLAY = function(a) {
        // ... tons of code
    },
    $(document).ready(function() {
        if (!window.thePKT_BM) {
            var a = new PKT_BM;
            window.thePKT_BM = a,
            a.init()
        }
        window.thePKT_BM.save()
    }
    )
}


来源:https://stackoverflow.com/questions/34528785/chrome-extension-checking-if-content-script-has-been-injected-or-not

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!