Closed Bug 962804 Opened 10 years ago Closed 2 years ago

Implement a js-friendly IPC actor API

Categories

(Core :: DOM: Content Processes, task, P3)

task

Tracking

()

RESOLVED WORKSFORME

People

(Reporter: fabrice, Unassigned)

References

Details

JS code using IPC makes use of the various message managers to exchange messages. This mechanism is relatively low level and leads to lots of code duplication and to subtle bugs when people forget to do the right thing.

However many use cases would be covered by an API that would provide the following:
- 1 to 1 communication between an object instance in the child and it's mirror in the parent.
- N to 1 communication between child objects and a parent singleton. The singleton will then automatically manage it's children list so broadcasting events won't target useless processes.

It has also become idiomatic in js to use promises for async calls, so an API proposal looks like:

The child side initiates the communication, and we create a new child actor object with:
let childObject = new JSChildActor("great-api", /* "protocol" name   */
                                   false,       /* isParentSingleton */
                                   callbacks);  /* an object that will receive
                                                   calls from the parent */

The childObject can then call childObject.sendMessage(jsval data) to send an asynchronous message to the parent and get a promise.

That will create a new instance of an xpcom component in the parent registered for the  @mozilla.org/js-ipc/great-api-parent;1 contract id.

The parent objects would implement something like:

interface nsIParentJSIpc {
  /* called when a child object connects to this parent object.
   * XXX: maybe could return a bool telling if we "accept" this child?
   */
  void onchildcreated(nsIChildProxy childObject);

  /* called when the child object dies */
  void onchilddestroyed(nsIChildProxy childObject);

  /* The permission that the child process needs to be granted for the
   * creation of the parent <-> child channel to succeed.
   */
  readonly attribute string permission;

  /* Called when a given child object sends a message */
  Promise onmessage(jsval message, nsIChildProxy childObject);
};

with:

interface nsIChildProxy {
  /* This sends an asynchronous message to the matching child object */
  Promise sendMessage(jsval message);
};

Using sendMessage() is not the most idiomatic js solution. Ideally we would let childObject callbacks and nsIChildProxy instances have real functions defined somewhere? maybe we can reuse ipdl, or just something simpler if we don't care about checking parameters types & count. This would let us do:

childObject.setFoo("bar") that would succeed if the parent implement the setFoo() method. Likewise, childProxy.setBar("Hello world", 2, true) would call |setbar| on the |callbacks| object passed to JSChildActor. I'm pretty sure this is doable with some JS API magic.
Summary: Implement a js-friendly → Implement a js-friendly IPC actor API
Part of the design of the message manager was that the parent always did the startup. This was done for security reasons so that the child couldn't run arbitrary code in the parent.

I'm also wary of building more XPCOM into this: do we need C++ interop here, or can we do this entirely in JS by having a list of the protocols and routing them to the correct .jsm?
(In reply to Benjamin Smedberg  [:bsmedberg] from comment #1)
> Part of the design of the message manager was that the parent always did the
> startup. This was done for security reasons so that the child couldn't run
> arbitrary code in the parent.

By startup you mean attaching message listeners?

> I'm also wary of building more XPCOM into this: do we need C++ interop here,
> or can we do this entirely in JS by having a list of the protocols and
> routing them to the correct .jsm?

I can live with no xpcom there, sure. We won't have many cases where we need c++ support anyway.

Do you think you could work on that?
> By startup you mean attaching message listeners?

I basically mean: the parent is given the ability to load arbitrary code in the child. The child is not given the ability to load arbitrary code in the parent. And the current mechanism for that is that we register frame scripts from the parent to load in the child.

I welcome a less-cumbersome API which removes some of the footguns, if there is one to be had!

> Do you think you could work on that?

No, my team isbooked for the quarter.
Component: IPC → DOM: Content Processes
Priority: -- → P3

This seems to be on the border between IPC and ContentProcesses? I would assume that we were happily using promises and actors for 8 years now without doing this and can just close this?

Type: defect → task
Flags: needinfo?(nika)

Sounds good.

Status: NEW → RESOLVED
Closed: 2 years ago
Flags: needinfo?(nika)
Resolution: --- → WORKSFORME
You need to log in before you can comment on or make changes to this bug.