port

We are planning to deprecate the use by Firefox add-ons of the techniques described in this document.

Don't use these techniques to develop new add-ons. Use WebExtensions instead.

If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions instead.

Add-ons developed using these techniques might not work with multiprocess Firefox (e10s), which is already the default in Firefox Nightly and Firefox Developer Edition, and will soon be the default in Beta and Release versions of Firefox. We have documentation on making your add-ons multiprocess-compatible, but it will be more future-proof for you to migrate to WebExtensions.

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

This article documents the port object, which is used to communicate between a content script and the main add-on code.

The port object provides message sending and receiving API enabling conversations between a content script and the main add-on code.

Each end of the conversation has access to a port: the content script via the global self property, and the main add-on code via a worker object associated with the SDK module you've used to attach the content script, such as page-mod or page-worker.

For an overview of content scripts, see the main article.

port

Methods

emit()

The port.emit() function sends a message from one side to the other.

It may be called with any number of parameters, but is most likely to be called with a name for the message and an optional payload. The payload can be any value that is serializable to JSON.

From the content script to the main add-on code:

// content-script.js

var myMessagePayload = "some data";
self.port.emit("myMessage", myMessagePayload);

From the main add-on code to the content script:

// main.js

var myMessagePayload = "some data";
worker.port.emit("myMessage", myMessagePayload);

on()

The port.on() function registers a function as a listener for a particular message sent from the other side using port.emit().

It takes two parameters: the name of the message and a function to handle it.

In a content script, to listen for "myMessage" sent from the main add-on code:

// content-script.js

self.port.on("myMessage", function handleMyMessage(myMessagePayload) {
  // Handle the message
});

In the main add-on code, to listen for "myMessage" sent from a a content script:

// main.js

worker.port.on("myMessage", function handleMyMessage(myMessagePayload) {
  // Handle the message
});

once()

Often you'll want to receive a message just once, then stop listening. The port object offers a shortcut to do this: the once() method.

This example rewrites the content script in the port.removeListener() example so that it uses once():

// content-script.js

function getFirstParagraph() {
  var paras = document.getElementsByTagName('p');
  console.log(paras[0].textContent);
}

self.port.once("get-first-para", getFirstParagraph);

removeListener()

You can use port.on() to listen for messages. To stop listening for a particular message, use port.removeListener(). This takes the same two parameters as port.on(): the name of the message, and the name of the listener function.

For example, here's an add-on that creates a page-worker and a button. The page-worker loads http://en.wikipedia.org/wiki/Chalk alongside a content script. The button sends the content script a message called "get-first-para" when it is clicked:

// main.js

pageWorker = require("sdk/page-worker").Page({
  contentScriptFile: require("sdk/self").data.url("listener.js"),
  contentURL: "http://en.wikipedia.org/wiki/Chalk"
});

require("sdk/ui/button/action").ActionButton({
  id: "get-first-para",
  label: "get-first-para",
  icon: "./icon-16.png",
  onClick: function() {
    console.log("sending 'get-first-para'");
    pageWorker.port.emit("get-first-para");
  }
});

The content script listens for "get-first-para". When it receives this message, the script logs the first paragraph of the document and then calls removeListener() to stop listening.

// content-script.js

function getFirstParagraph() {
  var paras = document.getElementsByTagName('p');
  console.log(paras[0].textContent);
  self.port.removeListener("get-first-para", getFirstParagraph);
}

self.port.on("get-first-para", getFirstParagraph);

The result is that the paragraph is only logged the first time the button is clicked.

Due to bug 816272 the page-mod's removeListener() function does not prevent the listener from receiving messages that are already queued. This means that if "main.js" sends the message twice in successive lines, and the listener stops listening as soon as it receives the first message, then the listener will still receive the second message.

JSON-serializable values

The payload for a message can be any JSON-serializable value. When messages are sent their payloads are automatically serialized, and when messages are received their payloads are automatically deserialized, so you don't need to worry about serialization.

However, you do have to ensure that the payload can be serialized to JSON. This means that it needs to be a string, number, boolean, null, array of JSON-serializable values, or an object whose property values are themselves JSON-serializable. This means you can't send functions, and if the object contains methods they won't be encoded. You also can't include circular references.

For example, to include an array of strings in the payload:

// main.js

var pageMods = require("sdk/page-mod");
var self = require("sdk/self");

var pageMod = pageMods.PageMod({
  include: ['*'],
  contentScriptFile: self.data.url("content-script.js"),
  onAttach: setupListener
});

function setupListener(worker) {
  worker.port.on('loaded', function(pageInfo) {
    console.log(pageInfo[0]);
    console.log(pageInfo[1]);
  });
}
//content-script.js

self.port.emit('loaded', [
                          document.location.toString(),
                          document.title
                          ]);

Document Tags and Contributors

 Contributors to this page: wbamberg, stoyanster, The_8472
 Last updated by: wbamberg,