Is cross-origin postMessage broken in IE10?

72,173

Solution 1

I was mistaken when I originally posted this answer: it doesn't actually work in IE10. Apparently people have found this useful for other reasons so I'm leaving it up for posterity. Original answer below:


Worth noting: the link in that answer you linked to states that postMessage isn't cross origin for separate windows in IE8 and IE9 -- however, it was also written in 2009, before IE10 came around. So I wouldn't take that as an indication that it's fixed in IE10.

As for postMessage itself, http://caniuse.com/#feat=x-doc-messaging notably indicates that it's still broken in IE10, which seems to match up with your demo. The caniuse page links to this article, which contains a very relevant quote:

Internet Explorer 8+ partially supports cross-document messaging: it currently works with iframes, but not new windows. Internet Explorer 10, however, will support MessageChannel. Firefox currently supports cross-document messaging, but not MessageChannel.

So your best bet is probably to have a MessageChannel based codepath, and fallback to postMessage if that doesn't exist. It won't get you IE8/IE9 support, but at least it'll work with IE10.

Docs on MessageChannel: http://msdn.microsoft.com/en-us/library/windows/apps/hh441303.aspx

Solution 2

Create a proxy page on the same host as launcher. Proxy page has an iframe with source set to remote page. Cross-origin postMessage will now work in IE10 like so:

  • Remote page uses window.parent.postMessage to pass data to proxy page. As this uses iframes, it's supported by IE10
  • Proxy page uses window.opener.postMessage to pass data back to launcher page. As this is on same domain - there are no cross-origin issues. It can also directly call global methods on the launcher page if you don't want to use postMessage - eg. window.opener.someMethod(data)

Sample (all URLs are fictitous)

Launcher page at http://example.com/launcher.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Test launcher page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function log(msg) {
            if (!msg) return;

            var logger = document.getElementById('logger');
            logger.value += msg + '\r\n';
        }            

        function toJson(obj) {
            return JSON.stringify(obj, null, 2);
        }

        function openProxy() {
            var url = 'proxy.htm';
            window.open(url, 'wdwProxy', 'location=no');
            log('Open proxy: ' + url);
        }

        window.addEventListener('message', function(e) {
            log('Received message: ' + toJson(e.data));
        }, false);
    </script>
    
    <button onclick="openProxy();">Open remote</button> <br/>
    <textarea cols="150" rows="20" id="logger"></textarea>

    </body>
</html>

Proxy page at http://example.com/proxy.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Proxy page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function toJson(obj) {
            return JSON.stringify(obj, null, 2);
        }

        window.addEventListener('message', function(e) {
            console.log('Received message: ' + toJson(e.data));

            window.opener.postMessage(e.data, '*');
            window.close(self);
        }, false);
    </script>

    <iframe src="http://example.net/remote.htm" frameborder="0" height="300" width="500" marginheight="0" marginwidth="0" scrolling="auto"></iframe>

    </body>
</html>

Remote page at http://example.net/remote.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Remote page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function remoteSubmit() {
            var data = {
                message: document.getElementById('msg').value
            };

            window.parent.postMessage(data, '*');
        }
    </script>
    
    <h2>Remote page</h2>

    <input type="text" id="msg" placeholder="Type a message" /><button onclick="remoteSubmit();">Close</button>

    </body>
</html>

Solution 3

== WORKING SOLUTION IN 2020 without iframe ==

Building on answer by tangle, I had success in IE11 [and emulated IE10 mode] using following snippet:

var submitWindow = window.open("/", "processingWindow");
submitWindow.location.href = 'about:blank';
submitWindow.location.href = 'remotePage to communicate with';

Then I was able to communicate using typical postMessage stack, I'm using one global static messenger in my scenario (although I don't suppose it's of any significance, I'm also attaching my messenger class)

    var messagingProvider = {
        _initialized: false,
        _currentHandler: null,

        _init: function () {
            var self = this;
            this._initialized = true;
            var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
            var eventer = window[eventMethod];
            var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

            eventer(messageEvent, function (e) {
                var callback = self._currentHandler;
                if (callback != null) {
                    var key = e.message ? "message" : "data";
                    var data = e[key];
                    callback(data);
                }
            }, false);
        },

        post: function (target, message) {
            target.postMessage(message, '*');
        },

        setListener: function (callback) {
            if (!this._initialized) {
                this._init();
            }

            this._currentHandler = callback;
        }
    }

No matter how hard I tried, I wasn't able to make things work on IE9 and IE8

My config where it's working:
IE version: 11.0.10240.16590, Update versions: 11.0.25 (KB3100773)

Solution 4

Building upon the answers by LyphTEC and Akrikos, another work-around is to create an <iframe> within a blank popup window, which avoids the need for a separate proxy page, since the blank popup has the same origin as its opener.

Launcher page at http://example.com/launcher.htm

<html>
  <head>
    <title>postMessage launcher</title>
    <script>
      function openWnd() {
        var w = window.open("", "theWnd", "resizeable,status,width=400,height=300"),
            i = w.document.createElement("iframe");

        i.src = "http://example.net/remote.htm";
        w.document.body.appendChild(i);

        w.addEventListener("message", function (e) {
          console.log("message from " + e.origin + ": " + e.data);

          // Send a message back to the source
          e.source.postMessage("reply", e.origin);
        });
      }
    </script>
  </head>
  <body>
    <h2>postMessage launcher</h2>
    <p><a href="javascript:openWnd();">click me</a></p>
  </body>
</html>

Remote page at http://example.net/remote.htm

<html>
  <head>
    <title>postMessage remote</title>
    <script>
      window.addEventListener("message", function (e) {
        alert("message from " + e.origin + ": " + e.data);
      });

      // Send a message to the parent window every 5 seconds
      setInterval(function () {
        window.parent.postMessage("hello", "*");
      }, 5000);
    </script>
  </head>
  <body>
    <h2>postMessage remote</h2>
  </body>
</html>

I'm not sure how fragile this is, but it is working in IE 11 and Firefox 40.0.3.

Solution 5

Right now, (2014-09-02), Your best bet is to use a proxy frame as noted in the msdn blog post that details a workaround for this issue: https://blogs.msdn.microsoft.com/ieinternals/2009/09/15/html5-implementation-issues-in-ie8-and-later/

Here's the working example: http://www.debugtheweb.com/test/xdm/origin/

You need to set up a proxy frame on your page that has the same origin as the popup. Send information from the popup to the proxy frame using window.opener.frames[0]. Then use postMessage from the proxy frame to the main page.

Share:
72,173
Bosh
Author by

Bosh

Updated on July 06, 2021

Comments

  • Bosh
    Bosh almost 3 years

    I'm trying to make a trivial postMessage example work...

    • in IE10
    • between windows/tabs (vs. iframes)
    • across origins

    Remove any one of these conditions, and things work fine :-)

    But as far as I can tell, between-window postMessage only appears to work in IE10 when both windows share an origin. (Well, in fact -- and weirdly -- the behavior is slightly more permissive than that: two different origins that share a host seem to work, too).

    Is this a documented bug? Any workarounds or other advice?

    (Note: This question touches on the issues, but its answer is about IE8 and IE9 -- not 10)


    More details + example...

    launcher page demo

    <!DOCTYPE html>
    <html>
      <script>
        window.addEventListener("message", function(e){
          console.log("Received message: ", e);
        }, false);
      </script>
      <button onclick="window.open('http://jsbin.com/ameguj/1');">
        Open new window
      </button>
    </html>
    

    launched page demo

    <!DOCTYPE html>
    <html>
      <script>
        window.opener.postMessage("Ahoy!", "*");
      </script>
    </html>
    

    This works at: http://jsbin.com/ahuzir/1 -- because both pages are hosted at the same origin (jsbin.com). But move the second page anywhere else, and it fails in IE10.

  • balpha
    balpha over 10 years
    How would you create a MessageChannel-based codepath that works? You still need functioning postMessage to get the channel's port to the other window.
  • ShZ
    ShZ over 10 years
    Using postMessage with the new MessageChannel API will work across new windows and origins. The working is a little awkward I guess, but basically: postMessage('foo', '*') is bad, postMessage('foo', [messageChannel.port2]) is good.
  • Todd Menier
    Todd Menier about 10 years
    I can't for the life of me get postMessage working with a cross-domain popup window using the latest version of IE (11) and the MessageChannel API. And honestly I can't find anywhere else on the InterWebs other than this answer indicating that this specific scenario should work. Can anyone point to an example proving that it works? I'd be forever grateful.
  • PhistucK
    PhistucK almost 10 years
    That does not work in cross origin settings, which is one of the prerequisites in the question.
  • EricLaw
    EricLaw over 9 years
    MessageChannel shouldn't work for the same reason postMessage won't. Microsoft needs to fix the cross-process marshalling. blogs.msdn.com/b/ieinternals/archive/2009/09/15/…
  • Akrikos
    Akrikos over 9 years
    This answer is annoying because it gives us false hope. The answer is: it won't work without a proxy because postMessage is required to get MessageChannel working (at least in every demo I've seen). Unless someone shows me a demo of MessageChannel working without postMessage or postMessage('name', '<domain>', [messageChannel.port2]) working cross domain (I couldn't get it to work), I won't believe this works without a proxy frame.
  • Akrikos
    Akrikos over 9 years
    Your answer would be better if it included a link to the Microsoft example page & workaround page linked to in other answers. Workaround: blogs.msdn.com/b/ieinternals/archive/2009/09/16/… Example (from workaround page): debugtheweb.com/test/xdm/origin
  • Akrikos
    Akrikos over 9 years
    Also, your example page now links to a godaddy page not found.
  • LyphTEC
    LyphTEC over 9 years
    huh? ... all URLs are EXAMPLES and are not meant to actually point to existing pages... from the listed source you can determine what needs to be done to get it working..
  • Akrikos
    Akrikos over 9 years
    Thanks for the clarification. :-)
  • tangle
    tangle over 8 years
    ... and now it doesn't work (silent failure in popup window to <iframe> direction) in IE 11 (11.0.9600.18036, update versions 11.0.23 (KB3087038)). Possibly the recent security update (KB3087038) is implicated.
  • dkr88
    dkr88 over 7 years
    I just have to say - in case anyone else is looking at this workaround thinking "no way, that couldn't possibly fix it" - yes, it actually fixed the inability to postMessage to the window.opener in IE11. Unbelievable.
  • lmiguelmh
    lmiguelmh about 7 years
    No way... I was like 2 days trying and this "solve" the problem
  • Simon
    Simon about 6 years
    works like charms and mysterious!! (IE10.0.9200, win7)
  • OG Sean
    OG Sean over 4 years
    Did you try easyXDM for XDM postMessage in IE/Edge? easyxdm.net ?
  • Bruno Laurinec
    Bruno Laurinec about 4 years
    Check my answer below, it makes the postMessage work for IE10 and 11 without any proxy
  • msm2020
    msm2020 almost 4 years
    Can you provide a full Example for this workaround ?
  • Bruno Laurinec
    Bruno Laurinec almost 4 years
    The whole trick is the 3 lines mentioned in the beginning of the solution
  • Sid Jonnala
    Sid Jonnala over 3 years
    @BrunoLaurinec Is the submitWindow.location.href = 'about:blank'; line required before it is reassigned to the actual remotePage?
  • Bruno Laurinec
    Bruno Laurinec over 3 years
    @SidJonnala not really, but I would recommend that. If you reassign to actual remote page immediately and your page takes 3-4s to load [might happen from time to time] then you risk that your window.open('/') page loads and confuses the user