MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

Embedded WebExtensions

嵌入一个 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>
如果旧式附加组件是一个 SDK 附加组件,在 package.json 中包含 "hasEmbeddedWebExtension" 并设为 true
 
"hasEmbeddedWebExtension": true
WebExtension 本身放在附加组件中的 "webextension" 顶层目录。例如:
 
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 的子集与旧式附加组件交换消息:

无需连接的消息

要发送一条消息,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 的旧式附加组件,您不能使用新的附加组件调试器来调试它。您必须使用基于浏览器工具箱的旧版调试工具

文档标签和贡献者

 此页面的贡献者: yfdyh000
 最后编辑者: yfdyh000,