Reputation: 1625
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 });
Upvotes: 3
Views: 3696
Reputation: 2879
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.
Upvotes: 0
Reputation: 57651
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.
Upvotes: 6