grack.com

I previously discussed using window.name as a transport last year, but there wasn’t enough information and theory in the previous post to implement your own transport. I’ll fill in the gaps a bit more here to help developers looking to implement this themselves. You can also see the implementation we’re using at DotSpots in the gwt-rpc-plus project: Cross­Domain­Frame­Transport.java and Cross­Domain­Frame­Transport­Request.java.

The process of making a window.name request is described below. Note that you can inspect the HTML and HTTP traffic on my blog to see how our DotSpots plugin performs the cross-domain requests.

  1. Create the iframe and form to post. In IE, we use ActiveXObject as previously discussed to work around the machine-gun navigation sounds. You’ll need to stash a reference to the ActiveXObject somewhere where IE won’t garbage collect it.

    if ("ActiveXObject" in window) {
        var doc = new ActiveXObject("htmlfile");
        doc.open();
        doc.write("<html><body><iframe id='iframe'></iframe></body></html>");
        doc.close();
        var iframe = doc.getElementById('iframe');
    } else {
        var doc = document;
        var iframe = doc.createElement('iframe');
        doc.body.appendChild(iframe);
    }
    
    var form = doc.createElement('form');
    doc.body.appendChild(form);
    
  2. Set the the iframe’s name to a globally unique request ID. Using a unique request ID is very important, as two frames with the same name in Safari will result in one of them being set to a random name.

    iframe.contentWindow.name = requestId;
    form.target = requestId;
    
  3. POST the form to the endpoint, including the unique request ID and URL on the host domain. You can usually use /favicon.ico as a local path to return to. This is what gets posted by the DotSpots plugin on my blog:

    serial=0-8908858
    data=... (truncated JSON data)
    type=window.name
    redirect=http%3A%2F%2Fgrack.com%2Ffavicon.ico
    
  4. At this point, the iframe is now in a different security domain, so onload events won’t fire and window name is inaccessible.

  5. The server generates code to set the window name and redirect back to the original domain. Since the iframe is in a different security domain, the parent page won’t be able to tell that it was loaded. We redirect this iframe back to the URL requested once we’ve set the name.

    <html><body>
    <script>
    window.name='wnr-0-8908858(truncated JSON data)';
    window.location.replace('/favicon.ico');
    </script></body></html>
    
  6. The browser redirects back. When it gets back to the other domain’s favicon, onload will successfully fire and the window.name previously set by the server will be accessible. As multiple onload and onreadystatechange events might occur during the request/response process, you should always check the window.name for the appropriate prefix.

  7. Clean up the iframe and form. Remove the iframe and form after the request completes, but do this after a short delay. If you remove the iframe immediately after onload fires, the Firefox loading throbber will continue to animate indefinitely.

If you are considering using a transport like this for your own project, you should also consider using postMessage for uplevel browsers that support it. There are two HTTP requests required for window.name, adding a fair bit of latency to each cross-domain request. Using postMessage reduces the number of requests to one, cutting the round-trip latency by 50%.

Your server responses when using postMessage should look like this instead:

<html><body>
<script>
window.parent.postMessage('wnr-0-8908858(truncated JSON data)', '*');
</script></body></html>

If you’re using GWT, all of this work is already done for you in our open-source library, gwt-rpc-plus. We’ve tested it against all of the current desktop browsers (all the way back to IE6) and the mobile browsers too (Android and iPhone).

Read full post