Message manager overview

In the initial version of multiprocess Firefox there are two processes:

  • the chrome process, also called the parent process, runs the browser UI (chrome) code and code inserted by extensions
  • the content processes, also called the child processes, run all web content. Different tabs may run in different processes.

Message managers are designed to enable code in one process to communicate with code in a different process. At the top level, there are two different sorts of message managers:

  • Frame message managers: these enable chrome process code to load a script into a browser frame (essentially, a single browser tab) in the content process. These scripts are called frame scripts, and as the name suggests, they are scoped to a specific browser frame. If chrome code wants to run code in the content process so it can access web content, this is usually the sort of message manager to use.
  • Process message managers: these correspond to process boundaries, and enable code running in the parent (chrome) process to communicate with code running in the child (content) process. From Firefox 38 onwards, they also enable code running in the parent process to load process scripts into the child process. These are like frame scripts, except they are global to the child process. Process scripts are most likely to be useful when chrome code wants to run some code only once in the content process, to access some global service: for example, to register an observer or a content policy.

Frame message managers

In multiprocess Firefox, when chrome code needs to interact with web content, it needs to:

  • factor the code that needs direct access to content into separate scripts, which are called "frame scripts"
  • use a frame message manager to load these frame scripts into the content process
  • use the frame message manager API to communicate with the frame script

Some older articles on multiprocess Firefox and the message manager might refer to "content scripts" instead of "frame scripts", but this usage is deprecated because the Add-on SDK uses "content script" to refer to a similar but different kind of script.

There are three different subtypes of frame message manager: the global message manager, the window message manager, and the browser message manager. All three enable chrome code to:

  • load a script into a frame in the content the content process using a loadFrameScript() function. These scripts are called "frame scripts".
  • communicate with frame scripts using addMessageListener() and broadcastAsyncMessage() or sendAsyncMessage() functions.

Note that in this context, "browser" refers to the XUL <browser> object, which is a frame that hosts a single Web document. It does not refer to the more general sense of a Web browser.

Global message manager

The global message manager operates on every <browser> (that is, every open content tab).

  • loadFrameScript() loads the given script into every <browser> in every browser window.
  • broadcastAsyncMessage() sends the message to every <browser> in every browser window.

It's a ChromeMessageBroadcaster object, which implements the following interfaces:

You can access the global message manager like this:

// chrome script
let globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
  .getService(Ci.nsIMessageListenerManager);

Window message manager

The window message manager is associated with a specific browser window, and operates on every <browser> (that is, every content tab) loaded into that window:

  • loadFrameScript() loads the given script into every <browser> in that browser window
  • broadcastAsyncMessage() sends the message to every <browser> in that browser window.

It's a ChromeMessageBroadcaster object, which implements the following interfaces:

The window message manager can be accessed as a property of the browser window:

// chrome script
let windowMM = window.messageManager;

Browser message manager

The browser message manager is specific to a single XUL <browser> element (which essentially corresponds to a single tab):

  • loadFrameScript() loads the given script only into its <browser>
  • sendAsyncMessage() sends the message only to that <browser>.

You can mix and match: so for example, you could load a script into every <browser> using the global message manager, but then send a message to the script instance loaded into a specific <browser> using the browser message manager.

It's a ChromeMessageSender object, which implements the following interfaces:

The browser message manager can be accessed as a property of the XUL <browser> element:

// chrome script
let browserMM = gBrowser.selectedBrowser.messageManager;

Process message managers

Process message managers correspond to process boundaries, and enable code running in different processes to communicate. Multiprocess Firefox has the concept of:

  • a "parent process"
  • "child processes" which are processes spawned by the parent process.

For practical purposes, in multiprocess Firefox the parent process is the chrome process, and child processes are content processes.

In each child process, there's a single child process message manager (CPMM). For each child process message manager, there's a parent process message manager (PPMM) in the parent process. There's also a single global parent process message manager (GPPMM) in the parent process, that provides access to all the parent process message managers:

With the GPPMM, you can broadcast messages to all CPMMs. With a PPMM, you can send a message to its corresponding CPMM. With a CPMM, you can send messages to the parent process: these messages are received first by the corresponding PPMM, then by the GPPMM.

From Firefox 38 onwards, you can also use a parent process message manager to load a script into a child process. This is the recommended way to load a script that executes just once per child process, which is something you might want to do if you are interacting with some global service (for example, adding listeners to observer notifications or registering components).

Accessing process message managers

You can access the global parent process manager with code like this:

// parent process
let parentProcessMessageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                                  .getService(Ci.nsIMessageBroadcaster);

You can also access it as the ppmm property of Services.jsm.

// child process script
let childProcessMessageManager = Cc["@mozilla.org/childprocessmessagemanager;1"]
                                 .getService(Ci.nsISyncMessageSender);

Parent process message manager

The parent process message manager lives in the

implements

Child process message manager

implements

Loading scripts per child process

Since the process message managers do not support script loading the only way to implement per-child process code is to use a frame script which imports a custom javascript module (JSM). Since javascript modules are singletons they will only be executed once per process even when they are loaded in multiple frame scripts.
The JSM can then use the child process message manager to exchange messages with the parent process message manager.

This can be useful for addons to register categories, observers and components in child processes.