Compatibility with Legacy Addons

Most add-ons will require changes in order to work optimally with multiprocess Firefox. Working with multiprocess Firefox gives an overview of the sorts of changes required for typical add-ons. However, to ease the transition to multiple processes, Firefox will also provide an "emulation layer" that will allow legacy add-ons to work mostly unaffected (although more slowly). This page describes the emulation layer.

install.rdf directive

The emulation layer will be enabled by default for all add-ons. Developers who want their add-on to work most efficiently with multiprocess Firefox should disable emulation using an install.rdf directive, as in this example:

    <em:type>2</em:type>
    <em:bootstrap>true</em:bootstrap>
    <em:multiprocessCompatible>true</em:multiprocessCompatible>
    <em:targetApplication>

This directive can be used for any Firefox add-on.

Cross-process object wrappers (CPOWs)

Here is a simple example of some add-on code that requires emulation to be compatible with electrolysis.

content.document.documentElement.style = "border: 1px solid red;";

Add-ons in multiprocess Firefox run in the main process, so they don't naturally have access to content documents. In order to make this code work, Firefox generates wrapper objects in the main process that look exactly like content objects. Each time an add-on accesses a property on one of these wrappers, a message is sent synchronously to the content process to get or set the value of the property. If the result of the property access is another content-process object, then Firefox generates a new wrapper for it (if one doesn't already exist). In the example above, wrappers will be generated for:

  • content: the Window object in the content process
  • content.document: the content document
  • content.document.documentElement: the root DOM element of the content tab

Primitive JavaScript values like strings and integers do not have wrappers.

The use of cross-process wrappers (CPOWs) generates a lot of communications traffic between the main process and the content process. We refer to this as inter-process communication (IPC) traffic. Let's consider the set of IPC messages that must be sent for the code above:

  • We'll assume that Firefox already has a wrapper available for content. The wrapper has an ID; let's assume it's 0.
  • Accessing the document property on the content wrapper sends a synchronous message to the content process. The result is a document object that the main process must wrap. Let's assume this wrapper is given ID 1. The IPC message looks something like "get property document of object ID 0". The result is simply "object with ID 1". When the main process sees this result, it creates a new wrapper for object ID 1, since it hasn't heard about it before.
  • Accessing the documentElement property on CPOW ID 1 sends another message. In a similar fashion, it returns a new CPOW, with ID 2, that wraps the root DOM element.
  • Setting the style property on CPOW ID 2 sends another message. The message looks like "set the value of the style property of CPOW ID 2 to 'border: 1px solid red'". Primitive values like strings and integers are not wrapped with CPOWs like objects are, so we send the value of the string in the IPC message.

Sending messages between processes is fairly expensive, and this simple fragment of code generated 4 messages. It should be clear that emulating single-process Firefox inside multiprocess Firefox is not cheap.

Bidirectional CPOWs

In the example above, all of the wrappers lived in the main process. However, it's possible to get CPOWs going in the other direction. What if, instead, we had done as follows?

content.document.onload = function() { ... };

The first message, to get the document, would be the same. The next message would have the form "set the onload property of CPOW ID 1 to ...". In this case, a property on an object in the content process is being set to a function in the main process. Unlike strings and integers, we can't send functions over the IPC channel. Instead, we make a CPOW that goes in the other direction: the wrapper lives in the content process and refers to an object in the main process. To distinguish these different kinds of wrappers, we call them "up CPOWs" and "down CPOWs". A down CPOW points down, from the main process to the content process. An up CPOW points up, from the content process to the main process. We also need to give distinct IDs to these objects. An object that lives in the main process will be given an idea like M1 and M2. An object in the content process will get an ID like C1 or C2. Let's redo the example with that in mind:

  • We'll assume that Firefox already has a wrapper available for content with ID C0.
  • Accessing the document property on the content wrapper sends a synchronous message to the content process. The IPC message looks something like "get property document of C0". The result is simply "C1". When the main process sees this result, it creates a new wrapper for C1 since it hasn't heard about it before.
  • Setting the onload property on C1 sends another message. The message looks like "set the value of the onload property of C1 to M0".

Note that the content process knows nothing about object M0. However, when the document has finished loading, it can send a message to the parent asking it to call M0.

CPOWs and lifetimes

Ideally, a cross-process wrapper would act just like any other object reference: as long as the wrapper stays alive, the object in the other process that it refers to should stay alive. This sort of reference is called a "strong" reference. Unfortunately, implementing strong references in both the up and down directions is very difficult: it's possible for cycles to exist between the main process and the content process, and neither process has enough information to garbage collect those cycles.

Nevertheless, it's very useful to have references in both the up and down directions. As a compromise, down CPOWs (the kind that point to content objects) are only "weak" references. That means that the main process cannot keep a content object alive any longer than it would live if the CPOW never existed. If the content object does get garbage collected, then any attempt by the main process to use the CPOW will throw an exception.

Most of the time, CPOWs point to DOM objects, which are kept alive by nature of being in the document. As long as an add-on uses content objects immediately upon obtaining them, there is no danger that they might be collected. However, if an add-on stashes away a DOM object somewhere and uses it later, there's the possibility that the element has been removed from the document and was garbage collected. In that case, the access will throw an exception.

Please note that up CPOWs (which point to objects in the main process) are strong references. There's no danger they will disappear.

API emulation

CPOWs are only one aspect of the emulation layer that multiprocess Firefox uses to look like single-process Firefox. To see why CPOWs aren't enough, consider this add-on code:

Services.obs.addObserver("content-document-global-created", function() { ... });

This observer is supposed to run whenever a new content document is created. It won't work with multiple processes because the observer will fire in the content process, where the document is created; the add-on only registered the observer in the main process. For efficiency, Firefox doesn't normally propagate observer notifications between processes.

However, if an add-on doesn't specify the multiprocessCompatible flag in install.rdf, then any observers it adds will be registered in the content process. Whenever one of these observers is notified, a synchronous message is sent to the main process notifying it. The observer registered by the add-on runs in the parent.

Document Tags and Contributors

 Contributors to this page: billmccloskey
 Last updated by: billmccloskey,