简单示例

作为教程,让我们创建一个非常简单的示例,包含几个模块

  • 一个根(父进程)模块来检索浏览器的当前版本

  • 一个 windowglobal(内容进程)模块来检索给定标签页的位置

这里使用的一些概念不会详细解释。后续应该会有更多文档来澄清这些。

在本示例中,我们不会使用事件,只使用命令。

创建根 version 模块

首先让我们创建根模块。

import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";

class VersionModule extends Module {
  destroy() {}

  getVersion() {
    return Services.appinfo.platformVersion;
  }
}

export const version = VersionModule;

所有模块都应该扩展 Module.sys.mjs 并且必须定义一个 destroy 方法。Module 类的每个公共方法都将作为此模块的命令公开。用于导出模块类的名称将是模块的公共名称,用于在其上调用命令。

创建 windowglobal location 模块

让我们创建第二个模块。

import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";

class LocationModule extends Module {
  #window;

  constructor(messageHandler) {
    super(messageHandler);

    // LocationModule will be a windowglobal module, so `messageHandler` will
    // be a WindowGlobalMessageHandler which comes with a few helpful getters
    // such as a `window` getter.
    this.#window = messageHandler.window;
  }

  destroy() {
    this.#window = null;
  }

  getLocation() {
    return this.#window.location.href;
  }
}

export const location = LocationModule;

我们可以简化模块,只需编写 getLocation 来返回 this.messageHandler.window.location.href,但这给了我们一个机会来了解模块构造函数。

将模块注册为 Firefox 模块

在我们将这些模块注册到 MessageHandler 框架之前,我们需要先将它们注册为 Firefox 模块。为简单起见,我们可以假设它们添加到一个新的文件夹 remote/example

  • remote/example/modules/root/version.sys.mjs

  • remote/example/modules/windowglobal/location.sys.mjs

在 jar.mn 中注册它们,以便它们可以像任何其他 Firefox 模块一样加载。

这些路径仅包含相应的层(root、windowglobal)以提高清晰度。我们不依赖于此作为命名约定来实际加载模块,因此您可以决定以不同的方式组织文件夹。但是,用于导出模块类的名称(例如 location)将是模块的正式名称,用于命令和事件,因此请注意并使用正确的导出名称。

定义 ModuleRegistry

但是,我们需要指示框架在何处加载每个模块。

这是通过 ModuleRegistry 完成的。在不深入细节的情况下,每个打算与 MessageHandler 框架一起使用的“模块集”都需要提供一个 ModuleRegistry 模块,该模块导出单个 getModuleClass 帮助程序。框架将调用此方法以了解哪些模块可用。现在,让我们在 remote/example/modules/root/ModuleRegistry.sys.mjs 下为我们定义最简单的注册表。

export const getModuleClass = function(moduleName, moduleFolder) {
  if (moduleName === "version" && moduleFolder === "root") {
    return ChromeUtils.importESModule(
      "chrome://remote/content/example/modules/root/version.sys.mjs"
    ).version;
  }
  if (moduleName === "location" && moduleFolder === "windowglobal") {
    return ChromeUtils.importESModule(
      "chrome://remote/content/example/modules/windowglobal/location.sys.mjs"
    ).location;
  }
  return null;
};

请注意,这可以通过定义一些命名约定或模式来改进,但目前每个模块集都可以根据需要自由实现此逻辑。

也将此模块添加到 jar.mn 中,以便它成为有效的 Firefox 模块。

使用自定义 ModuleRegistry 的临时解决方法

这样,我们就有一组几乎可以使用的模块了。除了目前 MessageHandler 硬编码为仅使用 WebDriver BiDi 模块之外。一旦 Bug 1722464 修复,我们将能够指定其他协议,但目前,指示 MessageHandler 框架使用非 bidi 模块的唯一方法是更新 以下行 以指向 remote/example/modules/ModuleRegistry.sys.mjs

现在有了这个,您应该能够创建一个 MessageHandler 网络并使用您的模块。

试一试

例如,您可以打开浏览器控制台并运行以下代码段

(async function() {
  const { RootMessageHandlerRegistry } = ChromeUtils.importESModule(
    "chrome://remote/content/shared/messagehandler/RootMessageHandlerRegistry.sys.mjs"
  );
  const messageHandler = RootMessageHandlerRegistry.getOrCreateMessageHandler("test-session");
  const version = await messageHandler.handleCommand({
    moduleName: "version",
    commandName: "getVersion",
    params: {},
    destination: {
      type: "ROOT",
    },
  });
  console.log({ version });

  const location = await messageHandler.handleCommand({
    moduleName: "location",
    commandName: "getLocation",
    params: {},
    destination: {
      type: "WINDOW_GLOBAL",
      id: gBrowser.selectedBrowser.browsingContext.id,
    },
  });
  console.log({ location });
})();

这应该打印一个版本号 { version: "109.0a1" } 和一个位置 { location: "https://www.mozilla.org/en-US/" }(您的实际值当然应该有所不同)。

我们有意跳过对传递给 handleCommand 的各种参数以及 RootMessageHandlerRegistry 的详细解释,但这应该已经让您对如何开始创建模块并使用它们有所了解。