Skip to main content
浏览器扩展有三种方式在网页里运行代码。manifest 的 content_scripts 条目会在每个匹配模式的页面上运行。chrome.scripting.executeScript 在你调用时于一个标签页里运行一次脚本。chrome.scripting.registerContentScripts 在运行时注册一个 content script,并让它保持注册状态。本页说明每种方式何时运行、需要什么权限,以及各浏览器的差异。它也列出注入失败时控制台会打印的行,以及文件在 Extension.js 项目中应放在哪里。

在页面里运行代码的三种方式

manifest content_scriptsmanifest.json 中的静态声明。浏览器会把列出的 jscss 文件注入到每个匹配 matches 的页面,时机由 run_at 决定。Extension.js 会编译每个条目并为 HMR 包装它,参见 Content script chrome.scripting.executeScript 从 service worker 或另一个扩展页面发起的一次性调用。它针对一个标签页,运行 files(扩展内的路径)或 func(序列化到页面里的函数,可带 args)。它需要 scripting 权限,外加对该标签页的访问权:来自用户手势后的 activeTab,或来自匹配的 host_permissions 模式。 chrome.scripting.registerContentScripts 一种动态注册,形状与 manifest 条目相同(idmatchesjscssrunAtworld)。浏览器从那一刻起把它注入到匹配的页面,并且注册默认会在浏览器重启后保留。它需要 scripting 权限以及覆盖 matcheshost_permissions,因为 activeTab 不适用于未来的页面。 当功能属于一组已知站点时,使用 manifest 条目。当用户触发功能时,例如点击工具栏按钮,使用 executeScript。当站点集合在运行时才决定时,例如来自设置页,使用 registerContentScripts

Manifest 片段

scripting 权限同时解锁两个运行时 API。activeTab 覆盖用户点击的那个标签页,host_permissions 覆盖每个匹配的页面,而动态注册需要后者:
Chromium 与 Firefox 都按原样读取这个块,所以它不需要浏览器前缀。只有在 manifest content script 上使用 world: "MAIN" 时才用前缀,因为 Firefox 会忽略该字段。把它声明为 chromium:world 并保留一个 isolated world 回退,写法见浏览器特定字段 与这份 manifest 对应的运行时调用:

各浏览器差异

在 Safari 上,仅启用扩展并不够。在你授予网站访问权之前,没有任何 content script 会运行,运行时注入的脚本也一样。启用与授权步骤见 Safari

你会看到的控制台输出

把你看到的那一行复制到搜索里。每一行对应一个原因。 Cannot access contents of url "https://example.com/". Extension manifest must request permission to access this host. 该标签页不在你的 host 权限之内,并且 activeTab 没有为它授予。把该 host 加入 host_permissions,或者在标签页上发生用户手势之后的处理函数里调用 executeScript Could not load file: 'scripts/highlight.ts'. executeScriptregisterContentScripts 调用写的是源文件路径。Extension.js 会把 scripts/highlight.ts 编译为 scripts/highlight.js,所以请注入产出的 .js 路径。 Failed to load resource: net::ERR_FILE_NOT_FOUND 一个以 .ts 结尾的 chrome-extension:// URL(或其他从未到达 dist/ 的路径)。修法相同:引用产出的 .js 文件,然后确认它存在于 dist/<browser>/scripts/ 下。 NS_ERROR_CONTENT_BLOCKED 同一个文件缺失错误在 Firefox 的 moz-extension:// URL 上的表现形式。注入产出的 .js 路径。 Cannot access a chrome:// URL 浏览器内置页面不能被脚本化。在普通的 https:// 页面上测试。 The extensions gallery cannot be scripted. Chrome Web Store 对所有扩展都禁止脚本化。换一个页面测试。 This page cannot be scripted due to an ExtensionsSettings policy. 受管理的浏览器在这个 host 上屏蔽了你的扩展。在策略允许的 host 上测试,或者换一个不受该策略管理的配置文件。

Extension.js 的做法

把运行时注入的文件放进 scripts/ 特殊文件夹。规则很短:
  • scripts/ 位于项目根目录、和 package.json 并排,而不是在 src/ 里。嵌套的 src/scripts/ 只是普通文件夹。
  • 那里的每个文件都会编译成 .js,并落在 dist/<browser>/scripts/<name>.js
  • filesjs 数组里引用产出的路径。.ts 路径能正常构建,但在浏览器里会 404。
  • 当运行时字面量写的是 .ts 源文件时,构建会打印一条警告,给出应使用的产出路径。
  • 该文件遵循 content script 契约:export default 一个同步函数,并返回可选的清理函数。
完整的文件夹契约见特殊文件夹 extension dev 期间,你用 executeScriptscripts/ 注入的脚本会在编辑时被重放,因此注入的代码会像声明式 content_scripts 一样实时更新。参见重新加载与 HMR 生成一个在运行时注入 scripts/ 入口的项目:
如果走静态路径,请改从 content 模板开始:

另请参阅