Electrolysis/Jetpack: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
m (Clarifications about existing CPOW limitations)
m (Shortened some code snippets)
Line 59: Line 59:
   addEventListener("tabs.onReady",
   addEventListener("tabs.onReady",
     function(event) {
     function(event) {
       var signature = event.getData("signature").QueryInterface(Components.interfaces.nsIVariant);
       var signature = event.getData("signature").QueryInterface(Ci.nsIVariant);
       sendAsyncMessage("tabs.onReady", {callback: signature});
       sendAsyncMessage("tabs.onReady", {callback: signature});
     }, false, true);
     }, false, true);
Line 86: Line 86:
Some jetpacks do things like the following:
Some jetpacks do things like the following:


   var win = Components.classes["@mozilla.org/appshell/window-mediator;1"]
   var win = Cc["@mozilla.org/appshell/window-mediator;1"]
             .getService(Components.interfaces.nsIWindowMediator)
             .getService(Ci.nsIWindowMediator)
             .getMostRecentWindow("navigator:browser");
             .getMostRecentWindow("navigator:browser");


I'm not yet sure what the best way to deal with this is.  Perhaps create an API that wraps the window mediator and allows you to get specific window CPOWs in well-defined ways?  Perhaps proxying the window mediator component to return CPOWs or something?
I'm not yet sure what the best way to deal with this is.  Perhaps create an API that wraps the window mediator and allows you to get specific window CPOWs in well-defined ways?  Perhaps proxying the window mediator component to return CPOWs or something?

Revision as of 02:10, 27 March 2010

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 Jetpack CPOW callback using a content CPOW document object (assuming content processes exist).

Note that the solutions as described are currently un-implementable for certain circumstances, because there's no way to pass chrome objects to the CPOW callback as a parameter. We need reverse (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(Ci.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.

Accessing windows

Some jetpacks do things like the following:

 var win = Cc["@mozilla.org/appshell/window-mediator;1"]
           .getService(Ci.nsIWindowMediator)
           .getMostRecentWindow("navigator:browser");

I'm not yet sure what the best way to deal with this is. Perhaps create an API that wraps the window mediator and allows you to get specific window CPOWs in well-defined ways? Perhaps proxying the window mediator component to return CPOWs or something?