Overview
This page lists patterns that add-on authors might be using that won't work, or will work differently, in multiprocess Firefox.
It's split into two sections: chrome process and content process.
Chrome process
The first section, chrome process, covers things that used to work in the chrome process that will no longer work in multiprocess Firefox. These are the sorts of things that will break an old add-on in multiprocess Firefox. The fix is generally some variant of "do that in a frame script loaded into the content process".
Compatibility shims
For many of the patterns described here we've implemented compatibility shims so the patterns still work. For example: whenever extensions try to access web content from the chrome process, the browser will return a Cross Process Object Wrapper that gives the chrome code synchronous access to the content.
You'll get the shims for your add-on by default, unless you set the multiprocessCompatible
flag in your add-on's install manifest.
However, these shims are not a substitute for migrating extensions:
- they are only a temporary measure, and will be removed eventually
- they can have a bad effect on responsiveness
- there are likely to be edge cases in which they don't work properly
For each pattern we've noted:
- whether a shim exists and what kind of behavior it provides
- how to update your add-on so you don't need the shim
Content process
However, some things that used to work in a chrome process will not work in the content process. The second section, content process, lists these sorts of things along with their mitigation.
Chrome process pitfalls
gBrowser.contentWindow, window.content...
Without the shim
All APIs in the chrome process that provide direct access to content objects will no longer work. For example:
// chrome code gBrowser.contentWindow; // null gBrowser.contentDocument; // null gBrowser.selectedBrowser.contentWindow; // null window.content; // null content; // null
With the shim
The shim will give you a CPOW for the content object in these situations.
To make the shim unnecessary: factor the code that needs to access content into a separate script, load that script into the content process as a frame script, and communicate between the chrome script and the frame script using the message-passing APIs. See the article on using the message manager.
nsIContentPolicy
Without the shim
Under multiprocess Firefox you can't use nsIContentPolicy
in the chrome process, because it needs to touch web content.
With the shim
The shim enables you to add content policies in the chrome process. It transparently registers an nsIContentPolicy
in the content process, whose shouldLoad
just forwards to the chrome process. The content to check is forwarded as a CPOW. The chrome process then checks the content against the policy supplied by the add-on, and forwards the response back to the child to be enforced.
To make the shim unnecessary: define and register nsIContentPolicy
inside a frame script.
Observers in the chrome process
Depending on the topic, you need to register observers in either the chrome process or in a frame script.
For most topics you need to register observers in the chrome process.
However, you must listen to content-document-global-created
and document-element-inserted
in a frame script. Observers for these topics get content objects as the aSubject
argument to observe()
, so notifications are not sent to the chrome process.
There is a shim that will forward these two topics to the chrome process, sending CPOWs as the aSubject
argument.
HTTP requests
You can't observe HTTP requests in the content process. If you do, you'll get an error.
If you do so in the chrome process, it will mostly work. The subject for the observer notification will be an nsIHttpChannel
as you would expect. A common pattern here is to use the notificationCallbacks
property of the nsIHttpChannel
to get the DOM window that initiated the load, like this:
observe: function (subject, topic, data) { if (topic == "http-on-modify-request") { var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel); var domWindow = httpChannel.notificationCallbacks.getInterface(Ci.nsIDOMWindow); } }
Or this:
observe: function (subject, topic, data) { if (topic == "http-on-modify-request") { var httpChannel = subject.QueryInterface(Ci.nsIHttpChannel); var domWindow = httpChannel.notificationCallbacks.getInterface(Ci.nsILoadContext).associatedWindow; } }
In multiprocess Firefox these patterns will no longer work: the getInterface
call will fail.
In multiprocess Firefox, notificationCallbacks
is a special object that tries to emulate the single-process notificationsCallbacks
object as best it can. It will return a dummy nsILoadContext
when asked, but any attempt to get a window out of it will fail.
There is an outstanding bug (bug 1108827) to implement a shim here, that will make notificationCallbacks
a CPOW for the objects in the content process.
DOM Events
Without the shim
In multiprocess Firefox, if you want to register an event listener on some content DOM node, that needs to happen in the content process.
It used to be that if you registered a listener on the XUL <browser>
or <tab>
element that hosted some DOM content, then events in the content would bubble up to the XUL and you could handle them there. This no longer happens in multiprocess Firefox.
With the shim
The shim intercepts chrome process code that adds listeners to XUL elements and sets up listeners in the content process, relaying the result back to the chrome process. The Event
object itself is relayed to the chrome process as a CPOW.
To make the shim unnecessary: register event listeners on the global object inside a frame script. For example:
addEventListener("load", handler, true) // for example
Content process pitfalls
Many privileged APIs will just work in a content process. Anything that just manipulates data structures will just work. XHR and Workers will work.
File I/O
You should not write to or read from the disk from a frame script, in particular the profile directory. Even if this is possible, you should not do it and may expect that it could stop working at any time. File I/O should all be done in the chrome process. For example:
XUL and browser UI
Anything that tries to touch the browser UI or anything to do with XUL is likely to not work in the content process. For example:
nsIPromptService
nsIFilePicker
nsIXUL*
- <need more examples>
Chrome windows
Anything that needs to use chrome windows will not work in the content process. For example:
nsISessionStore
nsIWindowMediator
- <need more examples>
Places API
The Places API can't be used inside a frame script. For example:
Observers in the content process
As noted in Observers in the chrome process, most observers should be registered in the chrome process and will not work in the content process. The exceptions are content-document-global-created
and document-element-inserted
, which must be registered in a frame script.
QI from content window to chrome window
window.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebNavigation) .QueryInterface(Ci.nsIDocShellTreeItem) .rootTreeItem .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow);
nsITabChild
, that cannot be converted to an nsIDOMWindow
, so the second getInterface
call here will fail.If you want a chrome window: send a message from the content process using the message manager. The target
property of the object passed into the message handler in the chrome process is the XUL <browser>
receiving the message, and you can get the chrome window from that (Note: I'm not really sure how...).
nsIAboutModule
By default, custom about:
pages registered using nsIAboutModule
are loaded in the chrome process. This means that you can't access their content from the content process (via XHR, for example).
There is a shim for this, that makes the content of about:
pages registered by the add-on transparently available in the content process.
To avoid the shim: if you need to access the content of your about page from the content process, you need to register the nsIAboutModule
in the content process as well as the chrome process. By default, about:
pages (except for a small whitelist) are loaded in the chrome process when browsed to from the AwesomeBar.