content_scripts 条目会在每个匹配模式的页面上运行。chrome.scripting.executeScript 在你调用时于一个标签页里运行一次脚本。chrome.scripting.registerContentScripts 在运行时注册一个 content script,并让它保持注册状态。本页说明每种方式何时运行、需要什么权限,以及各浏览器的差异。它也列出注入失败时控制台会打印的行,以及文件在 Extension.js 项目中应放在哪里。
在页面里运行代码的三种方式
manifestcontent_scripts。 在 manifest.json 中的静态声明。浏览器会把列出的 js 与 css 文件注入到每个匹配 matches 的页面,时机由 run_at 决定。Extension.js 会编译每个条目并为 HMR 包装它,参见 Content script。
chrome.scripting.executeScript。 从 service worker 或另一个扩展页面发起的一次性调用。它针对一个标签页,运行 files(扩展内的路径)或 func(序列化到页面里的函数,可带 args)。它需要 scripting 权限,外加对该标签页的访问权:来自用户手势后的 activeTab,或来自匹配的 host_permissions 模式。
chrome.scripting.registerContentScripts。 一种动态注册,形状与 manifest 条目相同(id、matches、js、css、runAt、world)。浏览器从那一刻起把它注入到匹配的页面,并且注册默认会在浏览器重启后保留。它需要 scripting 权限以及覆盖 matches 的 host_permissions,因为 activeTab 不适用于未来的页面。
当功能属于一组已知站点时,使用 manifest 条目。当用户触发功能时,例如点击工具栏按钮,使用
executeScript。当站点集合在运行时才决定时,例如来自设置页,使用 registerContentScripts。
Manifest 片段
scripting 权限同时解锁两个运行时 API。activeTab 覆盖用户点击的那个标签页,host_permissions 覆盖每个匹配的页面,而动态注册需要后者:
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'.
executeScript 或 registerContentScripts 调用写的是源文件路径。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。 - 在
files或js数组里引用产出的路径。.ts路径能正常构建,但在浏览器里会 404。 - 当运行时字面量写的是
.ts源文件时,构建会打印一条警告,给出应使用的产出路径。 - 该文件遵循 content script 契约:
export default一个同步函数,并返回可选的清理函数。
extension dev 期间,你用 executeScript 从 scripts/ 注入的脚本会在编辑时被重放,因此注入的代码会像声明式 content_scripts 一样实时更新。参见重新加载与 HMR。
生成一个在运行时注入 scripts/ 入口的项目:
content 模板开始:

