API 实现基础¶
此页面描述了创建 WebExtensions API 时涉及的一些部分。有关这些部分如何协同工作以构建特定功能的详细文档位于下一节。
API 模式¶
如前所述,WebExtension 在沙盒环境中运行,但 WebExtensions API 的实现则具有完整的 chrome 权限。API 实现不会直接与扩展的 Javascript 环境交互,这是由 WebExtensions 框架处理的。每个 API 都包含一个模式,该模式描述了 API 可能注入到扩展的 Javascript 环境中的所有函数、事件和其他属性。除其他事项外,该模式还指定了应将 API 注入到的命名空间、使用 API 所需的权限(如果有)以及在哪些上下文中(例如,扩展页面、内容脚本等)应使 API 可用。WebExtensions 框架读取此模式并负责将正确的对象注入到每个扩展的 Javascript 环境中。
API 模式使用 JSON 编写,并基于 JSON Schema,并进行了一些扩展以描述 API 函数和事件。下一节将详细描述模式的格式。
ExtensionAPI 类¶
每个 WebExtensions API 都由 Javascript ExtensionAPI 类的实例表示。每次启用具有访问 API 权限的扩展时,都会创建其 API 类的实例。此类的实例包含公开给扩展的函数和事件的实现,并且还包含用于处理清单密钥以及扩展生命周期其他部分(例如更新、卸载等)的代码。此类的详细信息将在后续部分介绍,目前需要注意的是,此类包含支持特定 WebExtensions API 的所有实际代码。
内置 API 与实验性 API¶
WebExtensions API 可以直接构建到浏览器中,也可以包含在一个称为特权扩展的特殊类型的扩展中,该扩展定义了 WebExtensions 实验(即实验性 API)。无论 API 将如何交付,API 模式和 ExtensionAPI 类的编写方式都相同,本节的其余部分将说明如何使用这些方法打包新的 API。
添加内置 API¶
内置 WebExtensions API 是延迟加载的。也就是说,在激活使用该 API 的扩展之前,实际上不会加载和解释模式和伴随的代码。要实际向 WebExtensions 框架注册 API,必须在以下文件中之一的 WebExtensions 模块列表中添加一个条目
toolkit/components/extensions/ext-toolkit.json
browser/components/extensions/ext-browser.json
mobile/shared/components/extensions/ext-android.json
这是一个新 API 的示例片段
"myapi": {
"schema": "chrome://extensions/content/schemas/myapi.json",
"url": "chrome://extensions/content/ext-myapi.js",
"paths": [
["myapi"],
["anothernamespace", "subproperty"]
],
"scopes": ["addon_parent"],
"permissions": ["myapi"],
"manifest": ["myapi_key"],
"events": ["update", "uninstall"]
}
schema
和 url
属性只是 API 模式和实现 API 的代码的 URL。上面的示例中的 chrome:
URL 通常是通过在 mozilla-central 目录中的 jar.mn
中添加条目来创建的,其中保存了 API 实现。API 实现的标准位置是
toolkit/components/extensions
:这是 Firefox 的桌面版和移动版(以及可能基于 Gecko 构建的任何其他应用程序)都适用的 API 应该放置的位置browser/components/extensions
:仅在 Firefox 桌面版上支持的 API。mobile/shared/components/extensions
:仅在 Firefox Android 版上支持的 API。
在相应的扩展目录中,约定是 API 模式位于名为 schemas/name.json
的文件中(其中 name 是 API 的名称,如果它具有 Javascript 可见功能,通常与它的命名空间相同)。ExtensionAPI 类的代码放在名为 ext-name.js
的文件中。如果 API 具有在子进程中运行的代码,则通常将其放在名为 ext-c-name.js
的文件中。
其余属性指定何时应加载 API。paths
、scopes
和 permissions
属性一起导致当扩展中的 Javascript 代码引用 browser
全局对象下属于 API 的部分时加载 API。paths
属性是一个路径数组,其中每个单独的路径也是一个属性名称数组。在上面的示例中,如果扩展引用 browser.myapi
或 browser.anothernamespace.subproperty
,则将加载示例 API。
browser
下的属性的引用仅在它出现在 scopes
属性中列出的范围内时才会导致加载 API。范围对应于 Javascript 环境(例如,扩展页面、内容脚本等)和应在其中运行 API 代码的进程(即主/父进程或内容/子进程)的组合。有效的 scopes
是
"addon_parent"
、"addon_child
:扩展页面"content_parent"
、"content_child
:内容脚本"devtools_parent"
、"devtools_child"
:开发者工具页面
_parent
和 _child
范围之间的区别将在后续部分详细解释。
只有当引用该属性的扩展也具有 permissions
属性中列出的所有权限时,对属性的引用才会导致加载 API。
受清单密钥控制的 WebExtensions API 也可以在激活包含相关清单密钥的扩展时加载。这由 manifest
属性指定,该属性列出了应导致加载 API 的任何清单密钥。
最后,可以基于 WebExtension 生命周期中的其他事件加载 API。这些事件在 events
属性中列出,并在 管理扩展生命周期 中进行了更详细的描述。
在特权扩展中添加实验性 API¶
新的 API 也可以在特权扩展中实现。以这种方式实现的 API 称为 WebExtensions 实验(或简称实验性 API)。当积极开发新的 API 时,实验很有用,因为它们不需要在本地构建 Firefox。这些扩展可以通过 about:debugging
临时安装,或者在支持它的浏览器(当前的 Nightly 和 Developer Edition)上,通过将首选项 xpinstall.signatures.required
设置为 false
来安装。您还可以将首选项 extensions.experiments.enabled
设置为 true
以正常安装加载项并在重启后进行测试。
注意
树外特权扩展无法由 addons.mozilla.org 签名。使用不同的管道用特权证书对其进行签名。您可以在 GitHub 上的 xpi-manifest 存储库 中找到更多信息。
与内置 API 相比,实验性 API 有一些限制
实验性 API(目前)只能公开给扩展页面,不能公开给开发者工具页面或内容脚本。
实验性 API 无法处理清单密钥(因为在加载实验性 API 之前需要解析和验证扩展清单)。
实验性 API 无法使用静态的
"update"
和"uninstall"
生命周期事件(因为通常这些事件可能在受影响的扩展未激活或未安装时发生)。
实验性 API 在 WebExtension 的 manifest.json
文件中的 experiment_apis
属性中声明。例如
{
"manifest_version": 2,
"name": "Extension containing an experimental API",
"experiment_apis": {
"apiname": {
"schema": "schema.json",
"parent": {
"scopes": ["addon_parent"],
"paths": [["myapi"]],
"script": "implementation.js"
},
"child": {
"scopes": ["addon_child"],
"paths": [["myapi"]],
"script": "child-implementation.js"
}
}
}
}
这与内置 API 所需的信息基本相同,只是组织方式不同。 schema
属性是扩展程序内部包含 API 架构的文件的相对路径。父进程和子进程的实际实现细节分别在 API 定义的 parent
和 child
属性中定义。在这些部分中,scope
和 paths
属性与内置 API 定义中的这些属性具有相同的含义(尽管请参阅上面关于限制的说明;scope
目前唯一有效的值是 "addon_parent"
和 "addon_child"
)。script
属性是扩展程序内部包含 API 实现的文件的相对路径。
包含以这种方式定义的实验的扩展程序会自动访问实验性 API。扩展程序还可以通过在其 manifest.json
文件的 permissions`
属性中包含字符串 experiments.name
来使用另一个扩展程序中实现的实验性 API。在这种情况下,字符串名称必须替换为定义该 API 的扩展程序中的 API 名称(例如,上面示例中的 apiname
)。
API 脚本全局中可用的全局变量¶
API 脚本不会作为 JSM 加载,因此
它们不会完全彼此隔离(并且当扩展程序第一次使用它们时,它们将被延迟加载)并在每个进程共享的全局作用域中执行)
特权扩展程序中嵌入的实验性 API 在每个扩展程序的全局作用域中执行(与内置 API 使用的作用域分开)
执行 API 脚本的全局作用域预先填充了一些有用的全局变量
AppConstants
console
CC
、Ci
、Cr
和Cu
ChromeWorker
extensions
、ExtensionAPI
、ExtensionCommon
和ExtensionUtils
global
MatchGlob
、MatchPattern
和MatchPatternSet
服务
StructuredCloneHolder
XPCOMUtils
有关所有 API 脚本中默认可用的全局变量的更完整和更新的列表,请查看以下来源
仅在 Firefox 父进程中可用:toolkit/components/extensions/parent/ext-toolkit.js
仅在 Firefox 子进程中可用:toolkit/components/extensions/child/ext-toolkit.js
仅在桌面版本中可用:browser/components/extensions/parent/ext-browser.js
仅在 Android 版本中可用:mobile/shared/components/extensions/ext-android.js
警告
扩展程序 API 作者绝不应重新定义这些全局变量,以避免在 API 脚本之间引入潜在冲突(例如,请参阅Bug 1697404 评论 3 和Bug 1697404 评论 4)。
WebIDL 绑定¶
在 manifest_version: 3
中,扩展程序将能够声明后台服务工作线程而不是后台页面,并且现有的 WebExtensions API 绑定无法注入到这个新的扩展程序全局中,因为它位于主线程之外。
为了将 WebExtensions API 绑定公开给 WebExtensions background.service_worker
全局,我们正在为 WebExtensions API 生成新的 WebIDL 绑定。
可以在此处找到体系结构的高级视图以及有关创建或修改 WebExtensions API 的 WebIDL 绑定的体系结构过程的更深入详细信息