BroadcastChannel API in Firefox 38

Recently the BroadcastChannel API landed in Firefox 38. This API can be used for simple messaging between browser contexts that have the same user agent and origin. This API is exposed to both Windows and Workers and allows communication between iframes, browser tabs, and worker threads.

The intent of the BroadcastChannel API is to provide an API that facilitates communicating simple messages between browser contexts within a web app. For example, when a user logs into one page of an app it can update all the other contexts (e.g. tabs or separate windows) with the user’s information, or if a user uploads a photo to a browser window, the image can be passed around to other open pages within the app. This API is not intended to handle complex operations like shared resource locking or synchronizing multiple clients with a server. In those cases, a shared worker is more appropriate.

API Overview

A BroadcastChannel object can be created by using the following code:

var myChannel = new BroadcastChannel("channelname");

The channelname is case sensitive. The BroadcastChannel instance contains two methods, postMessage and close. The postMessage method is used for sending messages to the channel. The message contents can be any object, including complex objects such as images, arrays, and arrays of objects. When the message is posted, all browser contexts with the same origin, user agent, and a BroadcastChannel with the same channel name are notified of the message. For example, the following code posts a simple string:

myChannel.postMessage("Simple String");

The dispatched message can be handled by any page with the channel opened by using the onmessage event handler of the BroadcastChannel instance.

myChannel.onmessage = function(event) {
    console.log(event.data);
}

Note that the data attribute of the event contains the data of the message. Other attributes of the event may be of interest as well. For example, you can check the web app origin using the event.origin attribute or the channel name is available in the event.target.name attribute.

The close method is used to close the channel for the particular browser context and can be called by simply using:

broadCastChannel.close();

Blob Message Posting

While posting strings and other primitive types is fairly simple, the BroadcastChannel API supports more complex objects as well. For example, to post an Image from one context to another the following code could be used.

var broadcastExample = {};
broadcastExample.setupChannel = function() {
    if ("BroadcastChannel" in window) {
        if (typeof broadcastExample.channel === 'undefined' || 
           !broadcastExample.channel) {
            //create channel
            broadcastExample.channel = new BroadcastChannel("foo");
        }
    }
}
broadcastExample.pMessage = function() {
    broadcastExample.setupChannel();
    //get image element
    var img = document.getElementById("ffimg");
    //create canvas with image 
    var canvas = document.createElement("canvas");
    canvas.width = img.width;
    canvas.height = img.height;
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    //get blob of canvas
    canvas.toBlob(function(blob) {
        //broadcast blob
        broadcastExample.channel.postMessage(blob);
    });
}

The above code contains two functions and represents the sender side of a BroadcastChannel client. The setupChannel method just opens the Broadcast Channel and the pMessage method retrieves an image element from the page and converts it to a Blob. Lastly the Blob is posted to the BroadcastChannel. The receiver page will need to already be listening to the BroadcastChannel in order to receive the message and can be coded similar to the following.

var broadcastFrame = {};

//setup broadcast channel
broadcastFrame.setup = function() {
    if ("BroadcastChannel" in window) {
        if (typeof broadcastFrame.channel === 'undefined' || !broadcastFrame.channel) {
            broadcastFrame.channel = new BroadcastChannel("foo");
        }
        //function to process broadcast messages
        function func(e) {
            if (e.data instanceof Blob) {
                //if message is a blob create a new image element and add to page
                var blob = e.data;
                var newImg = document.createElement("img"),
                    url = URL.createObjectURL(blob);
                newImg.onload = function() {
                    // no longer need to read the blob so it's revoked
                    URL.revokeObjectURL(url);
                };
                newImg.src = url;
                var content = document.getElementById("content");
                content.innerHTML = "";
                content.appendChild(newImg);
            }
        };
        //set broadcast channel message handler
        broadcastFrame.channel.onmessage = func;
    }
}
window.onload = broadcastFrame.setup();

This code takes the blob from the broadcast message and creates an image element, which is added to the receiver page.

Workers and BroadcastChannel messages

The BroadcastChannel API also works with dedicated and shared workers. As a simple example, assume a worker is started and when a message is sent to the worker through the main script, the worker then broadcasts a message to the Broadcast Channel.

var broadcastExample = {};
broadcastExample.setupChannel = function() {
    if ("BroadcastChannel" in window) {
        if (typeof broadcastExample.channel === 'undefined' || 
           !broadcastExample.channel) {
            //create channel
            broadcastExample.channel = new BroadcastChannel("foo");
        }
    }
}

function callWorker() {
    broadcastExample.setupChannel();
    //verify compat
    if (!!window.Worker) {
        var myWorker = new Worker("worker.js");
        //ping worker to post to Broadcast Channel
        myWorker.postMessage("Broadcast from Worker");
    }
}


//Worker.js
onmessage = function(e) {
    //Broadcast Channel Message with Worker
    if ("BroadcastChannel" in this) {
        var workerChannel = new BroadcastChannel("foo");
        workerChannel.postMessage("BroadcastChannel Message Sent from Worker");
        workerChannel.close();
    }
}

The message can be handled in the main page in the same way as described in previous sections of this post.
workerbroadcast
Using a Worker to Broadcast a message to an iframe

More Information

In addition to demonstrating how to use in an iframe, another example of using the code in this post is available on github. More information about the BroadcastChannel API is available in the specification, bugzilla entry, and on MDN. The code and test pages for the API are located in the gecko source code.


8 comments

  1. Hubert SABLONNIERE

    This BroadcastChannel spec seems very interesting… I want to know more. Where does this come from? Who at the WHATWG edits it? What are the other broswer vendors opinions?

    I gave a talk about cross window/tab messaging and multi-screene experiences at Paris-Web and thanks to this article I’m already trying out the API.

    http://www.paris-web.fr/2014/conferences/les-recettes-du-web-multi-ecran.php

    February 3rd, 2015 at 01:05

  2. Jason Weathersby

    Take a look at: WHATWG FAQ for spec editor. Currently I believe only Mozilla has implemented this API. Thanks for posting the link to the presentation.

    February 4th, 2015 at 08:24

  3. Luke

    Sounds interesting, but what advantage does this have over ServiceWorker? From the draft spec: “service workers are started and kept alive by their relationship to events, not documents.”
    “Service workers may be started by user agents without an attached document and may be killed by the user agent at nearly any time” (https://slightlyoff.github.io/ServiceWorker/spec/service_worker/)

    “They will also allow access to push notifications and background sync APIs”
    https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API

    I’ve just started looking into the possibilities of ServiceWorker, and I see Chrome 40 already has this available. It’s a shame that Firefox doesn’t have it usable in standard Firefox yet, especially since if I understand correctly, this would actually be the only way to have push-notifications in FirefoxOS?

    February 4th, 2015 at 21:36

  4. Hubert SABLONNIERE

    Hi Luke,

    You said :
    “I see Chrome 40 already has this available. It’s a shame that Firefox doesn’t have it usable in standard Firefox yet, especially since if I understand correctly, this would actually be the only way to have push-notifications in FirefoxOS”.

    It’s actually not that simple… at all!

    First, Chrome has shipped some parts of the spec in 40 but it’s not 100% compliant. It’s a work in progress, like the spec. If you play with it, you’ll see that some details are still missing or still refer to previous API designs.

    Jake Archibald keeps a track of some parts of the implentations here :
    https://jakearchibald.github.io/isserviceworkerready/

    Regarding Firefox status and involvement on SW, I would say it’s good. Mozilla has been working on the spec like Google and others for a long time. Some parts are implemented in Nightly and it will land in future releases.

    Push notifications in SW are a separated spec and will after. It’s too soon to ask for a stable implementation.

    You asked :
    “but what advantage does this have over ServiceWorker”.

    Like I said in my first comment, I did a lot of experiments of cross tab/window communications in FF, Chrome, Opera, IE and Safari. I used postMessage() with SharedWorker, MessageChannel, iframes and popups. I also played with the “StorageEvent hack”.

    Let’s take the example of sending a message from a tab to some other specific tabs of your origin.

    With a SharedWorker, all tabs must open a communication with it and the SharedWorker script has to keep track of all connected tabs to “broadcast” the messages. If you don’t want some of the tabs to receive the message, you’ll have to implement some logic in the SharedWorker script or in all the tabs message listeners. It can be cumbersome.

    With a ServiceWorker, you don’t have to keep track of the connected tabs. You can iterate over the connected tabs in the ServiceWorker script and target the pages you want using their location (not yet in Chrome). But you’ll still have to implement a “middle-man” script to let your pages communicate. And it’s not that simple to debug…

    With these techniques, tabs receive messages with global window.onmessage and everything is mixed up. All messages coming from other contexts are mixed up. You can use one or more MessageChannel separate the different types or usages but again, support and ease of use are not great.

    The advantages I see with BroadcastChannel for the “one tab to some specific tabs messaging” are :
    * You don’t have to write a “middle-man” script to transmit the messages. Tabs can talk directly to other tabs. It’s easier to write and easier to debug.
    * You can have simple message listeners without “ifs hell” for separation because you just listen to a named channel.

    I see BroadcastChannel like message rooms or namespace in Socket.IO. I used them for a multi-user purpose it was a great metaphor.

    This all thing is not easy to explain in a post answer :-(
    Ping me if you want to know more.

    StorageEvent for cross tab communication :
    http://truongtx.me/2014/06/16/cross-tab-communication-using-html5-dom-storage/

    February 5th, 2015 at 08:32

  5. Nick Desaulniers

    This API is important because you no longer have to maintain a reference to iframes or workers that you with to communicate with. They can simply “subscribe” to particular channels by constructing a BroadcastChannel, and have full-duplex (bi-directional) communication (“publish”). Theoretically, you even use this in the same JS context to implement message passing between objects that have their own instances of BroadcastChannel subscribed to the same channel. This is useful since implementing EventEmitter interfaces from Node.js in the browser isn’t the most straight forward thing to do, and only DOM elements can be EventTargets. Not that I’ve ever implemented them before, but I don’t see why this couldn’t lead to an implementation of Actors in JavaScript.

    February 5th, 2015 at 12:27

  6. anon

    Does this refer to the following specification, but with a renamed/”vendor prefixed” BOM object?
    http://www.w3.org/TR/webmessaging/#channel-messaging

    February 5th, 2015 at 13:50

    1. jperrier@mozilla.com

      No this is something different. The Broadcast Channel API is defined in the HTML Specification: https://html.spec.whatwg.org/multipage/comms.html#broadcasting-to-other-browsing-contexts

      February 6th, 2015 at 01:09

  7. Jason Weathersby

    Message channels should be coming very soon, see this bug and is using this spec.

    February 6th, 2015 at 05:49

Comments are closed for this article.