tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

jsactors.rst (27474B)


      1 JSActors
      2 ========
      3 
      4 In the Fission world, the preferred method of communication between between things-that-may-live-in-a-different-process are JSActors.
      5 
      6 At the time of this writing, Fission offers the following JSActors:
      7 
      8 - `JSProcessActor`, to communicate between a child process and its parent;
      9 - `JSWindowActor`, to communicate between a frame and its parent.
     10 
     11 JSProcessActor
     12 ---------------
     13 
     14 What are JSProcessActors?
     15 ~~~~~~~~~~~~~~~~~~~~~~~~~
     16 
     17 A JSProcess pair (see below) is the preferred method of communication between a child process and its parent process.
     18 
     19 In the Fission world, JSProcessActors are the replacement for e10s-era *process scripts*.
     20 
     21 The life of a JSProcessActor pair
     22 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     23 
     24 JSProcessActors always exist by pair:
     25 
     26 - one instance of `JSProcessActorChild`, which lives in the child process – for instance, `MyActorChild`;
     27 - one instance of `JSProcessActorParent`, which lives in the parent process – for instance, `MyActorParent`.
     28 
     29 The pair is instantiated lazily, upon the first call to `getActor("MyActor")` (see below). Note that if a
     30 parent process has several children, the parent process will typically host several instances of `MyActorParent`
     31 whereas the children will each host a single instance of `MyActorChild`.
     32 
     33 JSProcessActor primitives allow sending and receiving messages *within the pair*. As of this writing,
     34 JSProcessActor does not offer primitives for broadcasting, enumerating, etc.
     35 
     36 The pair dies when the child process dies.
     37 
     38 About actor names
     39 ``````````````````
     40 
     41 Note that the names
     42 `MyActorChild` and `MyActorParent` are meaningful – suffixes `Child` and `Parent` are how `getActor(...)` finds
     43 the correct classes to load within the JS code.
     44 
     45 
     46 JSWindowActor
     47 ---------------
     48 
     49 What are JSWindowActors?
     50 ~~~~~~~~~~~~~~~~~~~~~~~~~
     51 
     52 A JSWindowActor pair (see below) is the preferred method of communication between a frame and its parent, regardless of whether the frame
     53 and parent live in the same process or in distinct processes.
     54 
     55 In the Fission world, JSWindowActors are the replacement for *framescripts*. Framescripts were how we structured code to be aware of the parent (UI) and child (content) separation, including establishing the communication channel between the two (via the Frame Message Manager).
     56 
     57 However, the framescripts had no way to establish further process separation downwards (that is, for out-of-process iframes). JSWindowActors will be the replacement.
     58 
     59 How are they structured?
     60 ~~~~~~~~~~~~~~~~~~~~~~~~~~
     61 
     62 A review of the pre-Fission Message Manager mechanism
     63 `````````````````````````````````````````````````````
     64 
     65 .. note::
     66   There are actually several types of Message Managers: Frame Message Managers, Window Message Managers, Group Message Managers and Process Message Managers. For the purposes of this documentation, it's simplest to refer to all of these mechanisms altogether as the "Message Manager mechanism". Most of the examples in this document will be operating on the assumption that the Message Manager is a Frame Message Manager, which is the most commonly used one.
     67 
     68 Currently, in the post `Electrolysis Project`_ Firefox codebase, we have code living in the parent process (UI) that is in plain JS (.js files) or in ES modules (.sys.mjs files). In the child process (hosting the content), we use framescripts (.js) and also ES modules. The framescripts are instantiated once per top-level frame (or, in simpler terms, once per tab). This code has access to all of the DOM from the web content, including all iframes within it.
     69 
     70 The two processes communicate via the Frame Message Manager (mm) using the ``sendAsyncMessage`` / ``receiveMessage`` API, and any code in the parent can communicate with any code in the child (and vice versa), by just listening to the messages of interest.
     71 
     72 The Frame Message Manager communication mechanism follows a publish / subscribe pattern similar to how Events work in Firefox:
     73 
     74 1. Something exposes a mechanism for subscribing to notifications (``addMessageListener`` for the Frame Message Manager, ``addEventListener`` for Events).
     75 2. The subscriber is responsible for unsubscribing when there's no longer interest in the notifications (``removeMessageListener`` for the Frame Message Manager, ``removeEventListener`` for Events).
     76 3. Any number of subscribers can be attached at any one time.
     77 
     78 .. figure:: Fission-framescripts.png
     79   :width: 320px
     80   :height: 200px
     81 
     82 How JSWindowActors differ from the Frame Message Manager
     83 ``````````````````````````````````````````````````````````
     84 
     85 For Fission, the JSWindowActors replacing framescripts will be structured in pairs. A pair of JSWindowActors will be instantiated lazily: one in the parent and one in the child process, and a direct channel of communication between the two will be established. The JSWindowActor in the parent must extend the global ``JSWindowActorParent`` class, and the JSWindowActor in the child must extend the global ``JSWindowActorChild`` class.
     86 
     87 The JSWindowActor mechanism is similar to how `IPC Actors`_ work in the native layer of Firefox:
     88 
     89 #. Every Actor has one counterpart in another process that they can communicate directly with.
     90 #. Every Actor inherits a common communications API from a parent class.
     91 #. Every Actor has a name that ends in either ``Parent`` or ``Child``.
     92 #. There is no built-in mechanism for subscribing to messages. When one JSWindowActor sends a message, the counterpart JSWindowActor on the other side will receive it without needing to explicitly listen for it.
     93 
     94 Other notable differences between JSWindowActor's and Message Manager / framescripts:
     95 
     96 #. Each JSWindowActor pair is associated with a particular frame. For example, given the following DOM hierarchy::
     97 
     98     <browser src="https://www.example.com">
     99       <iframe src="https://www.a.com" />
    100       <iframe src="https://www.b.com" />
    101 
    102   A ``JSWindowActorParent`` / ``JSWindowActorChild`` pair instantiated for either of the ``iframe``'s would only be sending messages to and from that ``iframe``.
    103 
    104 #. There's only one pair per actor type, per frame.
    105 
    106   For example, suppose we have a ``ContextMenu`` actor. The parent process can have up to N instances of the ``ContextMenuParent`` actor, where N is the number of frames that are currently loaded. For any individual frame though, there's only ever one `ContextMenuChild` associated with that frame.
    107 
    108 #. We can no longer assume full, synchronous access to the frame tree, even in content processes.
    109 
    110   This is a natural consequence of splitting frames to run out-of-process.
    111 
    112 #. ``JSWindowActorChild``'s live as long as the ``WindowGlobalChild`` they're associated with.
    113 
    114  If in the previously mentioned DOM hierarchy, one of the ``<iframe>``'s unload, any associated JSWindowActor pairs will be torn down.
    115 
    116 .. hint::
    117   JSWindowActors are "managed" by the WindowGlobal IPC Actors, and are implemented as JS classes (subclasses of ``JSWindowActorParent`` and ``JSWindowActorChild``) instantiated when requested for any particular window. Like the Frame Message Manager, they are ultimately using IPC Actors to communicate under the hood.
    118 
    119 .. figure:: Fission-actors-diagram.png
    120   :width: 233px
    121   :height: 240px
    122 
    123 .. note::
    124    Like the Message Manager, JSWindowActors are implemented for both in-process and out-of-process frame communication. This means that porting to JSWindowActors can be done immediately without waiting for out-of-process iframes to be enabled.
    125 
    126 
    127 Communication with actors
    128 -------------------------
    129 
    130 Sending messages
    131 ~~~~~~~~~~~~~~~~
    132 
    133 The ``JSActor`` base class exposes two methods for sending messages. Both methods are asynchronous.
    134 There **is no way to send messages synchronously** with ``JSActor``.
    135 
    136 
    137 ``sendAsyncMessage``
    138 ````````````````````
    139 
    140    sendAsyncMessage("SomeMessage", value[, transferables]);
    141 
    142 The ``value`` is anything that can be serialized using the structured clone algorithm. Additionally, a ``nsIPrincipal`` can be sent without having to manually serialize and deserialize it.
    143 
    144 The ``transferables`` argument is an optional array of `Transferable`_ objects. Note that transferable objects like ``ArrayBuffers`` are not transferable across process and their contents will just be copied into the serialized data. However, ``transferables`` are still useful for objects like ``MessageChannel`` ports, as these can be transferred across process boundaries.
    145 
    146 .. note::
    147    Cross Process Object Wrappers (CPOWs) cannot be sent over JSWindowActors.
    148 
    149 
    150 ``sendQuery``
    151 `````````````
    152 
    153    Promise<any> sendQuery("SomeMessage", value);
    154 
    155 
    156 ``sendQuery`` improves upon ``sendAsyncMessage`` by returning a ``Promise``. The receiver of the message must then return a ``Promise`` that can eventually resolve into a value - at which time the ``sendQuery`` ``Promise`` resolves with that value.
    157 
    158 The ``sendQuery`` method arguments follow the same conventions as ``sendAsyncMessage``, with the second argument being a structured clone.
    159 
    160 Receiving messages
    161 ~~~~~~~~~~~~~~~~~~
    162 
    163 ``receiveMessage``
    164 ``````````````````
    165 
    166 To receive messages, you need to implement
    167 
    168    receiveMessage(value)
    169 
    170 The method receives a single argument, which is the de-serialized arguments that were sent via either ``sendAsyncMessage`` or ``sendQuery``.
    171 
    172 .. note::
    173    If `receiveMessage` is responding to a `sendQuery`, it MUST return a ``Promise`` for that message.
    174 
    175 .. hint::
    176    Using ``sendQuery``, and the ``receiveMessage`` is able to return a value right away? Try using ``Promise.resolve(value);`` to return ``value``, or you could also make your ``receiveMessage`` method an async function, presuming none of the other messages it handles need to get a non-Promise return value.
    177 
    178 Other methods that can be overridden
    179 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    180 
    181 ``constructor()``
    182 
    183 If there's something you need to do as soon as the ``JSActor`` is instantiated, the ``constructor`` function is a great place to do that.
    184 
    185 .. note::
    186    At this point the infrastructure for sending messages is not ready yet and objects such as ``manager`` or ``browsingContext`` are not available.
    187 
    188 ``observe(subject, topic, data)``
    189 `````````````````````````````````
    190 
    191 If you register your Actor to listen for ``nsIObserver`` notifications, implement an ``observe`` method with the above signature to handle the notification.
    192 
    193 ``handleEvent(event)``
    194 ``````````````````````
    195 
    196 If you register your Actor to listen for content events, implement a ``handleEvent`` method with the above signature to handle the event.
    197 
    198 .. note::
    199    Only JSWindowActors can register to listen for content events.
    200 
    201 ``actorCreated``
    202 ````````````````
    203 
    204 This method is called immediately *after* a child actor is created and initialized. Unlike the actor's constructor, it is possible to do things like access the actor's content window and send messages from this callback.
    205 
    206 ``didDestroy``
    207 ``````````````
    208 
    209 This is another point to clean-up an Actor before it is destroyed, but at this point, no communication is possible with the other side.
    210 
    211 .. note::
    212    This method cannot be async.
    213 
    214 .. note::
    215    As a `JSProcessActorChild` is destroyed when its process dies, a `JSProcessActorChild` will never receive this call.
    216 
    217 Other things exposed on a JSWindowActorParent
    218 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    219 
    220 ``CanonicalBrowsingContext``
    221 ````````````````````````````
    222 
    223 Getter: ``this.browsingcontext``.
    224 
    225 ``WindowGlobalParent``
    226 ``````````````````````
    227 
    228 TODO
    229 
    230 Other things exposed on a JSWindowActorChild
    231 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    232 
    233 ``BrowsingContext``
    234 ```````````````````
    235 
    236 TODO
    237 
    238 ``WindowGlobalChild``
    239 `````````````````````
    240 
    241 TODO
    242 
    243 
    244 Helpful getters
    245 ```````````````
    246 
    247 A number of helpful getters exist on a ``JSWindowActorChild``, including:
    248 
    249 ``this.document``
    250 ^^^^^^^^^^^^^^^^^
    251 
    252 The currently loaded document in the frame associated with this ``JSWindowActorChild``.
    253 
    254 ``this.contentWindow``
    255 ^^^^^^^^^^^^^^^^^^^^^^
    256 
    257 The outer window for the frame associated with this ``JSWindowActorChild``.
    258 
    259 ``this.docShell``
    260 ^^^^^^^^^^^^^^^^^
    261 
    262 The ``nsIDocShell`` for the frame associated with this ``JSWindowActorChild``.
    263 
    264 See `JSWindowActor.webidl`_ for more detail on exactly what is exposed on both ``JSWindowActorParent`` and ``JSWindowActorChild`` implementations.
    265 
    266 How to port from message manager and framescripts to JSWindowActors
    267 -------------------------------------------------------------------
    268 
    269 .. _fission.message-manager-actors:
    270 
    271 Message Manager Actors
    272 ~~~~~~~~~~~~~~~~~~~~~~
    273 
    274 While the JSWindowActor mechanism was being designed and developed, large sections of our framescripts were converted to an "actor style" pattern to make eventual porting to JSWindowActors easier. These Actors use the Message Manager under the hood, but made it much easier to shrink our framescripts, and also allowed us to gain significant memory savings by having the actors be lazily instantiated.
    275 
    276 You can find the list of Message Manager Actors (or "Legacy Actors") in :searchfox:`BrowserGlue.sys.mjs <browser/components/BrowserGlue.sys.mjs>` and :searchfox:`ActorManagerParent.sys.mjs <toolkit/modules/ActorManagerParent.sys.mjs>`, in the ``LEGACY_ACTORS`` lists.
    277 
    278 .. note::
    279  The split in Message Manager Actors defined between ``BrowserGlue`` and ``ActorManagerParent`` is mainly to keep Firefox Desktop specific Actors separate from Actors that can (in theory) be instantiated for non-Desktop browsers (like Fennec and GeckoView-based browsers). Firefox Desktop-specific Actors should be registered in ``BrowserGlue``. Shared "toolkit" Actors should go into ``ActorManagerParent``.
    280 
    281 "Porting" these Actors often means doing what is necessary in order to move their registration entries from ``LEGACY_ACTORS`` to the ``JSWINDOWACTORS`` list.
    282 
    283 Figuring out the lifetime of a new Actor pair
    284 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    285 
    286 In the old model, framescript were loaded and executed as soon as possible by the top-level frame. In the JSWindowActor model, the Actors are much lazier, and only instantiate when:
    287 
    288 1. They're instantiated explicitly by calling ``getActor`` on a ``WindowGlobal``, and passing in the name of the Actor.
    289 2. A message is sent to them.
    290 3. A pre-defined ``nsIObserver`` observer notification fires with the subject of the notification corresponding to an inner or outer window.
    291 4. A pre-defined content Event fires.
    292 
    293 Making the Actors lazy like this saves on processing time to get a frame ready to load web pages, as well as the overhead of loading the Actor into memory.
    294 
    295 When porting a framescript to JSWindowActors, often the first question to ask is: what's the entrypoint? At what point should the Actors instantiate and become active?
    296 
    297 For example, when porting the content area context menu for Firefox, it was noted that the ``contextmenu`` event firing in content was a natural event to wait for to instantiate the Actor pair. Once the ``ContextMenuChild`` instantiated, the ``handleEvent`` method was used to inspect the event and prepare a message to be sent to the ``ContextMenuParent``. This example can be found by looking at the patch for the `Context Menu Fission Port`_.
    298 
    299 .. _fission.registering-a-new-jswindowactor:
    300 
    301 Using ContentDOMReference instead of CPOWs
    302 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    303 
    304 Despite being outlawed as a way of synchronously accessing the properties of objects in other processes, CPOWs ended up being useful as a way of passing handles for DOM elements between processes.
    305 
    306 CPOW messages, however, cannot be sent over the JSWindowActor communications pipe, so this handy mechanism will no longer work.
    307 
    308 Instead, a new module called :searchfox:`ContentDOMReference.sys.mjs <toolkit/modules/ContentDOMReference.sys.mjs>` has been created which supplies the same capability. See that file for documentation.
    309 
    310 How to start porting parent-process browser code to use JSWindowActors
    311 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    312 
    313 The :ref:`fission.message-manager-actors` work made it much easier to migrate away from framescripts towards something that is similar to ``JSWindowActors``. It did not, however, substantially change how the parent process interacted with those framescripts.
    314 
    315 So when porting code to work with ``JSWindowActors``, we find that this is often where the time goes - refactoring the parent process browser code to accommodate the new ``JSWindowActor`` model.
    316 
    317 Usually, the first thing to do is to find a reasonable name for your actor pair, and get them registered (see :ref:`fission.registering-a-new-jswindowactor`), even if the actors implementations themselves are nothing but unmodified subclasses of ``JSWindowActorParent`` and ``JSWindowActorChild``.
    318 
    319 Next, it's often helpful to find and note all of the places where ``sendAsyncMessage`` is being used to send messages through the old message manager interface for the component you're porting, and where any messages listeners are defined.
    320 
    321 Let's look at a hypothetical example. Suppose we're porting part of the Page Info dialog, which scans each frame for useful information to display in the dialog. Given a chunk of code like this:
    322 
    323 .. code-block:: javascript
    324 
    325    // This is some hypothetical Page Info dialog code.
    326 
    327    let mm = browser.messageManager;
    328    mm.sendAsyncMessage("PageInfo:getInfoFromAllFrames", { someArgument: 123 });
    329 
    330    // ... and then later on
    331 
    332    mm.addMessageListener("PageInfo:info", async function onmessage(message) {
    333      // ...
    334    });
    335 
    336 If a ``PageInfo`` pair of ``JSWindowActor``'s is registered, it might be tempting to simply replace the first part with:
    337 
    338 .. code-block:: javascript
    339 
    340    let actor = browser.browsingContext.currentWindowGlobal.getActor("PageInfo");
    341    actor.sendAsyncMessage("PageInfo:getInfoFromAllFrames", { someArgument: 123 });
    342 
    343 However, if any of the frames on the page are running in their own process, they're not going to receive that ``PageInfo:getInfoFromAllFrames`` message. Instead, in this case, we should walk the ``BrowsingContext`` tree, and instantiate a ``PageInfo`` actor for each global, and send one message each to get information for each frame. Perhaps something like this:
    344 
    345 .. code-block:: javascript
    346 
    347    let contextsToVisit = [browser.browsingContext];
    348    while (contextsToVisit.length) {
    349      let currentContext = contextsToVisit.pop();
    350      let global = currentContext.currentWindowGlobal;
    351 
    352      if (!global) {
    353        continue;
    354      }
    355 
    356      let actor = global.getActor("PageInfo");
    357      actor.sendAsyncMessage("PageInfo:getInfoForFrame", { someArgument: 123 });
    358 
    359      contextsToVisit.push(...currentContext.children);
    360    }
    361 
    362 The original ``"PageInfo:info"`` message listener will need to be updated, too. Any responses from the ``PageInfoChild`` actor will end up being passed to the ``receiveMessage`` method of the ``PageInfoParent`` actor. It will be necessary to pass that information along to the interested party (in this case, the dialog code which is showing the table of interesting Page Info).
    363 
    364 It might be necessary to refactor or rearchitect the original senders and consumers of message manager messages in order to accommodate the ``JSWindowActor`` model. Sometimes it's also helpful to have a singleton management object that manages all ``JSWindowActorParent`` instances and does something with their results.
    365 
    366 Where to store state
    367 ~~~~~~~~~~~~~~~~~~~~
    368 
    369 It's not a good idea to store any state within a ``JSWindowActorChild`` that you want to last beyond the lifetime of its ``BrowsingContext``. An out-of-process ``<iframe>`` can be closed at any time, and if it's the only one for a particular content process, that content process will soon be shut down, and any state you may have stored there will go away.
    370 
    371 Your best bet for storing state is in the parent process.
    372 
    373 .. hint::
    374    If each individual frame needs state, consider using a ``WeakMap`` in the parent process, mapping ``CanonicalBrowsingContext``'s with that state. That way, if the associates frames ever go away, you don't have to do any cleaning up yourself.
    375 
    376 If you have state that you want multiple ``JSWindowActorParent``'s to have access to, consider having a "manager" of those ``JSWindowActorParent``'s inside of the same .sys.mjs file to hold that state.
    377 
    378 Registering a new actor
    379 -----------------------
    380 
    381 ``ChromeUtils`` exposes an API for registering actors, but both ``BrowserGlue`` and ``ActorManagerParent`` are the main entry points where the registration occurs. If you want to register an actor,
    382 you should add it either to ``JSPROCESSACTORS`` or ``JSWINDOWACTORS`` in either of those two files.
    383 
    384 In the ``JS*ACTORS`` objects, each key is the name of the actor pair (example: ``ContextMenu``), and the associated value is an ``Object`` of registration parameters.
    385 
    386 The full list of registration parameters can be found:
    387 
    388 - for JSProcessActor in file `JSProcessActor.webidl`_ as ``WindowActorOptions``, ``ProcessActorSidedOptions`` and ``ProcessActorChildOptions``.
    389 - for JSWindowActor in file `JSWindowActor.webidl`_ as ``WindowActorOptions``, ``WindowActorSidedOptions`` and ``WindowActorChildOptions``.
    390 
    391 Here's an example ``JSWindowActor`` registration pulled from ``BrowserGlue.sys.mjs``:
    392 
    393 .. code-block:: javascript
    394 
    395   Plugin: {
    396      kind: "JSWindowActor",
    397      parent: {
    398        esModuleURI: "resource:///actors/PluginParent.sys.mjs",
    399      },
    400      child: {
    401        esModuleURI: "resource:///actors/PluginChild.sys.mjs",
    402        events: {
    403          PluginCrashed: { capture: true },
    404        },
    405 
    406        observers: ["decoder-doctor-notification"],
    407      },
    408 
    409      allFrames: true,
    410    },
    411 
    412 This example is for the JSWindowActor implementation of crash reporting for GMP.
    413 
    414 Let's examine parent registration:
    415 
    416 .. code-block:: javascript
    417 
    418      parent: {
    419        esModuleURI: "resource:///actors/PluginParent.sys.mjs",
    420      },
    421 
    422 Here, we're declaring that class ``PluginParent`` (here, a subclass of ``JSWindowActorParent``) is defined and exported from module ``PluginParent.sys.mjs``. That's all we have to say for the parent (main process) side of things.
    423 
    424 .. note::
    425    It's not sufficient to just add a new .sys.mjs file to the actors subdirectories. You also need to update the ``moz.build`` files in the same directory to get the ``resource://`` linkages set up correctly.
    426 
    427 Let's look at the second chunk:
    428 
    429 .. code-block:: javascript
    430 
    431      child: {
    432        esModuleURI: "resource:///actors/PluginChild.sys.mjs",
    433        events: {
    434          PluginCrashed: { capture: true },
    435        },
    436 
    437        observers: ["decoder-doctor-notification"],
    438      },
    439 
    440      allFrames: true,
    441    },
    442 
    443 We're similarly declaring where the ``PluginChild`` subclassing ``JSWindowActorChild`` can be found.
    444 
    445 Next, we declare the content events which, when fired in a window, will cause the ``JSWindowActorChild`` to instantiate if it doesn't already exist, and then have ``handleEvent`` called on the ``PluginChild`` instance. For each event name, an Object of event listener options can be passed. You can use the same event listener options as accepted by ``addEventListener``. If an event listener has no useful effect when the actor hasn't been created yet, ``createActor: false`` may also be specified to avoid creating the actor when not needed.
    446 
    447 .. note::
    448  Content events make sense for ``JSWindowActorChild`` (which *have* a content) but are ignored for ``JSProcessActorChild`` (which don't).
    449 
    450 Next, we declare that ``PluginChild`` should observe the ``decoder-doctor-notification`` ``nsIObserver`` notification. When that observer notification fires, the ``PluginChild`` actor will be instantiated for the ``BrowsingContext`` corresponding to the inner or outer window that is the subject argument of the observer notification, and the ``observe`` method on that ``PluginChild`` implementation will be called. If you need this functionality to work with other subjects, please file a bug.
    451 
    452 .. note::
    453  Unlike ``JSWindowActorChild`` subclasses, observer topics specified for ``JSProcessActorChild`` subclasses will cause those child actor instances to be created and invoke their ``observe`` method no matter what the subject argument of the observer is.
    454 
    455 Finally, we say that the ``PluginChild`` actor should apply to ``allFrames``. This means that the ``PluginChild`` is allowed to be loaded in any subframe. If ``allFrames`` is set to false (the default), the actor will only ever load in the top-level frame.
    456 
    457 Design considerations when adding a new actor
    458 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    459 
    460 A few things worth bearing in mind when adding your own actor registration:
    461 
    462 - Any ``child`` or ``parent`` side you register **must** have a ``esModuleURI`` property.
    463 - You do not need to have both ``child`` and ``parent`` modules, and should avoid having actor sides that do nothing but send messages. The process without a defined module will still get an actor, and you can send messages from that side, but cannot receive them via ``receiveMessage``. Note that you **can** also use ``sendQuery`` from this side, enabling you to handle a response from the other process despite not having a ``receiveMessage`` method.
    464 - If you are writing a JSWindowActor, consider whether you really need ``allFrames`` - it'll save memory and CPU time if we don't need to instantiate the actor for subframes.
    465 - When copying/moving "Legacy" :ref:`fission.message-manager-actors`, remove their ``messages`` properties. They are no longer necessary.
    466 
    467 
    468 Minimal Example Actors
    469 -----------------------
    470 
    471 Get a JSWindowActor
    472 ~~~~~~~~~~~~~~~~~~~~
    473 
    474 **Define an Actor**
    475 
    476 .. code-block:: javascript
    477 
    478  // resource://testing-common/TestWindowParent.sys.mjs
    479  export class TestParent extends JSWindowActorParent {
    480    ...
    481  }
    482 
    483 .. code-block:: javascript
    484 
    485  // resource://testing-common/TestWindowChild.sys.mjs
    486  export class TestChild extends JSWindowActorChild {
    487    ...
    488  }
    489 
    490 
    491 **Get a JS window actor for a specific window**
    492 
    493 .. code-block:: javascript
    494 
    495  // get parent side actor
    496  let parentActor = this.browser.browsingContext.currentWindowGlobal.getActor("TestWindow");
    497 
    498  // get child side actor
    499  let childActor = content.windowGlobalChild.getActor("TestWindow");
    500 
    501 Get a JSProcessActor
    502 ~~~~~~~~~~~~~~~~~~~~
    503 
    504 **Define an Actor**
    505 
    506 .. code-block:: javascript
    507 
    508  // resource://testing-common/TestProcessParent.sys.mjs
    509  export class TestParent extends JSProcessActorParent {
    510    ...
    511  }
    512 
    513 .. code-block:: javascript
    514 
    515  // resource://testing-common/TestProcessChild.sys.mjs
    516  export class TestChild extends JSProcessActorChild {
    517    ...
    518  }
    519 
    520 
    521 **Get a JS process actor for a specific process**
    522 
    523 .. code-block:: javascript
    524 
    525  // get parent side actor
    526  let parentActor = this.browser
    527    .browsingContext
    528    .currentWindowGlobal
    529    .domProcess
    530    .getActor("TestProcess");
    531 
    532  // get child side actor
    533  let childActor = ChromeUtils.domProcessChild
    534    .getActor("TestProcess");
    535 
    536 And more
    537 ===========
    538 
    539 
    540 .. _Electrolysis Project: https://wiki.mozilla.org/Electrolysis
    541 .. _IPC Actors: https://developer.mozilla.org/en-US/docs/Mozilla/IPDL/Tutorial
    542 .. _Context Menu Fission Port: https://hg.mozilla.org/mozilla-central/rev/adc60720b7b8
    543 .. _JSProcessActor.webidl: https://searchfox.org/mozilla-central/source/dom/chrome-webidl/JSProcessActor.webidl
    544 .. _JSWindowActor.webidl: https://searchfox.org/mozilla-central/source/dom/chrome-webidl/JSWindowActor.webidl
    545 .. _BrowserElementParent.sys.mjs: https://searchfox.org/mozilla-central/source/toolkit/actors/BrowserElementParent.sys.mjs
    546 .. _Transferable: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects