Cross Frame Messaging with postal.xframe

On February 26, 2013, in JavaScript, by Jim Cowart

Writing open source software provides some great moments of being the giddy kid in the candy store. Of course, too much candy is never a good idea, so hopefully I’m not over-doing it with chocolate-covered cross-frame messaging.

Wait – chocolate-covered what?!

Cross-frame messaging.  “But why would I need to do this?” What – cover something in *chocolate*? Are you serious? Ohhh, you mean “why would I need to use cross-frame messaging?” Fantastic question. Perhaps you want your site to be “mashable” (usable in other sites) or are otherwise writing “widgets” that will be embedded – via iframe – into another site.  Maybe you want to move some CPU-bound problems to a hidden iframe – or a web worker – to prevent it from bogging down your main window as users interact with it. Cross-frame messaging comes into play when you need to communicate between the windows (and/or workers). The good news is, the tools are available (in most cases) to make this happen. The bad news is these tools are of the “lowest common denominator” variety – so generalized that you almost always need a higher level abstraction to prevent infrastructure concerns from running rampant in your code. I can’t promise chocolate (that’s up to you). However, I can hopefully provide some ‘sugar’ for a situation that quickly turns bitter.

The Window Over There

Cross-frame messaging is an interesting beast to tame. Most modern browsers support postMessage, which allows you to send messages between windows. Here’s a basic snippet of how it works:

// Somewhere in our iframe, for example:
window.addEventListener("message", function(e) {
    if (e.origin !== "") {
    // yeah, cause in the real world we just want
    // to console.log messages. AMIRITE?!
    console.log(JSON.stringify(, null, 4));
    // Curious about the extra stringify args?
    // See
}, false);

// Somewhere in our parent window, for example
var target = document.getElementById("zeeiframe");
    food           : "bacon",
    description : "Yummy"
}, "");

/* iframe would print:
    "food": "bacon",
    "description": "Yummy"

MDN is a great resource for further information on postMessage if you want to read more.  You can see from the above example that we listen for messages via “addEventListener” (or “attachEvent” if you’re in IE 8). You can infer from the “message” event callback that it receives an argument that contains at least an “origin” and “data” property (oh, it has much more). Assuming the sender was allowed to send a message to you, you get to handle checking from that point on, usually by verifying that you actually do want to process a message from the message’s origin.  When sending a message, you provide the message data and an origin (I advise against using the infamous “*” origin option in most circumstances). If you’ve had to work in this space before, this is all old news, of course. The DOM gods smiled upon us and provided a ‘pipeline’ between windows, right?

Really, though, it’s the rawest of pipelines. Every message comes across as a “message” event, and if multiple components are utilizing postMessage, your handlers will need to filter out and ignore irrelevant messages. The above example just accounts for filtering by origin. Beyond that, it’s up to you to add additional metadata to the payload by which to filter messages. So – with even a moderate level of complexity, the need for some sort of abstraction on top of postMessage quickly becomes apparent – something to help unpack and route messages. Not only that, but if you want it to work in IE 8 or 9, you’ll have to eagerly serialize your payloads to JSON and parse them on the receiving side, since they only support sending strings as the message payload. Of course, you don’t have to wrap postMessage in an API – you could bake all that infrastructure code right into your application logic (really, please don’t). Wouldn’t it be nice if the components communicating could operate completely ignorant of the barrier? What if they could do so using an API you might already be using to de-couple your components? This is actually a goal I had in mind early on when I wrote postal.js.

Over the last few months I’ve tried solving this problem several different ways. After a couple of failed attempts, and a couple of mediocre successes – all of which I tossed – I believe I’ve finally settled on an approach that I may not want to burn to the ground next week and dance like an elf in the cinders and ash. The first step in this approach was postal.federation.

Step One: postal.federation

The postal.federation add-on to postal.js doesn’t do anything on its own, other than add the core functionality necessary to federate postal.js instances. This “core” functionality includes top level API calls, and the ability to whitelist or blacklist both inbound and outbound messages, but without a “transport” plugin, there’s no “wire” over which to communicate with remote postal instances. The goal is for postal.federation to carry the concerns that would be common to any transport plugin, minus the actual implementation details on how to communicate across that transport.

Step Two: postal.xframe

The first transport plugin (at least, the first one that works with this architecture) is postal.xframe. postal.xframe provides the behavior necessary for postal instances in a window to communicate with postal instances in another accessible window or in a dedicated web worker. In effect, it wraps the postMessage call (on both ends), using it as a bridge to ship selected postal envelopes to and from remote windows/workers. What’s interesting about this is that if you’re already decoupling pieces of your application using postal, you can actually move some of the components into an iframe or worker, for example, and as long as postal.xframe is present (and postal.federation’s filters are allowing the messages to pass), they will continue to operate as they always have.

“Yeah, but really, Jim. How often do you want to move components into an iframe just to show they can continue to work as expected, with no changes?”

Exactly – this isn’t a common use-case. But if you’re one of the many, like myself, that’s had to solve the pain of cross-frame communication, you know how nice it would be to have a consistent (not to mention flexible and powerful) API you can use to drop into any cross-frame situation. So, what does it look like to use postal.xframe?

Sample App

I’ve created an xframe sample app and – as many sample apps go – it’s silly and contrived (even makes use of much-loathed “word clouds”, sorry John Sheehan). Here’s the gist:

  • The main page (index.html) contains a search box (top right), which defaults to “JavaScript”. Clicking “initialize” will kick off a server-side search utility against Twitter’s public HTTP API for that term. (Search results will be rendered in 3 iframes, explained below)
  • When the parent page (index.html) loads, it loads up 5 iframes and federates with 4 of them via postal.xframe.
    • 1 frame contains the README from the repo (shown initially, by default). It disappears as soon as you initialize a search. (This iframe isn’t federated via postal.)
    • The “comm” iframe is a “communication iframe” – it contains a socket connection to the server, and is not visible
    • The “stream” iframe contains assets necessary to render a twitter timeline based on the search results
    • The “language” iframe contains assets necessary to render a raphael.js-based pie chart, showing a percentage breakdown by language in the tweets returned from the search
    • The “hashtag” iframe contains assets necessary to render a canvas-based tag cloud of all the hashtags represented in the search results
  • As the server process search results from twitter, messages are batched and sent to the comm iframe via The comm iframe publishes these results via postal, which results in them being passed up into the parent iframe and onto the other federated iframes.
  • There is an alternate example page – worker.html – which replaces the “comm” iframe with a worker – everything continues to function as expected, just one less iframe. Caveat emptor: postal.xframe’s API still needs to be evolved – the worker support especially is perhaps one notch above “bare bones” – but it’s a start. Of course, to run the worker-enabled version of the demo, you’ll need a browser that supports workers.

I won’t cover every detail, so let’s look at the highlights:

// Taken from the top of the parent-main.js module

  { channel: 'search',  topic: '#', direction: 'both' },
  { channel: 'ctrl', topic: '#', direction: 'out'  },
  { channel: 'language-stats', topic: '#', direction: 'both' },
  { channel: 'hashtag-stats',  topic: '#', direction: 'both' },
  { channel: 'user-stats',  topic: '#', direction: 'both' }


The above snippet shows how the parent window configures itself to work via xframe.

  • instanceId() – Every instance of postal must have a way to be uniquely identified if it federates with other instances. The postal.federation core bits add a postal.instanceId() method to provide this. In our case, instead of a GUID or otherwise random identifier, we’re cheating and calling this postal instance “parent”, since it lives in the parent window.
  • filters – By default, postal.federation starts in “whitelist” mode, which means ONLY messages matching any added filters will sent to/from remote instances of postal via federated connections. Unless we add filters, or switch the mode to “blacklist”, no messages will leave the local window. Filters can affect inbound messages, outbound messages or both (via “in”, “out” and “both”). The channel & topic values are passed to the bindings resolver (same one used to route messages to subscribers), so it’s possible to use topic wildcards here.
  • signalReady() – This is how the local instance of postal reaches out to see if any other instances can federate. Each transport plugin would provide it’s own implementation of *how* to signalReady, and since we’re using postal.xframe in this scenario, it will (by default) find all the iframes in the document and post a message to each that will start the postal federation handshake, if postal is on the other end as well.

So – that’s the parent window, what about one of the “widget” iframes? Let’s look at an example from the “language stats” pie chart widget:

// taken from the languageStats-main.js module
  { channel: 'language-stats', topic: '#', direction: 'in' },
  { channel: 'search', topic: 'started', direction: 'in' }

// and later:

Each “iframe” that joins in the federation has similar set-up code along these lines. Aside from that, however, the rest of the code is written with zero knowledge nor baked-in assumption that messages might originate at a remote location.

So What About Cross Domain Security?

I’m glad you asked. In order for windows served from different origins to communicate, the burden is on you to ensure that the proper headers are in the response(s) and that you’re using a client that supports CORS. (See the further reading at the end of the post.) Assuming you have all that in place, here’s an example of configuring postal.xframe to set allowed origins from/to which you’ll allow messages to be sent:

// "allowedOrigins"- origins you're ok interacting with
// "defaultOriginUrl" the origin to be passed to postMessage
// when postal.xframe transmits messages to other windows
    allowedOrigins : [
    defaultOriginUrl : ""

So, About that “Overkill”

This example is somewhat over the top. After all, it’s not everyday that you’ll have multiple widgets that could open their own socket connections back to the same source. However, I have run into this very situation – and opening multiple socket connections back to a single source is a recipe for the devops-pocalypse™. In that case, postal.xframe made it possible for the widgets to share the same connection. In addition, I’ve run into simpler situations where the page hosted a form widget (in an iframe), needed some sort of clean way to exchange data between parent and child windows. The setup overhead in both circumstances is the same – and given what’s being done under the hood, I consider it a pretty light configuration overhead.

You’ll definitely find more everyday use-cases for postal.js itself vs the federation add-ons. But if you need them, they’re easy to get, setup and use.  In the meantime, pull down the sample app and have some fun….

Also – feel free to watch the high level walk-through of the xframe demo app (forgive the grainy quality):

Futher Reading

window.postMessage at MDN

Enable CORS

Cross Origin Resource Sharing on Wikipedia


MDN – Worker

postal wiki

postal.federation core add-on (github)

postal.xframe transport plugin (github)

Want a different option than postal.xframe? Check out Easy XDM.

Tagged with:  
  • bb

    Hi,one question,are the widget supports ie6+ ?

    • sntx


    • Jim Cowart

      Support for postMessage is only IE8+, so IE6 does not support these widgets.