postMessage still broken on IE11?

31,933

Solution 1

Update 16/01: The really bad part is that it does break even if the message is send from the same domain, but after being redirected by another domain.

Hilariously, this "security feature" can be used in reverse to bypass the crossdomain restriction entirely.

In parent window at example.com:

<script>
  window.open("http://example.com/dummy_redirect");
  window.addEventListener('message', function(ev) {console.log(ev.data)})
</script>

On example.com server:

GET /dummy_redirect 302 http://jsfiddle.net/b6yfbunw/

A popup will open to your domain, redirect to jsfiddle, and the postMessage call will work in IE. You can even navigate to any domain after that and continue to make postMessage calls to the parent window.

Solution 2

Is it broken? Well, sort of.

I tried various ideas and couldn't get the code in your jsFiddle to work. Looking at this MSDN Blog post, we find that postMessage only works between IFrames in older versions of IE, which has yet to be fixed for IE 11.

That article links to a demo of the problem. There are a couple of workarounds that involve calling scripts on the window.opener. However, as that blog states (emphasis mine):

Unfortunately, this workaround often isn't possible, because same-origin-policy dictates that the popup window and the window.opener page must be from the same origin in order to call each other's script functions.

So it looks like the only way to do this is something like this, where the child is hosted in an IFrame in the parent. I have created a similar demo here, based on the code in yours. It's pretty simple, but posts a message to the contentWindow of the IFrame, which in turn responds.

I see the recommendation to use MessageChannel instead, but I also wonder if using Web Workers might be worth investigating, though their use would of course depend on the nature of your task. There is also the answer to this question, where the IFrame approach was used, but a jQuery UI dialog was used to display it - I would imagine you could do the same thing with modals in Bootstrap if you prefer that.


For reference:

HTML

<iframe id="iframe" src="http://jsbin.com/iLapokOS/7/"></iframe>
<div id="log"></div>
<button id="post-message-button">Post message to window</button>

Parent script

var wnd;

$(document).ready(function() {
    $('#log').append('listening...');

    wnd = $('#iframe')[0].contentWindow;

    window.addEventListener('message', function(e){
      $('#log').append('<br/>Received message: ' + JSON.stringify(e.data));
    }, false);

    $('#post-message-button').click(function() {
        if(!wnd){
            return;
        }
        $('#log').append('<br/>sending...');
        wnd.postMessage('Hello?', 'http://jsbin.com');
    });
});

Child HTML and JS

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  <script>
    $(document).ready(function() {

      window.addEventListener("message", function(e){
        $('body').append('<br/>Origin: ' + e.origin);        
        $('body').append("<br/>Received message: " + JSON.stringify(e.data));

        e.source.postMessage('Hello yourself', e.origin);
      }, false);
    });
  </script>


</body>
</html>

Solution 3

There are a few mentions of an iframe workaround, but the only ones I've seen are sending a message to the iframe.

Here's an example of receiving a message from the iframe instead:

Parent page (http&colon;//first-domain.com/receive-message.html)

<html>
  <head>
    <script>
      window.addEventListener('message', console.log.bind(console, 'Got message:'));
    </script>
  </head>
  <body>
    <iframe src="http://second-domain.com/send-message.html"></iframe>
  </body>
</html>

Child-page (http&colon;//second-domain.com/send-message.html)

<html>
  <head>
    <script>
      window.parent.postMessage('hi there', '*');
    </script>
  </head>
  <body></body>
</html>

Solution 4

I found, if I launch another window, launch my window, then close the other window, then I can work with my window just fine.

vvWindow0 = window.open("http://apsed4065:8047/virtualviewer/index.html");
vvWindow = window.open("http://apsed4065:8047/virtualviewer/index.html");
vvWindow0.close(); <!-- to close the window -->

vvWindow.postMessage(message, 'http://apsed4065:8047');

Share:
31,933
bumpmann
Author by

bumpmann

Webshell CTO

Updated on April 05, 2020

Comments

  • bumpmann
    bumpmann about 4 years

    It seems that window.postMessage is still broken on IE 11 when the message is

    • between a window and a child popup/tab with window.open
    • when it's sent from different domains [or same domain in some case, c.f. update 16/01]

    There was similar issues with IE 8/9/10 but this feature was flagged as 'supported' in IE 11 from 'partially supported' in IE 10

    There is an example of the code that works on chrome/ff but not IE:

    The opener (jsfiddle):

    $(document).ready(function() {
        $('#log').append('listening...');
        window.addEventListener("message", function(e){
            $('#log').append("Received message: " + JSON.stringify(e.data));
        }, false);
        $('button').click(function() {
            window.open('http://jsbin.com/eQeSeros/1', 'popup','menubar=no, status=no, scrollbars=no, menubar=no, width=200, height=100');
        });
    });
    

    The child popup (jsbin): (won't work if not open by jsfiddle)

    $(document).ready(function() {
       $('body').append('sending...');
       window.opener.postMessage("Hello?", "http://fiddle.jshell.net");
       $('body').append('sent...');
    });
    

    I read from the post Is cross-origin postMessage broken in IE10? that we can use a MessageChannel instead of postMessage, but reading the doc, i did not find how to use it in my real case, because you have to pass the port to the child window.

    There is a redirect chain before i need to send my message, so even if i could send a port, i will lose any js object sent initially/before the redirects.

    Any idea for a replacement ?

    Update 14/01: I m thinking about passing my data in the window/tab title and regulary check this title from the parent... but this would be quite a dirty trick.

    Update 16/01: The really bad part is that it does break even if the message is send from the same domain, but after being redirected by another domain.

    here is the example: http://jsfiddle.net/L4YzG/13/ opens the popup http://jsbin.com/eQeSeros/4/edit that redirects to http://jsfiddle.net/mxS8Q/2/ (that posts the message)

    If you change the url popup directly by the final url redirects to http://jsfiddle.net/mxS8Q/2/show this works on IE because there is no other domain between the opening & post

    I am still working on my window title dirty trick. we cannot receive the title of the window when it is on another domain, but if it comes back on jsfiddle the title is available (there is not the previous problem with postMessage). Here is the example: http://jsfiddle.net/L4YzG/14/ ... This may be an alternative solution, but i just saw something about passing the data in a cookie, it just needs to be tested.

    Update 04/02: Passing the infos in the title is not sufficient, if works well if the final domains are the same but not in cross domain. I wanted to inject an iframe of the same domain to pass these infos but i cannot share the child window object either (postMessage need a serializable object).

    Finally i tried to share a cookie (created & received in js) between the injected iframe and child window, this works well on chrome & ff but still could not receive it correctly with IE. After adding P3P headers it worked fine, this seems to be the true solution. Safari seems to have some problems with this technique so i just keep this technique as a fallback.