Access Add-On SDK 'main' from XUL

Access Add-On SDK 'main' from XUL

本文关键字:from XUL main Add-On Access SDK      更新时间:2023-09-26

在我的附加组件中,我使用XUL来显示对话框窗口,因为我可以自定义它们的外观以适应附加组件的一般样式(如自定义titlebar)。

使用迁移指南,我可以很容易地做到这一点。问题是,我想从XUL对话框中调用附加组件的main模块中的某些函数。

经过一番搜索,我发现了loader模块,这似乎能够做到我想要的。但是,我在使用它访问main模块时遇到了麻烦。

首先,我尝试了文档中提到的显而易见的方法;

xul_dialog.js :

let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
    paths: {
        'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
        '': 'resource:///modules/',
        './': 'resource://<my-addon-name>/root/'
    }
});
let main = Loader.main(loader, './main');

我得到一个错误,'./main'没有找到resource://<my-addon-name>/root/。考虑到我使用了不正确的路径,我做了一些实验,直到我可以删除所有与路径相关的错误。

xul_dialog.js :

let {Loader} = Components.utils.import('resource://gre/modules/commonjs/toolkit/loader.js');
let loader = Loader.Loader({
    paths: {
        'toolkit/': 'resource://gre/modules/commonjs/toolkit/',
        '': 'resource://gre/modules/commonjs/',
        './': 'resource://<my-addon-id>-at-jetpack/<my-addon-name>/lib/'
    }
});
let main = Loader.main(loader, './main');

这次我在loader.js, line 279得到了一个相当令人困惑的错误。

Components is not available in this context.
Functionality provided by Components may be available in an SDK
module: https://jetpack.mozillalabs.com/sdk/latest/docs/
However, if you still need to import Components, you may use the
`chrome` module's properties for shortcuts to Component properties:
Shortcuts:
    Cc = Components.classes
    Ci = Components.interfaces
    Cu = Components.utils
    CC = Components.Constructor
Example:
    let { Cc, Ci } = require('chrome');

当我使用Loader.Require(loader, {id: './main'})而不是Loader.main时,我得到同样的错误。在实例化加载器时,我甚至尝试将Components作为globals传递,但没有太多运气。

我相当肯定我做错了很多事情。我不明白为什么我得到的错误,即使花了相当多的时间在loader.js。另外,我还认为有一个更好的选择,而不是必须使用附加id的路径main.js;像那样硬编码似乎是不对的。

你要做的是找到一个特定的Loader实例,而不是创建一个新的。

在main.js

const { id, name, prefixURI } = require("@loader/options");
//pass these to the XUL dialog

在xul.js(或任何xul对话框脚本的名称)

Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
var extensionscope = XPIProvider.bootstrapScopes[id];
var mainjssandbox = extensionscope.loader.sandboxes[prefixURI + name + "/lib/main.js"];

假设在main.js中有一个foo函数,你可以像

那样调用它
 mainjssandbox.foo();

当然,不要指望事情会像XUL和Add-on SDK实际上融合在一起一样工作。

如果它是你的 XUL对话框应该与你的插件交互,那么请不要使用Loader的东西,特别是不要去XPIProvider.bootstrapScopes @paa建议。虽然这可能是可行的(目前),但应该注意的是,它依赖于大量的实现细节,这些细节在任何时候都可能发生变化,这使得这个解决方案非常脆弱。

相反,有几个其他的选项(不是一个详尽的列表):

  • 如果SDK部分打开了窗口,您可以使用.openDialog,它支持向创建的窗口传递参数,这些参数甚至可以是对象和函数。此外,您可以拥有窗口调度(自定义)事件,并且您的SDK部分可以通过在.openDialog调用返回的window上调用addEventListener来侦听。
  • 如果窗口是从其他地方创建的(例如从AddonManager因为em:optionsURL),那么nsIObserverService是另一种通信方式。例如,.notifyObservers上的DOMContentLoaded包含对自身的引用。SDK部分只需要观察addObserver的此类通知。
  • 另一种方法,有点hacky,但工作,是SDK部分监听新窗口通过nsIWindowWatcher.registerNotification,然后注入一些API到browser.xul窗口通过XPCNativeWrapper.unwrap(subject.QueryInterface(Ci.nsIDOMWindow)).myAddonAPI = something

一定要处理好卸载或你的附加组件-它仍然是不可重启的-即逆转你所做的任何更改并删除任何观察者,等等。

如果您想与不是由您创建的SDK插件进行交互,那么XPIProvider路由可能是唯一可行的。但是,也许值得先联系插件作者,要求添加一些公共API,而不是深入到AddonManager和SDK加载器内部。

PS:

考虑将require或全局作用域通过openDialog传递给你的窗口,如果你想的话。通过将其放入main.js:

来获取全局作用域
const globalScope = this;