Ivan Zuzak home about blog projects talks & papers other

Inter-window browser communication and how to make it better

10 Oct 2009

As promised in my post on named arguments in JavaScript, this post will be about pmrpc - a project I've been working on with Marko for the last few months.

But first, a bit of background. From mid 2007. when I was interning at Google up until today and my dissertation-related research, an issue that's consistently been popping up in anything I worked on is inter-window communication. Inter-window communication (IWC) is the process of transporting data from one window object within a browser to another window object inside the same browser. What's relevant here is that window objects are both full-size windows and iframes - window objects that can be embedded within other window objects (think of an iGoogle page and all the gadgets in it).

The reason why IWC is an issue is the security model of communication within browsers. Same Origin Policy (SOP), a security policy introduced to reduce security risks, restricts access of processes executing within browsers. In essence, SOP prevents a document or script loaded from one domain from getting or setting properties of a document from another domain. For example, a page loaded from http://www.google.com can not directly access data in an iframe loaded from http://www.yahoo.com.

Although SOP turns Web applications into a set of isolated units, the need for communication between those islands is growing rapidly. With the advancement web technologies, web applications were slowly becoming componentized and many current web applications are based on mashing-up content from different sources - map gadgets, finance widgets, blog commenting toolboxes, games, chat widgets and many, many others. Since widgets, content encapsulated within an iframe, are the most popular method of including cross-domain content on a web page, inter-widget communication is most often implemented as inter-window communication.

A lot of groups and projects are researching models and developing systems that address the problem of communication between widgets and between windows in general. So here's a short overview of this broad space of R&D. Various widget specifications are trying to formally define and standardize what a widget actually is. Browser security in general and various issues concerning intra-browser communication and mashup security are being heavily researched. Low-level IWC mechanisms are being proposed and implemented, sometimes even as browser hacks, and high-level communication models are being researched and developed. Even patents have been filed for methods of inter-window and inter-widget communication.

Among all of these efforts, one is catching on on a wider scale - the HTML5 cross-document messaging specification aka the postMessage API. The postMessage API has been implemented in all popular web browsers and enables secure message-oriented communication between any window objects - scripts in any window may send messages to any other window and receive messages from scripts in other windows. Security is established by two access-control mechanisms: 1) the message sender may specify the domain from which the destination window must be loaded in order for the message to be delivered, and 2) the message receiver may inspect the domain from which a message was sent and drop unwanted messages.

The problem with postMessage is that it's a very low-level and general communication mechanism which requires a lot of boiler-plate code every time it is used and even more if one wants to implement advanced communication or access-control models. Have a look at the example on using postMessage in the HTML5 spec to see what I'm talking about. However, it's exactly this generality and low-levelness that makes postMessage an excellent foundation to build upon! Some of the projects I mentioned above have been working on advanced communication libraries based on postMessage, but none that I know of are open-source and applicable in any context but rather tied to a specific application environment. Furthermore, advanced access control, fault tolerance and many other aspects of inter-window communication have been under-explored. These drawbacks were the main motivators for developing pmrpc.

Pmrpc (postMessage remote procedure call) is a JavaScript library for advanced inter-window cross-domain communication. The library utilizes the postMessage API as an underlying communication mechanism and extends it to a remote procedure call (RPC) model using JSON-RPC. JSON-RPC is a transport-independent protocol that uses the JSON data format for formatting messages. The pmrpc library provides a simple API for exposing and calling procedures from windows or iFrames on different domains.

Here's a simple example for calling a function in another window:

1. First, a procedure is registered for remote calls in the window or iFrame that contains the procedure:

1 // [window object A] exposes a procedure in it's window
2 pmrpc.register( {
3   publicProcedureName : "HelloPMRPC",
4   procedure : function(printParam) { alert(printParam); } } );

2. Second, the procedure is called from a remote window or iFrame by specifying the window object which contains the remote procedure, name of the procedure and parameters:

1 // [window object B] calls the exposed procedure remotely, from its own window
2 pmrpc.call( {
3   destination : window.frames["myIframe"],
4   publicProcedureName : "HelloPMRPC",
5   params : ["Hello World!"] } );

Furthermore, pmrpc also provides several features for advanced communication: callbacks for successful and unsuccessful calls, access control lists on both the client and server side, fault tolerance using user-defined retries and timeouts for each call, registering asynchronous procedures. The features and their usage are explained in detail on the pmrpc API docs page and a short presentation, so I'll just give an example of an advanced pmrpc call:

1. First, a synchronous procedure named "HelloPMRPC" accessible from windows and iframes loaded from any page on "http://www.myFriendlyDomain1.com" or exactly from the "http://www.myFriendlyDomain2.com" page is registered:

 1 // [window object A] exposes a procedure in it's window
 2 var publicProcedureName = "HelloPMRPC";
 3 var procedure = function (alertText) { alert(alertText); return "Hello!"; };
 4 var accessControlList = {
 5   whitelist : ["http://www.myFriendlyDomain1.com*", "http://www.myFriendlyDomain2.com"],
 6   blacklist : []
 7 };
 8 
 9 pmrpc.register( {
10   "publicProcedureName" : publicProcedureName,
11   "procedure" : procedure,
12   "acl" : accessControlList
13 } );

2. Second, the procedure "HelloPMRPC" is called with a single positional parameter "Hello world from pmrpc client!". In case of success, the return value will be alerted while in case of errors the error description will be alerted. The call will be retried up to 15 times, waiting 1500 milliseconds between retries. Windows or iFrames loaded from any domain, except from http://evil.com, may process the call:

 1 // [window object B] calls the exposed procedure remotely, from its own window
 2 var parameters = {
 3   destination : window.frames["targetIframeName"],
 4   publicProcedureName : "HelloPMRPC",
 5   params : ["Hello world from pmrpc client!"],
 6   onError : function(statusObj) {alert(statusObj.description);},
 7   retries : 15,
 8   timeout : 1500,
 9   destinationDomain :  {  whitelist : ["*"],  blacklist : ["http://evil.com*"] }
10 };
11 
12 pmrpc.call(parameters);

Pmrpc is an open-source project developed by Marko Ivankovic and myself and is available under the Apache v2.0 license. The library works in all web browsers that implement the postMessage API (Firefox 3, Google Chrome, Internet Explorer 8). For more information please visit the project homepage on Google Code and the API docs page for a full description of the API, feature list and usage examples.

I'll end this post with a few ideas on making pmrpc even better:

  1. discovery - currently, in order to call a remote procedure one needs to have a reference to a window that exposes that procedure. Sometimes obtaining a reference to a window is not very practical and easy (or possible) and a way to find windows by various criteria is needed. For example, discover all windows with a specific name or location or discover windows that expose a procedure with a specific name.

  2. web workers - web workers are another specification being developed in parallel with HTML5. The specification "defines an API for running scripts in the background independently of any user interface scripts". What's interesting is that the postMessage API is used to communicate with web workers and therefor pmrpc could be extended to include support for web workers.

  3. publish-subscribe mechanism - there are lots of communication models other than remote procedure call. One of the most popular models is publish-subscribe in which publishers publish events on channels and subscribers subscribe for events on these channels. Messages are delivered from publishers to subscribers without having a direct reference to each other.

I'd appreciate any feedback on pmrpc and our ideas for future work. What are your ideas for the future of inter-window communication? @izuzak

Comments