Параллелизм со сценарием надстройки Firefox и сценарием контента

Когда я писал надстройку для Firefox, используя SDK дополнений, я заметил, что код дополнения и код скрипта содержимого блокируют выполнение друг друга. Кроме того, код дополнения блокирует взаимодействие даже с другими окнами Firefox (не только с вкладками).

Какова модель параллелизма/процесса надстроек Firefox?

Возможно ли одновременное выполнение кода надстройки и кода сценария контента без совместной многопоточности (как таймеры)?

Сколько раз загружается код дополнения? Один раз на окно? Один раз на вкладку? Один раз?

документация указывает:

Платформа Mozilla движется к модели, в которой используются отдельные процессы для отображения пользовательского интерфейса, обработки веб-контента и выполнения надстроек. Основной код надстройки будет выполняться в процессе надстройки и не будет иметь прямого доступа к какому-либо веб-контенту.

Поэтому я надеюсь, что в будущем они действительно будут отдельными процессами, которые не будут мешать друг другу, но сейчас это не так.


Обновление:

Я пытался использовать page-worker из кода надстройки, но, к сожалению, он по-прежнему блокирует сценарий содержимого (как и все остальные javascript). Я также пытался использовать веб-работника в обработчике страниц, но получаю следующую ошибку при вызове функции postMessage веб-работника.

TypeError: worker.postMessage не является функцией

Я также пытался создать iframe в page-worker, а затем создать веб-воркер в iframe, но, к сожалению, я не могу использовать window.addEventListener из page-worker. Я получаю следующую ошибку:

TypeError: window.addEventMessage не является функцией

Наконец, я попытался внедрить скрипт (через элемент скрипта) на страницу page-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!' });
        });
    }
});

данные/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 });

person fixedpoint    schedule 09.02.2012    source источник


Ответы (2)


Система обмена сообщениями была разработана с учетом многопроцессорной среды. Однако эта среда так и не сложилась и, похоже, не появится и в ближайшем будущем. Итак, что у вас есть на самом деле, так это надстройка и сценарий контента, работающие в одном и том же процессе в основном потоке (потоке пользовательского интерфейса). А это значит, что одновременно работает только один из них, как вы уже заметили, параллелизма нет.

Можно ли одновременно запускать дополнительный код и код скрипта контента без совместной многопоточности (как таймеры)?

Да, вы используете веб-воркеры (которые не имеют ничего общего с модулем page-worker, несмотря на схожее название). . Как правило, это рекомендуется для дорогостоящих операций — вы не хотите, чтобы ваша надстройка переставала отвечать на сообщения, пока она что-то делает. К сожалению, Add-on 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"];

Сколько раз загружается код дополнения? Один раз на окно? Один раз на вкладку? Один раз?

Если вы спрашиваете о коде надстройки: он загружается только один раз и остается, пока надстройка активна. Что касается скриптов содержимого, то для каждого документа существует отдельный экземпляр, в который внедряется скрипт.

person Wladimir Palant    schedule 10.02.2012
comment
Кажется, это не работает. Если я пытаюсь создать веб-воркер в коде надстройки, он говорит ReferenceError: Worker не определен. Если я попытаюсь создать веб-воркер в скрипте контента, он выдаст сообщение ReferenceError: webWorker.addEventListener не является функцией. - person fixedpoint; 10.02.2012
comment
@ user967974: Да, после просмотра некоторых обсуждений Add-on SDK, похоже, что веб-воркеры еще не поддерживаются. Я обновил свой ответ хаком, который, тем не менее, позволяет вам их использовать. - person Wladimir Palant; 10.02.2012
comment
Кажется, это работает, но есть одно странное предостережение. Я не могу передать объект в postMessage рабочему. Выдает исключение Не удалось клонировать объект. Конечно, я могу сериализовать объекты с помощью JSON, а затем передать строку в worker. Спасибо. - person fixedpoint; 10.02.2012
comment
@ user967974: postMessage принимает только сериализуемые объекты JSON, это сообщение появляется всякий раз, когда вы пытаетесь передать ему что-то, что не имеет представления JSON. Опять же, могут быть проблемы с областью действия (модуль JS, из которого вы получили Worker, имел собственную область видимости со своими собственными классами Object и Array), и вам действительно нужно использовать сериализацию JSON вручную. - person Wladimir Palant; 10.02.2012

Я нашел хак, чтобы получить 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 обходной мусор и, кажется, работает довольно хорошо.

person andrew    schedule 23.03.2014