与 Firefox 附加脚本和内容脚本的并发

发布于 2025-01-04 06:31:52 字数 2598 浏览 3 评论 0原文

当我使用 附加SDK,我注意到附加代码和内容脚本代码会互相阻塞执行。此外,附加代码似乎甚至阻止了与其他 Firefox 窗口(不仅仅是选项卡)的交互。

Firefox 附加组件的并发/进程模型是什么?

是否可以在没有协作多线程(定时器)的情况下同时运行附加组件代码和内容脚本代码?

附加代码加载了多少次?每个窗口一次?每个选项卡一次?一次?

文档指出:

Mozilla 平台正在走向一种使用它的模型 单独的进程来显示 UI、处理 Web 内容和执行 附加组件。主要的附加代码将在附加进程中运行,并将 无法直接访问任何网络内容。

所以我希望将来它们确实是独立的进程,不会互相干扰,但现在情况似乎并非如此。


更新:

我尝试使用附加代码中的页面工作人员,但不幸的是,这仍然阻止内容脚本(以及所有其他 JavaScript)。我还尝试在页面工作人员中使用网络工作人员,但在调用网络工作人员的 postMessage 函数时出现以下错误。

类型错误:worker.postMessage 不是函数

我也尝试在页面工作人员中创建一个 iframe,然后在 iframe 中创建一个网络工作人员,但不幸的是我无法从页面工作人员中使用 window.addEventListener。我收到以下错误:

类型错误:window.addEventMessage 不是函数

最后,我尝试将脚本(通过 script 元素)注入到 page-worker 页面中,以创建一个似乎可以工作的 Web Worker。不幸的是,我无法与这个网络工作者通信,因为我只能通过 document.defaultView.postMessage 向它发送消息。

哦,我正在编织的纠结的网...

内容脚本 ->附加组件->页面工作者-> iframe->网络工作者 ->我的代码


我包含了一个简单的示例:

package.json

{
    "name": "test", 
    "author": "me", 
    "version": "0.1", 
    "fullName": "My Test Extension", 
    "homepage": "http://example.com", 
    "id": "jid1-FmgBxScAABzB2g", 
    "description": "My test extension"
}

lib/main.js

var data = require("self").data;
var pageMod = require("page-mod");

pageMod.PageMod({
    include: ["http://*", "https://*"],
    contentScriptWhen: "start",
    contentScriptFile: [data.url("content.js")],
    onAttach: function (worker) {
        worker.port.on("message", function (data) {
            // simulate an expensive operation with a busy loop
            var start = new Date();
            while (new Date() - start < data.time);
            worker.port.emit("message", { text: 'done!' });
        });
    }
});

data/content.js

self.port.on("message", function (response) {
    alert(response.text);
});

// call a very expensive operation in the add-on code
self.port.emit("message", { time: 10000 });

As I was writing a Firefox add-on using the Add-on SDK, I noticed that the add-on code and the content script code block the execution of each other. Furthermore, the add-on code seems even to block the interaction with other Firefox windows (not just tabs).

What is the concurrency/process model of Firefox add-ons?

Is it possible to run add-on code and content script code concurrently without cooperative multithreading (a la timers)?

How many times is the add-on code loaded? Once per window? Once per tab? Once?

The documentation states:

The Mozilla platform is moving towards a model in which it uses
separate processes to display the UI, handle web content, and execute
add-ons. The main add-on code will run in the add-on process and will
not have direct access to any web content.

So I hope that in the future that they are indeed separate processes that will not interfere with each other, but that doesn't seem to be the case now.


Update:

I have tried using a page-worker from the add-on code, but unfortunately that still blocks the content script (as well as all other javascript). I also tried using a web worker in the page-worker, but I get the following error when calling the web worker's postMessage function.

TypeError: worker.postMessage is not a function

I also tried creating an iframe in the page-worker and then creating a web worker in the iframe, but unfortunately I cannot use window.addEventListener from the page-worker. I get the following error:

TypeError: window.addEventMessage is not a function

Finally, I tried to inject script (via script element) into the page-worker page to create a web worker which does seem to work. Unfortunately, I cannot communicate with this web worker because I can only send messages to it via document.defaultView.postMessage.

Oh the tangled webs I am weaving...

content-script -> add-on -> page-worker -> iframe -> web worker -> my code


I have included a simple example:

package.json

{
    "name": "test", 
    "author": "me", 
    "version": "0.1", 
    "fullName": "My Test Extension", 
    "homepage": "http://example.com", 
    "id": "jid1-FmgBxScAABzB2g", 
    "description": "My test extension"
}

lib/main.js

var data = require("self").data;
var pageMod = require("page-mod");

pageMod.PageMod({
    include: ["http://*", "https://*"],
    contentScriptWhen: "start",
    contentScriptFile: [data.url("content.js")],
    onAttach: function (worker) {
        worker.port.on("message", function (data) {
            // simulate an expensive operation with a busy loop
            var start = new Date();
            while (new Date() - start < data.time);
            worker.port.emit("message", { text: 'done!' });
        });
    }
});

data/content.js

self.port.on("message", function (response) {
    alert(response.text);
});

// call a very expensive operation in the add-on code
self.port.emit("message", { time: 10000 });

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

被你宠の有点坏 2025-01-11 06:31:52

消息传递系统在设计时考虑了多进程环境。不过这个环境并没有出现,看来近期也不会出现。因此,您真正拥有的是在主线程(UI 线程)上的同一进程中运行的附加组件和内容脚本。这意味着一次只有其中一个正在运行,正如您已经注意到的那样,没有并发性。

是否可以在没有协作多线程(la 计时器)的情况下同时运行附加代码和内容脚本代码?

是的,您使用网络工作人员(与page-worker< /code> 模块,尽管名称相似)。对于昂贵的操作,这通常是推荐的 - 您不希望您的附加组件在执行某些操作时停止响应消息。不幸的是,附加 SDK 无法正确公开网络工作人员,因此我不得不使用建议的解决方法 此处

worker.port.on("message", function (message) {
    // Get the worker class from a JavaScript module and unload it immediately
    var {Cu} = require("chrome");
    var {Worker} = Cu.import(data.url("dummy.jsm"));
    Cu.unload(data.url("dummy.jsm"));

    var webWorker = new Worker(data.url("expensiveOperation.js"));
    webWorker.addEventListener("message", function(event)
    {
      if (event.data == "done")
        worker.port.emit("message", { text: 'done!' });
    }, false);
});

JavaScript 模块data/dummy.jsm 仅包含一行:

var EXPORTED_SYMBOLS=["Worker"];

附加代码加载了多少次?每个窗口一次?每个选项卡一次?一次?

如果您询问附加组件代码:它仅加载一次,并且只要附加组件处于活动状态,它就会一直存在。至于内容脚本,注入脚本的每个文档都有一个单独的实例。

The messaging system has been designed with a multi-process environment in mind. However, this environment didn't emerge and it looks like it won't happen in near future either. So what you really have is both the add-on and the content script running in the same process on the main thread (UI thread). And that means that only one of them is running at a time, as you already noticed there is no concurrency.

Is it possible to run add-on code and content script code concurrently without cooperative multithreading (a la timers)?

Yes, you use web workers (that have nothing to do with the page-worker module despite a similar name). This would be generally recommendable for expensive operations - you don't want your add-on to stop responding to messages while it is doing something. Unfortunately, the Add-on SDK doesn't expose web workers properly so I had to use the work-around suggested here:

worker.port.on("message", function (message) {
    // Get the worker class from a JavaScript module and unload it immediately
    var {Cu} = require("chrome");
    var {Worker} = Cu.import(data.url("dummy.jsm"));
    Cu.unload(data.url("dummy.jsm"));

    var webWorker = new Worker(data.url("expensiveOperation.js"));
    webWorker.addEventListener("message", function(event)
    {
      if (event.data == "done")
        worker.port.emit("message", { text: 'done!' });
    }, false);
});

The JavaScript module data/dummy.jsm only contains a single line:

var EXPORTED_SYMBOLS=["Worker"];

How many times is the add-on code loaded? Once per window? Once per tab? Once?

If you are asking about add-on code: it is loaded only once and stays around as long as the add-on is active. As to content scripts, there is a separate instance for each document where the script is injected.

静待花开 2025-01-11 06:31:52

我发现了一个在扩展的后台页面中获取 WebWorkers 的技巧:

if(typeof(Worker) == 'undefined')
{
    var chromewin   =   win_util.getMostRecentBrowserWindow();
    var Worker      =   chromewin.Worker;
}
var worker      =   new Worker(data.url('path/to/script.js'));

通过访问主窗口的 window 对象,您可以将 Worker 类拉入当前范围。这绕过了所有令人讨厌的 Page.Worker 解决方法垃圾,并且似乎工作得相当好。

I found a hack to get WebWorkers in the extension's background page:

if(typeof(Worker) == 'undefined')
{
    var chromewin   =   win_util.getMostRecentBrowserWindow();
    var Worker      =   chromewin.Worker;
}
var worker      =   new Worker(data.url('path/to/script.js'));

By accessing the main window's window object, you can pull the Worker class into the current scope. This gets around all the obnoxious Page.Worker workaround junk and seems to work fairly well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文