The message manager

  • Revision slug: The_message_manager
  • Revision title: The message manager
  • Revision id: 57763
  • Created:
  • Creator: Johnjbarton
  • Is current revision? No
  • Comment 356 words added, 246 words removed

Revision Content

{{ gecko_minversion_header("2.0") }}

In order to support different parts of web content being handled in separate threads (such as out-of-process plugins), Gecko now features API that allows the browser to communicate with content processes by loading script files that add listeners to the content process's root event handling scope; those event listeners can then send messages back to the chrome process.

Note: Currently, this API only supports JavaScript.

The chrome process

Firefox and other XUL applications have a main or controlling process called the Chrome Process (need link).  The Chrome Process has an nsIDOMWindow known as the "chrome window" (? The XULWindow?) and (a collection? array?) of {{ interface("nsIFrameLoader") }} frame loaders (where?).  

The chrome window and each {{ interface("nsIFrameLoader") }} object now have a messageManager attribute, which provides an {{ interface("nsIChromeFrameMessageManager") }} interface; these implement sendAsyncMessage() and loadFrameScript() to respectively send messages to and execute scripts in a content process.

To connect to a content process, code running in the Chrome process uses loadFrameScript() to inject a script into the content process. That script then can add listeners into the content process and send result messages back to the chrome process.

When a message is received from a content process, listeners in the {{ interface("nsIFrameLoader") }} object's messageManager(?) are called first, followed by the ones in the window's message manager.

Listening to messages coming from content

Calling {{ ifmethod("nsIFrameMessageManager", "addMessageListener") }} adds a listener to receive messages coming from content. Similarly, calling {{ ifmethod("nsIFrameMessageManager", "removeMessageListener") }} removes listeners.

Sending messages to content

The {{ ifmethod("nsIChromeFrameMessageManager", "sendAsyncMessage") }} method sends an asynchronous message to content.

The {{ ifmethod("nsIChromeFrameMessageManager", "loadFrameScript") }} method instructs the content process' root scope to execute a given script.

The content process

Scripts loaded into a content process via loadFrameScript() add listeners to the content window and send messages back to the Chrome process. These scripts run in a scope that implements the {{ interface("nsIContentFrameMessageManager") }} and {{ interface("nsIDOMEventTarget") }} interfaces. The event listeners added through these interfaces enclose all event listeners in the content process. This means that capturing event listeners added through these interfaces will be called first. Event listeners added to the bubble phase will be called after listeners in the content page have had a chance to handle the event.

You can access the current top level window using the content attribute in the {{ interface("nsIContentFrameMessageManager") }}, and that interface's docShell attribute references the top level window's {{ interface("nsIDocShell") }} object.

The content process also supports the methods from {{ interface("nsIDOMEventTarget") }}, as well as the methods added by {{ interface("nsIContentFrameMessageManager") }}.

Listening to messages coming from chrome

Calling {{ ifmethod("nsIFrameMessageManager", "addMessageListener") }} adds a listener to receive messages coming from chrome. Similarly, calling {{ ifmethod("nsIFrameMessageManager", "removeMessageListener") }} removes listeners.

Sending messages to chrome

Call the {{ ifmethod("nsIContentFrameMessageManager", "sendSyncMessage") }} method to send a synchronous message to chrome, or {{ ifmethod("nsIContentFrameMessageManager", "sendAsyncMessage") }} for asynchronous message sending.

A simple example

This simple example forwards all clicks on HTML {{ HTMLElement("a") }} elements to chrome. This is a simplified example, and won't work for child elements, but it should help understand how the messaging system works.

The content process's code

This code runs in the content process to set up an event listener that forwards the click events to the chrome process.

addEventListener("click",
  function(e) {
    if (e.target instanceof Components.interfaces.nsIDOMHTMLAnchorElement &&
        sendSyncMessage("linkclick", { href : e.target.href })[0].cancel) {
      e.preventDefault();
    }
  },
  false);

This code runs in chrome to receive the click events.

messageManager.loadFrameScript("path://to/remote.js", true);
messageManager.addMessageListener("linkclick",
  function(m) {
    return { cancel: !confirm("Do you want to load " + m.json.href) };
  }
);

See also

Revision Source

<p>{{ gecko_minversion_header("2.0") }}</p>
<p>In order to support different parts of web content being handled in separate threads (such as <a href="/en/Plugins/Out_of_process_plugins" title="en/Plugins/Out of process plugins">out-of-process plugins</a>), Gecko now features API that allows the browser to communicate with content processes by loading script files that add listeners to the content process<strike>'s root event handling scope</strike>; those event listeners can then send messages back to the chrome process.</p>
<div class="note"><strong>Note:</strong> Currently, this API only supports JavaScript.</div>

<h2>The chrome process</h2>
<p>Firefox and other XUL applications have a main or controlling process called the Chrome Process (need link).  The Chrome Process has an nsIDOMWindow known as the "chrome window" (? The XULWindow?) and (a collection? array?) of {{ interface("nsIFrameLoader") }} frame loaders (where?).  </p>
<p>The chrome window and each {{ interface("nsIFrameLoader") }} object now have a <code>messageManager</code> attribute, which provides an {{ interface("nsIChromeFrameMessageManager") }} interface; these implement <code>sendAsyncMessage()</code> and <code>loadFrameScript()</code> to respectively send messages to and execute scripts in a content process.</p>
<p>To connect to a content process, code running in the Chrome process uses <code>loadFrameScript()</code> to inject a script into the content process. That script then can add listeners into the content process and send result messages back to the chrome process. </p>
<p>When a message is received from a content process, listeners in the {{ interface("nsIFrameLoader") }} object's <code>messageManager</code>(?) are called first, followed by the ones in the window's message manager.</p>
<h3>Listening to messages coming from content</h3>
<p>Calling {{ ifmethod("nsIFrameMessageManager", "addMessageListener") }} adds a listener to receive messages coming from content. Similarly, calling {{ ifmethod("nsIFrameMessageManager", "removeMessageListener") }} removes listeners.</p>
<h3>Sending messages to content</h3>
<p>The {{ ifmethod("nsIChromeFrameMessageManager", "sendAsyncMessage") }} method sends an asynchronous message to content.</p>
<p>The {{ ifmethod("nsIChromeFrameMessageManager", "loadFrameScript") }} method instructs the content process' root scope to execute a given script.</p>
<h2>The content process</h2>
<p>Scripts loaded into a content process via <code>loadFrameScript()</code> add listeners to the content window and send messages back to the Chrome process. These scripts run in a scope that implements the {{ interface("nsIContentFrameMessageManager") }} and {{ interface("nsIDOMEventTarget") }} interfaces. The event listeners added through these interfaces enclose all event listeners in the content process. This means that capturing event listeners added through these interfaces will be called first. Event listeners added to the bubble phase will be called after listeners in the content page have had a chance to handle the event.</p>
<p>You can access the current top level window using the <code>content</code> attribute in the {{ interface("nsIContentFrameMessageManager") }}, and that interface's <code>docShell</code> attribute references the top level window's {{ interface("nsIDocShell") }} object.</p>
<p>The content process also supports the methods from {{ interface("nsIDOMEventTarget") }}, as well as the methods added by {{ interface("nsIContentFrameMessageManager") }}.</p>
<h3>Listening to messages coming from chrome</h3>
<p>Calling {{ ifmethod("nsIFrameMessageManager", "addMessageListener") }} adds a listener to receive messages coming from chrome. Similarly, calling {{ ifmethod("nsIFrameMessageManager", "removeMessageListener") }} removes listeners.</p>
<h3>Sending messages to chrome</h3>
<p>Call the {{ ifmethod("nsIContentFrameMessageManager", "sendSyncMessage") }} method to send a synchronous message to chrome, or {{ ifmethod("nsIContentFrameMessageManager", "sendAsyncMessage") }} for asynchronous message sending.</p>
<h2>A simple example</h2>
<p>This simple example forwards all clicks on HTML {{ HTMLElement("a") }} elements to chrome. This is a simplified example, and won't work for child elements, but it should help understand how the messaging system works.</p>
<h3>The content process's code</h3>
<p>This code runs in the content process to set up an event listener that forwards the click events to the chrome process.</p>
<pre class="brush: js">addEventListener("click",
  function(e) {
    if (e.target instanceof Components.interfaces.nsIDOMHTMLAnchorElement &amp;&amp;
        sendSyncMessage("linkclick", { href : e.target.href })[0].cancel) {
      e.preventDefault();
    }
  },
  false);
</pre>
<p>This code runs in chrome to receive the click events.</p>
<pre class="brush: js">messageManager.loadFrameScript("path://to/remote.js", true);
messageManager.addMessageListener("linkclick",
  function(m) {
    return { cancel: !confirm("Do you want to load " + m.json.href) };
  }
);
</pre>
<h2>See also</h2>
<ul> <li><a class=" link-https" href="https://wiki.mozilla.org/Content_Process_Event_Handlers" title="https://wiki.mozilla.org/Content_Process_Event_Handlers">Content Process Event Handlers</a> (design document)</li>
</ul>
Revert to this revision