Electrolysis/Jetpack: Difference between revisions

Jump to navigation Jump to search
removed old content, marked-up code to improve readability
(removed old content, marked-up code to improve readability)
Line 10: Line 10:


Predefined globals:
Predefined globals:
* require()
 
* <code>require()</code>


=== implementation-jetpack context ===
=== implementation-jetpack context ===
Line 17: Line 18:


Predefined globals:
Predefined globals:
* jetpack // the jetpack context global, used to create jetpack.require
* <code>jetpack</code> - the jetpack context global, used to create <code>jetpack.require</code>
* sendMessage("messageName" [, data]); // sends asynchronously, no return value
* <code>sendMessage("messageName" [, data]);</code> - sends asynchronously, no return value
* callMessage("messageName" [, data]); // sends synchronously, has return value
* <code>callMessage("messageName" [, data]);</code> - sends synchronously, has return value
* createHandle(object[, parentHandle]); // returns handle object
* <code>createHandle(object[, parentHandle]);</code> - returns handle object
* other special globals as necessary, e.g. ctypes would be exposed entirely within the jetpack process and wouldn't need to communicate with chrome
* other special globals as necessary, e.g. ctypes would be exposed entirely within the jetpack process and wouldn't need to communicate with chrome
* registerReceiver(messageName, func) // incoming messages of the specified name
* <code>registerReceiver(messageName, func)</code> - incoming messages of the specified name
* wrap(object) // create a COW
* <code>wrap(object)</code> - create a COW


=== implementation-chrome context ===
=== implementation-chrome context ===
Line 29: Line 30:
The rest of the jetpack implementation would live in the chrome process, primarily in a JS module. A method would exist (via XPCOM?) to bootstrap a jetpack:
The rest of the jetpack implementation would live in the chrome process, primarily in a JS module. A method would exist (via XPCOM?) to bootstrap a jetpack:


var ajetpack = createJetpackProcess();
<code>var ajetpack = createJetpackProcess();</code>


* ajetpack.sendMessage("messageName" [, data]); // only async messages are allowed
* <code>ajetpack.sendMessage("messageName" [, data]);</code> - only async messages are allowed
* ajetpack.createHandle(object [, parentHandle]);
* <code>ajetpack.createHandle(object [, parentHandle]);</code>
* ajetpack.loadImplementation(uri); // load a script into the jetpack implementation context
* <code>ajetpack.loadImplementation(uri);</code> - load a script into the jetpack implementation context
* ajetpack.loadUserScript(uri); // load a script into the jetpack context
* <code>ajetpack.loadUserScript(uri);</code> - load a script into the jetpack context
* ajetpack.registerReceiver(messageName, func)
* <code>ajetpack.registerReceiver(messageName, func)</code>


=== Messages and Handles ===
=== Messages and Handles ===
Line 41: Line 42:
Messages that communicate between the browser and jetpack process may contain only serializable (JSON) data and "handles". A handle can be used to provide context for messages. Either the browser or the jetpack implementation may create a handle. A handle keeps its associated JS object alive, and has the following properties:
Messages that communicate between the browser and jetpack process may contain only serializable (JSON) data and "handles". A handle can be used to provide context for messages. Either the browser or the jetpack implementation may create a handle. A handle keeps its associated JS object alive, and has the following properties:


* handle.object // the JS object associated with the handle
* <code>handle.object</code> - the JS object associated with the handle
* handle.parent // the parent handle of the object, if any
* <code>handle.parent</code> - the parent handle of the object, if any
* handle.destroy() // destroy the handle so that is is no longer valid
* <code>handle.destroy()</code> - destroy the handle so that is is no longer valid
* handle.isValid // boolean, is this handle still valid?
* <code>handle.isValid</code> - boolean, is this handle still valid?


A handle that is created without a parent stays alive until it is explicitly destroyed.
A handle that is created without a parent stays alive until it is explicitly destroyed.
Line 57: Line 58:


Jetpack:
Jetpack:
<pre>const Request = require("request").Request;
<pre>const Request = require("request").Request;
new Request({
new Request({
Line 64: Line 66:


implementation-jetpack:
implementation-jetpack:
<pre>function jp_request(r)
<pre>function jp_request(r)
{
{
Line 97: Line 100:


implementation-chrome:
implementation-chrome:
<pre>var ajetpack = createJetpackProcess();
<pre>var ajetpack = createJetpackProcess();


Line 116: Line 120:
=== Unresolved Issues ===
=== Unresolved Issues ===


This proposal doesn't really deal with jetpack->content communication at all, for pagemods, etc.  That's an easier question, in some ways: we can use CPOWs, and that already basically works. What we don't know is how to bootstrap it: given `ajetpack` and a content winow in chrome, how do we set up the machinery for jetpack/content communication?
This proposal doesn't really deal with jetpack-to-content communication at all, for pagemods, etc.  That's an easier question, in some ways: we can use CPOWs, and that already basically works. What we don't know is how to bootstrap it: given a jetpack and a content winow in chrome, how do we set up the machinery for jetpack/content communication?


cjones needs this for cross-process layers as well.  The basic idea will be that, in the chrome process, some code will do
cjones needs this for cross-process layers as well.  The basic idea will be that, in the chrome process, some code will do
contentProcessParent.Bridge(jetpackParent, PJetpackContent)
<code>contentProcessParent.Bridge(jetpackParent, PJetpackContent)</code>
which will asynchronously open a new channel between the content and jetpack processes.  The new channel will talk PJetpackContent (or whatever that's called).  Haven't worked out the notification bits or how the new PJetpackContent top-level actors will be created, but probably the content and jetpack top-levels will get an OnBridge() notification and perhaps that will be where the actors will be required to be created.
which will asynchronously open a new channel between the content and jetpack processes.  The new channel will talk <code>PJetpackContent</code> (or whatever that's called).  Haven't worked out the notification bits or how the new <code>PJetpackContent</code> top-level actors will be created, but probably the content and jetpack top-levels will get an <code>OnBridge()</code> notification and perhaps that will be where the actors will be required to be created.
 
== Older Notes ==
 
<div class="note">The following content is older notes from jdm that may no longer be relevant.</div>
 
I've encountered one main issue that must be considered:
 
<h3>Using callbacks</h3>
 
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 <b>sendAsyncMessage</b> 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.
 
<h3>Accessing windows</h3>
 
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?


== External Resources ==
== External Resources ==


* [https://developer.mozilla.org/en/Multi-Process_Architecture/Jetpack Multi-process Jetpack] on MDC
* [https://developer.mozilla.org/en/Multi-Process_Architecture/Jetpack Multi-process Jetpack] on MDC
874

edits

Navigation menu