Home > GWT > Implementing postMessage technology in GWT

Implementing postMessage technology in GWT

It was long time since I wrote last blog post here. I’ll do this more often now.

Today’s post will be about postMessage and how to use postMessage and GWT. In two words, postMessage is a html5 technology that allow you to exchange messages between iframe and parent and even between tabs (IE has some limitations on messages between tabs). If you want to know more about postMessage here is the few articles about:

The last one is extremely good in explanation what is postMessage and how to implement it. I used it when adopting this technology to use in GWT.

So, for implementing this feature in GWT we will use native capabilities of GWT. I’ve tried to find, but there is no wrapper in GWT for now, so native is the way to go. This example will show how to talk between iframe and parent. Let’s start.

We will need iframe embed in GWT app in order to has example working:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ui:UiBinder SYSTEM "http://google-web-toolkit.googlecode.com/files/xhtml.ent">
 
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
             xmlns:g='urn:import:com.google.gwt.user.client.ui'>
 
 
    <g:HTMLPanel>
        <g:Frame ui:field="childFrame" width="100%" height="100%"/> 
    </g:HTMLPanel>
</ui:UiBinder>

 

In order to find iframe from native we will need to set id for it.
When you initializing your view, you can setup id for the widget:

HTMLPanel mainPanel = uiBinder.createAndBindUi(this);
childFrame.getElement().setId("__child_frame" );
return mainPanel;

This id will be needed later when we will need for sending events to the iframe. Ok. on this step iframe sending events is done. Time to send few events for the iframe:

private native void sendIFrameMessage(String object) /*-{
    var domain = $wnd.location.protocol + "//" + $wnd.location.hostname;
    var iframe = $wnd.document.getElementById('__child_frame').contentWindow;
    iframe.postMessage(object, domain);
}-*/;

What was done here? You are sending event to iframe and as a domain put domain from which your host is now serving. As you can see, we find iframe contentWindow by the id we assigned earlier. In order to put some security for this kind of messages, there is a property of event in which you can get who send the event. If domains will not match then you will got an error.
Now, child can receive messages. You need to setup an event handler that will react on this messages in the child. I’ll just copy-paste code from the David’s article (http://davidwalsh.name/window-postmessage) for convenience:

window.addEventListener('message',function(event) {
	if(event.origin !== 'http://yourdomain.com') return;
	console.log('received response:  ',event.data);
},false);

Ok, this is an easy part. Now we want to know how to receive messages from the iframe to GWT. This is a little bit more complicated. In order to setup listener we will use the same approach with native functions – do security checks in the native and just forward the call to the GWT function. Here how it is look like:

private native void injectEventListener(ChildContainerPresenter p) /*-{
    function postMessageListener(e) {
        var curUrl = $wnd.location.protocol + "//" + $wnd.location.hostname;
        if (e.origin !== curUrl) return; // security check to verify that we receive event from trusted source
        p.@com.bear-z.demo.presenter.ChildContainerPresenter::eventListener(Ljava/lang/String;)(e.data); // call function with the name
    }
    // Listen to message from child window
    if ($wnd.BrowserDetect.browser == "Explorer") {
        // fucking IE
        $wnd.attachEvent("onmessage", postMessageListener, false);
    } else {
        // "Normal" browsers
        $wnd.addEventListener("message", postMessageListener, false);
    }
}-*/;

In my example I serve pages from the same host, so I’m checking for matching urls, but you may add your check if you want (line 4). Another interesting thing is how events attached. For IE/normal browsers there is some difference. I’m using Browser detect script (http://www.quirksmode.org/js/detect.html) to detect what browser user currently running. Then in IE it is “attachEvent” to ‘onmessage’ function and in other browsers it is ‘addEventListener’ to the ‘message’ function.
If you look closely to this function you will see that this ChildContainerPresenter argument. What is this? Actually, this is a class function from which it must be called. GWT documentation said that you can use “this” context to call the functions, but this never worked for me. I would highly appreciate if someone can explain me how to use ‘this’. So, I’m using this two functions for listen and inject this listener:

@Override
public void createPresenter() {
    injectEventListener(this);
}
 
public void eventListener(String message) {
    Log.debug("Received a message from child: " + message);
}

Important thing to say: postMessage do support sending objects, not only strings, but this is not working in IE (at least at the time I tried, in the recent documentation they are saying that this is supported). In order to not mess up with all the objects, I just use json strings and enums for sending back and force messages, but this is not the main part of this post, so if someone need this just ping me and I’ll share this code as well.

Categories: GWT Tags: , , ,
  1. No comments yet.
  1. No trackbacks yet.