Web 扩展是否需要显式加载内容脚本?

Does a web extension need to explicitly load content scripts?

我正在尝试编写一个简单的 Web 扩展。 目前我正在学习各种教程并尝试了解架构。

与特定选项卡的交互是通过注入网站源代码的 content_scripts 完成的。 在我看来,好像 content_scripts 是自动加载的: https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Content_scripts

MDN 上的 tutorial 使这一点更加清楚:

This script will be loaded into the pages that match the pattern given in the content_scripts manifest.json key. The script has direct access to the document, just like scripts loaded by the page itself.

我的扩展应该为每个文本选择提供一个上下文菜单。 作为起点,我找到了 chrome 的有用示例扩展。你可以在这里找到它https://developers.chrome.com/extensions/samples,它被称为"speak selection"

此扩展正在使用 tts 引擎阅读选定的文本。 但是源代码的一部分令人困惑:它们在选项卡中具有 运行 content_scripts 的显式功能。此代码在其后台脚本之一中作为 Init() 函数的一部分执行:

function loadContentScriptInAllTabs() {
  chrome.windows.getAll({'populate': true}, function(windows) {
    for (var i = 0; i < windows.length; i++) {
      var tabs = windows[i].tabs;
      for (var j = 0; j < tabs.length; j++) {
        chrome.tabs.executeScript(
            tabs[j].id,
            {file: 'keycodes.js', allFrames: true});
        chrome.tabs.executeScript(
            tabs[j].id,
            {file: 'content_script.js', allFrames: true});
      }
    }
  });
}

据我所知,代码是在浏览器启动时立即执行的。 这不是多余的吗?

他们的manifest.json应该负责content_script的执行,这里是相关代码:

"content_scripts": [
    {
      "matches": [
        "<all_urls>"
      ],
      "all_frames": true,
      "js": [
        "keycodes.js",
        "content_script.js"
      ]
    }
  ],

将脚本注入每个打开的选项卡的正确方法是什么?

简答:Chrome需要它,但 Firefox 不需要。


Chrome does not load 内容脚本在扩展加载时匹配页面(包括扩展更新,而不仅仅是初始加载)。

因此,如果您希望在扩展加载时打开选项卡中的内容脚本功能(而不是未来的导航),则需要此代码(或类似代码)。

您可以使用 chrome.tabs.query.

对这段代码进行一些现代化改造

Firefox 在这方面与 Chrome 不兼容:它 会自动在加载时匹配的所有内容中注入内容脚本。所以你应该使用一些浏览器检测 and/or inject-only-once guard code.

我希望他们没有将其作为重大更改引入。至少提供一个清单键来选择行为以便于转换是有意义的。


注意:在扩展重新加载场景中,旧实例的内容脚本继续存在(但扩展 API 将失败);优雅地处理它是你的责任。

您可以明确地将内容(前景)页面加载到任何选项卡中,或者创建一个选项卡然后加载内容页面。从后台脚本执行此操作。除了需要活动选项卡权限外,您不需要清单文件中的条目。

browser.tabs.executeScript(null,{file:"/content.js"}).then(NextAction);
function NextAction(Array of last evaluated statement of foreground script in each frame)
   {...}