Monday, October 04, 2010

HTML5 Messaging and PMRPC

From a suggestion by David Braun, I took a quick look at PMRPC as a way to send messages between browser windows and gadgets using HTML5 standards.  One of PMRPC's selling points is that it promises to hide the details of the much lower level HTML5 API.  Oh, computer science....

Since I'm not a great JavaScript programmer, I found the provided examples to be a little opaque.  Here are simple working examples, at least in Google Chrome on Mac.  They did not work for Firefox 3.6.

Window-IFrame Example
In this example, you have an HTML page that contains an iFrame, and you want to send a message from the container to the child iFrame.  Here's the parent's HTML.  I dubbed this file frame2.html.

<html>
<head>
<script src="json2.js" type="text/javascript"></script>
<script type="text/javascript" src="pmrpc.js"></script>
<script>
function dothing() {
// call the exposed procedure
pmrpc.call( {
  destination : window.frames["iFrameA"],
  publicProcedureName : "HollowPMRPC",
  params : ["Hollow World!"] } ); 
}
</script>
</head>
<body>
<iframe id="iFrameA" title="iFrameA" src="http://your.server.base/frame1.html"></iframe>
This is Frame B. 
<input type="button" name="send message" onClick="dothing()">
</body>
</html>

The embedded iFrame (called frame1.html above) looks like this:
<html>
<head>
<script src="json2.js" type="text/javascript"></script>
<script type="text/javascript" src="pmrpc.js"></script>
<script>
//function init(){
// expose a procedure
alert("Init");
pmrpc.register( {
  publicProcedureName : "HollowPMRPC",
  procedure : function(printParam) { alert(printParam); } } );
//}
</script>
</head>
<body>
This is Frame A.
</body>
Click the button and send the alert.   Welcome to 2010.

IFrame to IFrame Communication
To get two peer windows (two embedded iFrames in this case) to send messages to each other, we need to use the "publish" destination in frame1.html.  We'll also delete the iframe tag.  The new HTML looks like this:
<html>

<head>
<script src="json2.js" type="text/javascript"></script>
<script type="text/javascript" src="pmrpc.js"></script>
<script>
function dothing() {
// call the exposed procedure
pmrpc.call( {
  destination : "publish",
  publicProcedureName : "HollowPMRPC",
  params : ["Hollow World!"] } ); 
}
</script>
</head>
<body>
This is Frame B. 
<input type="button" name="send message" onClick="dothing()">
</body>
</html>


We don't need to make any modifications to frame2.html.  Finally, we need a simple HTML page to contain the iFrames for frame1.html and frame2.html.  I call this container.html:
<html>
<body>
<iframe id="iFrameA" title="iFrameA" src="http://your.server.base/frame1.html"></iframe>
<iframe id="iFrameB" title="iFrameB" src="http://your.server.base/frame2.html"></iframe>
</body>
</html>




3 comments:

Ivan Zuzak said...
This comment has been removed by the author.
Ivan Zuzak said...

Hi Marlon, thanks for taking pmrpc for a spin!

The point of pmrpc is, as you say, to simplify cross-context communication (context = window, iframe or web worker) by providing a higher-level programming model and some advanced features (like access control lists). This reduces the amount of boilerplate code you have to write to achieve some application goals. There are other awesome libraries like easyXDM and JSchannel that provide similar APIs and sometimes reduce the amount of boilerplate code even more. For example, in JSchannel, a "channel" is established between two window contexts which can then be used to send messages back and forth. The channel is set up once with specific options so that those options do not need to be specified with each call like in pmrpc. However, this also somewhat reduces the functionality. Basically -- you take the tool that better fits your needs.

What do you mean by the examples being opaque? I agree that the pmrpc project page needs a better "examples" section.

Ivan Zuzak said...

In order to achieve iframe-to-iframe communication, you don't NEED to use "publish". The "publish" feature is only a way to specify a destination indirectly. You could have just as well specified the destination directly by traversing the hierarchy of contexts:

pmrpc.call( {
destination : window.parent.frames["destinationFrameName"],
publicProcedureName : "HollowPMRPC",
params : ["Hollow World!"] } );
}

This will make a call to an iframe sibling with a "name" parameter value of "destinationFrameName".

The benefit of using "publish" is that you don't have to specify this name, and more importantly - sometimes you don't know the name in advance (since the names may be generated randomly by a top-level container).

However, using publish, the message is delivered to *all* methods with the name "Hollow World", which you may not want. In that case, you should use the destinationDomain parameter in the pmrpc.call method, like so:

pmrpc.call( {
destination : window.parent.frames["destinationFrameName"],
publicProcedureName : "HollowPMRPC",
destinationDomain : ["goodDomain.com", "veryGoodDomain.com"],
params : ["Hollow World!"] } );
}

This will force pmrpc to make the call to iframes loaded only from goodDomain.com and veryGoodDomain.com domains.

The pmrpc discover method is also a use case for this as it lets you discover contexts that satisfy a set of constraints:

If you have any other comments about pmrpc or suggestions on how to make it better - let me know :)

Best,
Ivan