Electrolysis/Jetpack
In order to move Jetpack to an out-of-process implementation, a mixture of CPOWs and proxying appears to be necessary. I've encountered one main issue that must be considered:
Using callbacks
Given an API that allows the following:
jetpack.tabs.onReady(
function(doc) {
$("#list", doc).appendChild(listElem);
}
}
Certain design choices are necessitated by the limitation of CPOWs - namely, functions cannot be sent directly between processes. Therefore, there are two choices:
1. any API that currently takes a callback must instead take an object with a named callback
In the chrome process:
function onReady(callbackObj) {
associateCallbackWithDOMDocumentLoaded(callbackObj.callback);
}
In the Jetpack process, the jetpack.tabs object can theoretically be a CPOW to the chrome Tabs object, and everything should work fine.
2. any API that currently takes a callback can continue to do so, but must proxy the call
In the jetpack process:
Tabs = {
onReady: function(callback) {
let uniqueSignature = new Date.time().toString();
this.params[uniqueSignature] = callback;
sendAsyncMessage("tabs.onReady", {callback: uniqueSignature});
}
}
In the chrome process:
addMessageListener("tabs.onReady",
function(m) {
let child = getCPOW(m.target);
JetpackEnv.Tabs.onReady(child.jetpack.Tabs.params[m.json.callback]);
});
Tabs = {
onReady: function(callback) {
associateCallbackWithDOMDocumentLoaded(callback);
}
}
In either instance, the chrome process will end up calling the CPOW callback using a CPOW document object (assuming content processes exist).
Note that the solutions as described are currently un-implementable, because there's no way to pass the document object to the CPOW callback as a parameter. We need chrome -> jetpack CPOWs for that to work, and they don't exist yet.
The upside is that jQuery should Just Work as soon as the chrome -> jetpack CPOWs do exist.
Please note that the above code is very much simplified from the actual implementation. One complication is that sendAsyncMessage is not accessible at document level. Instead, you need code like the following to implement proxying:
From a script loaded into the remote jetpack process via loadFrameScript:
addEventListener("tabs.onReady",
function(event) {
var signature = event.getData("signature").QueryInterface(Components.interfaces.nsIVariant);
sendAsyncMessage("tabs.onReady", {callback: signature});
}, false, true);
In the Jetpack process implementation:
Tabs = {
onReady: function(callback) {
let uniqueSignature = new Date.time().toString();
this.params[uniqueSignature] = callback;
var eventDoc = document.QueryInterface(Ci.nsIDOMDocumentEvent);
var event = eventDoc.createEvent("datacontainerevents");
var container = event.QueryInterface(Ci.nsIDOMDataContainerEvent);
var variant = Cc["@mozilla.org/variant;1"].createInstance(Ci.nsIWritableVariant);
variant.setAsAString(uniqueSignature);
container.setData("signature", variant);
event.initEvent("tabs.onReady", true, false);
window.dispatchEvent(event);
}
}
Still, that's just an implementation detail.