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 <bsendAsyncMessage 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.