嵌入一个 WebExtension 需要使用 Firefox 51 或更高版本。在 SDK 附加组件中嵌入一个 WebExtension 还需要 jpm 1.2.0。
从 Firefox 51 开始,你可以在传统附加组件类型中嵌入一个 WebExtension。
传统附加组件可以是经典的自举扩展或者Add-on SDK 附加组件。嵌入式 WebExtension 的文件打包在传统附加组件中。嵌入式 WebExtension 并不直接与嵌入的附加组件共享范围,但可以使用 runtime
API 中定义的消息函数交换消息。
这意味着您可以一次性迁移传统附加组件到 WebExtensions,并且在此期间附加组件的功能完全保留。尤其是它可以让您从旧版附加组件迁移存储数据到 WebExtension,通过撰写一个中间的混合式附加组件,使用旧版 API 读取数据(例如 simple-prefs 或 preferences 服务)并使用 WebExtension API 写入它(例如 storage
)。
连同本指南,我们撰写了两个例子展示如何使用嵌入式 WebExtensions 来帮助从传统附加组件迁移。如何从自举式附加组件迁移以及如何从 SDK 附加组件迁移。
嵌入 WebExtension
如果传统附加组件是一个带有install.rdf 的自举式扩展,在该 RDF 中加入 "hasEmbeddedWebExtension" 并设为 "true":
<em:hasEmbeddedWebExtension>true</em:hasEmbeddedWebExtension>
true
:"hasEmbeddedWebExtension": true
my-boostrapped-addon/ chrome/ webextension/ manifest.json background.js ... bootstrap.js chrome.manifest install.rdf
my-sdk-addon/ index.js package.json webextension/ manifest.json background.js ...
Firefox 不将嵌入式 WebExtension 视为一个独立的附加组件。因此,您不应该为它指定一个附加组件 ID。如果你这样做,那只会被忽略。
但是,在您完成附加组件的迁移并移除旧式嵌入代码后,您必须添加一个 applications 键,设置 ID 为旧式附加组件的 ID。通过此方式,addons.mozilla.org 可以识别 WebExtension 是旧式附加组件的一个更新。
启动 WebExtension
嵌入式 WebExtension 必须由被嵌入的附加组件明确启动。
如果被嵌入的附加组件是一个自举式附加组件,那么传递到自举式扩展的 startup()
函数的 data
将获得一个明确的 webExtension
:
// bootstrapped add-on function startup({webExtension}) { ...
如果被嵌入的附加组件是一个 SDK 附加组件,它可以使用 sdk/webextension
模块访问一个 WebExtension 对象:
// SDK add-on const webExtension = require("sdk/webextension");
无论哪种方式,此对象都有一个 startup()
函数,它返回一个 Promise
。该 promise 使用一个 browser
属性解决一个对象:这包括 runtime
API,被嵌入的附加组件可以用它来与嵌入式 WebExtension 交换消息:
例如:
// bootstrapped add-on function startup({webExtension}) { webExtension.startup().then(api => { const {browser} = api; browser.runtime.onMessage.addListener(handleMessage); }); }
// SDK add-on
const webExtension = require("sdk/webextension");
webExtension.startup().then(api => {
const {browser} = api;
browser.runtime.onMessage.addListener(handleMessage);
});
应注意的是,嵌入的附加组件不能启动通信,它可以使用 onMessage
接收(并可选响应)一次性消息,并可以使用 onConnect
接受连接请求。
如果嵌入式 WebExtension 缺少一个 manifest,或者如果 manifest 无效,该 promise 会被拒绝。这种情况下,您可以在浏览器工具箱的控制台中看到更多细节。
交换消息
一旦嵌入式 WebExtension 处在运行,它可以使用 runtime
API 的子集与旧式附加组件交换消息:
- 它可以使用
runtime.sendMessage()
发送一次性消息。 - 它可以使用
runtime.connect()
建立一个连接。
无需连接的消息
要发送一条消息,WebExtension 可以使用 runtime.sendMessage()
。您可以省略 extensionId
参数,因为浏览器认为嵌入式 WebExtension 是被嵌入附加组件的一部分:
browser.runtime.sendMessage("message-from-webextension").then(reply => { if (reply) { console.log("response from legacy add-on: " + reply.content); } });
被嵌入的附加组件可以使用 runtime.onMessage
对象接收消息(并可选响应):
// bootstrapped add-on function startup({webExtension}) { // Start the embedded webextension. webExtension.startup().then(api => { const {browser} = api; browser.runtime.onMessage.addListener((msg, sender, sendReply) => { if (msg == "message-from-webextension") { sendReply({ content: "reply from legacy add-on" }); } }); }); }
基于连接的消息
要在 WebExtension 与传统附加组件间设置一个长寿命连接,WebExtension 可以使用 runtime.connect()
。
var port = browser.runtime.connect({name: "connection-to-legacy"}); port.onMessage.addListener(function(message) { console.log("Message from legacy add-on: " + message.content); });
旧式附加组件可以使用 runtime.onConnect
监听连接尝试,双方可以使用得到的 runtime.Port
来交换消息:
function startup({webExtension}) { // Start the embedded webextension. webExtension.startup().then(api => { const {browser} = api; browser.runtime.onConnect.addListener((port) => { port.postMessage({ content: "content from legacy add-on" }); }); }); }
从传统附加组件迁移数据
嵌入式 WebExtensions 的一项主要用途是迁移附加组件的存储数据。
人们从旧式附加组件类型迁移的一个主要问题是存储数据,因为旧式附加组件不能使用 WebExtension 存储 API,WebExtensions 也不能使用旧式存储 API。例如,如果一个 SDK 附加组件使用了 SDK 的 simple-prefs API 来存储首选项,WebExtension 版本不可能访问这项数据。
使用嵌入式 WebExtensions,您可以创建一个嵌入了 WebExtension 的附加组件中间版本来迁移数据。中间版本使用旧式 API 读取存储的数据,然后使用 WebExtension API 写入数据。
- 在初始版本中,基于 SDK 的附加组件使用 simple-prefs API 读取和写入附加组件首选项。
-
在中间版本中,SDK 附加组件启动嵌入式 WebExtension。WebExtension 要求 SDK 附加组件用 simple-prefs 检索存储的数据。WebExtension 然后使用
storage
API 存储数据。在某些情况下,中间版本必须在初始导入后保持数据同步。例如,附加组件的首选项界面仍然使用旧系统,所以如果用户在这里修改了设置,它修改的是旧数据。中间的附加组件必须监听这些更改并将新数据发送到嵌入式 WebExtension。
有个 "embedded-webextension-sdk" 示例说明了这一点。
- 在最终版本中,附加组件只是一个 WebExtension,并且只使用存储 API。
我们提供了两个例子说明此模式:"embedded-webextension-bootstrapped" 展示了从一个自举式附加组件迁移,而 "embedded-webextension-sdk" 展示了从一个 SDK 附加组件迁移。
限制
调试
如果您有一个嵌入了 WebExtension 的旧式附加组件,您不能使用新的附加组件调试器来调试它。您必须使用基于浏览器工具箱的旧版调试工具。