Chrome扩展程序:检查是否已注入内容脚本

KF *_*Lin 9 javascript google-chrome google-chrome-extension

我正在开发Chrome扩展程序.我没有使用manifest.json匹配所有URL的内容脚本,而是chrome.tabs.executeScript在用户单击扩展图标时通过调用来懒惰地注入内容脚本.

我正在尝试的是避免多次执行脚本.所以我在我的内容脚本中有以下代码:

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

问题#1,chrome.tabs.executeScript每次点击扩展图标时这是否足够安全?换句话说,这是幂等的吗?

问题#2,是否有类似的方法chrome.tabs.insertCSS

似乎无法在backgroud脚本中检查内容脚本注入状态,因为它无法访问网页的DOM.我已经尝试了ping/pong方法来检查任何内容脚本实例是否存活.但这会带来设计ping超时的开销和复杂性.

问题#3,任何更好的后台脚本方法来检查内容脚本的注入状态,所以我可以阻止chrome.tabs.executeScript每次用户点击图标时调用?

提前致谢!

Rob*_*b W 12

chrome.tabs.executeScript每次点击扩展图标时,这是否足够安全?换句话说,这是幂等的吗?

  1. 是的,除非您的内容脚本修改了页面的DOM并且重新加载了扩展名(通过设置页面重新加载,通过更新等).在这种情况下,旧的内容脚本将不再在扩展的上下文中运行,因此它不能使用扩展API,也不能直接与您的扩展通信.

是否有类似的方法chrome.tabs.insertCSS

  1. 不,没有任何包容性的保护chrome.tabs.insertCSS.但是再次插入相同的样式表并不会改变页面的外观,因为所有规则都具有相同的CSS特性,并且在这种情况下最后一个样式表优先.但是如果样式表与您的扩展名紧密耦合,那么您只需使用executeScript注入脚本,检查它是否是第一次注入,如果是,请插入样式表(参见下面的示例).

任何更好的后台脚本方法来检查内容脚本的注入状态,所以我可以阻止chrome.tabs.executeScript每次用户点击图标时调用 ?

  1. 您可以向tab(chrome.tabs.sendMessage)发送消息,如果没有得到答复,则假设选项卡中没有内容脚本并插入内容脚本.

代码示例为2

在弹出/后台脚本中:

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' });
    }
});
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

注意,window.hasRun明确地检查值是非常重要的(true在上面的例子中),否则它可以是具有id="hasRun"属性的DOM元素的自动创建的全局变量,请参阅是否有规范要求元素的id应该成为全局变量?


Giv*_*ivP 7

Rob W 的选项 3 对我很有用。基本上,后台脚本会 ping 内容脚本,如果没有响应,它将添加所有必要的文件。我只在一个选项卡被激活时才这样做,以避免在后台添加到每个打开的选项卡的复杂性:

背景.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"});
    }
  });
});
Run Code Online (Sandbox Code Playgroud)

内容.js

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

  • 抛出 ``Unchecked runtime.lastError: 无法建立连接。接收端不存在。`` 如果 content.js 不存在。 (2认同)