Cross-Domain Communication with IFrames

Recently I encountered a situation where I had to communicate between an iframe located on a different domain and its parent. Due to the “same origin policy”, a security concept for browsers, which only permits scripts running on pages originating from the same site, this was not possible to do right out of the box. After some Googling, I learned there were a couple of workarounds for it. You could use dynamic script tags included from external domains aka JSONP or use postMessage or the IFrame URL technique. All of these solutions are quite hackish or complicated and not 100% secure. Luckily I found this nice little library called easyXDM.

At the core easyXDM provides a transport stack capable of passing string based messages between two windows, a consumer (the main document) and a provider (a document included using an iframe). It does this by using one of several available techniques, always selecting the most efficient one for the current browser. For all implementations the transport stack offers bi-directionality, reliability, queueing and sender-verification.

Here’s a small test I created. It calls a function to scroll the page up from the remote site. Local and remote directories represent the servers where the files will be residing on.

local/methods.html

<!doctype html>
<html>
    <head>
        <title>easyXDM</title>
        <script type="text/javascript" src="easyXDM.js">
        </script>
        <script type="text/javascript">
            var REMOTE = (function(){
                var remote = location.href;
                switch (location.host) {
                    case "jesal.us":
                        location.href = remote.replace("provider", "consumer");
                        break;
                    case "calistolabs.com":
                        remote = remote.replace("calistolabs.com", "jesal.us");
                        break;
                }
                return remote.substring(0, remote.lastIndexOf("/"));
            }());
            var remote = new easyXDM.Rpc(/** The channel configuration */{
                /**
                 * Register the url to hash.html, this must be an absolute path
                 * or a path relative to the root.
                 * @field
                 */
                local: "name.html",
                /**
                 * Register the url to the remote interface
                 * @field
                 */
                remote: REMOTE + "/../remote/remotemethods.html",
                remoteHelper: REMOTE + "/../remote/name.html",
                /**
                 * Register the DOMElement that the generated IFrame should be inserted into
                 */
                container: "embedded",
                props: {
                    style: {
                        border: "2px dotted red",
                        height: "1200px"
                    }
                }
            }, /** The interface configuration */ {
                local: {
                    triggerScrollUp: function(){
						scroll(0,0);
                    }
                }
            });
        </script>
        <style type="text/css">
            #embedded iframe {
                width: 100%;
                height: 100%;
            }
        </style>
    </head>
    <body>
        <script type="text/javascript">
            document.write("Domain: " + location.host);
        </script>
            <br/>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<br>
			<div id="embedded">
			</div>
    </body>
</html>

remote/remotemethods.html

<!doctype html>
<html>
    <head>
        <title>easyXDM</title>
        <script type="text/javascript" src="easyXDM.js">
        </script>
        <script type="text/javascript">
            var remote = new easyXDM.Rpc(/** The channel configuration*/{
                local: "name.html",
				onReady: function(){
                    /**
                     * Call a method on the other side
                     */
                    remote.triggerScrollUp();
                }
            }, /** The configuration */ {
                remote: {
                    triggerScrollUp: {}
                }
            });
        </script>
    </head>

    <body>
        <script type="text/javascript">
            document.write("Domain: " + location.host);
        </script>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<br>
		<input type="button" onclick="remote.triggerScrollUp();" value="Call to triggerScrollUp on mother ship"/>
    </body>
</html>

remote/name.html

<!doctype html>
<html>
    <head>
        <title></title>
        <meta http-equiv="CACHE-CONTROL" content="PUBLIC"/>
        <meta http-equiv="EXPIRES" content="Sat, 01 Jan 2050 00:00:00 GMT"/>
    </head>
    <body>
        <script type="text/javascript">

        function sendMessage(message, url){
            window.setTimeout(function(){
                window.name = message;
                location.href = url + "," + encodeURIComponent(location.protocol + "//" + location.host + location.pathname);
            }, 0);
        }

        if (location.hash) {
            if (location.hash.substring(1, 2) === "_") {
                var channel, url, hash = location.href.substring(location.href.indexOf("#") + 3), indexOf = hash.indexOf(",");
                if (indexOf == -1) {
                    channel = hash;
                }
                else {
                    channel = hash.substring(0, indexOf);
                    url = decodeURIComponent(hash.substring(indexOf + 1));
                }
                switch (location.hash.substring(2, 3)) {
                    case "2":
                        // NameTransport local

                        window.parent.parent.easyXDM.Fn.get(channel)(window.name);
                        location.href = url + "#_4" + channel + ",";
                        break;
                    case "3":
                        // NameTransport remote

                        var guest = window.parent.frames["easyXDM_" + channel + "_provider"];
                        if (!guest) {
                            throw new Error("unable to reference window");
                        }
                        guest.easyXDM.Fn.get(channel)(window.name);
                        location.href = url + "#_4" + channel + ",";
                        break;
                    case "4":
                        // NameTransport idle

                        var fn = window.parent.easyXDM.Fn.get(channel + "_load");
                        if (fn) {
                            fn();
                        }
                        break;
                }
            }
        }
        </script>
    </body>
</html>

You can find lot more examples and documentation on easyXDM’s website.

If you have any questions or comments, please post them below. If you liked this post, you can share it with your followers or follow me on Twitter!