在IE11上,postMessage仍然被打破?

时间:2014-01-12 02:24:16

标签: javascript internet-explorer cross-browser cross-domain postmessage

当消息

时,似乎IE11上的window.postMessage仍然被破坏
  • 在窗口和带有window.open的子弹出/选项卡之间
  • 当它来自不同的域 [或某些情况下的相同域名时,c.f。更新16/01]

IE 8/9/10也存在类似的问题,但是这个功能在IE 11中被标记为“支持”来自IE 10中的“部分支持”

有一个代码的示例适用于chrome / ff但不适用于IE:

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');
    });
});

child popup (jsbin) :(如果没有被jsfiddle打开则无效)

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

我从Is cross-origin postMessage broken in IE10?帖子中读到,我们可以使用MessageChannel代替postMessage,但是阅读文档,我在实际案例中找不到如何使用它,因为你必须将端口传递给子窗口。

在我需要发送消息之前有一个重定向链,所以即使我可以发送一个端口,我也会丢失最初/重定向之前发送的任何js对象。

有没有替换的想法?

更新14/01 :我正在考虑在窗口/标签标题中传递我的数据并定期检查父标题中的这个标题......但这将是一个非常肮脏的技巧。

更新16/01 :真正糟糕的是,即使邮件是从同一个域发送的,但在被另一个域重定向后,它确实会中断。

这是一个例子: http://jsfiddle.net/L4YzG/13/会打开重定向到http://jsbin.com/eQeSeros/4/edit的弹出窗口http://jsfiddle.net/mxS8Q/2/(发布消息)

如果您通过最终网址直接更改网址弹出重定向到http://jsfiddle.net/mxS8Q/2/show,这适用于IE,因为在开放和开放之间没有其他域名。交

我还在处理我的窗口标题脏伎俩。当它在另一个域上时,我们无法接收窗口的标题,但如果它返回到jsfiddle,则标题可用(postMessage之前没有问题)。这是一个例子:http://jsfiddle.net/L4YzG/14/ ...这可能是一个替代解决方案,但我刚看到一些关于在cookie中传递数据的事情,它只需要进行测试。

更新04/02 :如果最终域名相同但不在跨域中,则在标题中传递信息是不够的。我想注入一个相同域的iframe来传递这些信息,但我也不能共享子窗口对象(postMessage需要一个可序列化的对象)。

最后,我尝试在注入的iframe和子窗口之间共享一个cookie(在js中创建和接收),这在chrome& amp; ff但仍然无法使用IE正确接收它。添加P3P头后它工作正常,这似乎是真正的解决方案。 Safari似乎对这种技术有一些问题,所以我只是将这种技术作为后备。

4 个答案:

答案 0 :(得分:14)

它坏了吗?嗯,有点。

我尝试了各种各样的想法,无法让jsFiddle中的代码生效。看看这个MSDN Blog post,我们发现postMessage仅适用于旧版IE中的IFrame,IE11尚未修复。

该文章链接到问题的demo。有几种解决方法涉及在window.opener上调用脚本。然而,正如该博客所述(强调我的):

  

不幸的是,这种解决方法通常是不可能的,因为同源策略规定弹出窗口和window.opener页面必须来自同一个原点才能调用彼此的脚本功能

所以看起来这样做的唯一方法就是this,其中子项托管在父项的IFrame中。我根据您的代码创建了一个类似的演示here。它非常简单,但会向IFrame的contentWindow发帖,然后响应。{/ p>

我看到建议使用MessageChannel代替,但我也想知道使用Web Workers是否值得调查,尽管它们的使用当然取决于您的任务的性质。还有这个question的答案,其中使用了IFrame方法,但是使用了jQuery UI对话框来显示它 - 我想如果您愿意,可以使用Bootstrap中的模态执行相同的操作。< / p>


供参考:

<强> 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>

父脚本

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');
    });
});

儿童HTML和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>

答案 1 :(得分:14)

  

更新16/01:真正糟糕的是,即使邮件是从同一个域发送的,但在被另一个域重定向后,它确实会中断。

非常有趣的是,这个“安全功能”可以反过来用来完全绕过跨域限制。

example.com的父窗口中:

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

example.com服务器上:

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

弹出窗口将打开您的域名,重定向到jsfiddle,postMessage调用将在IE中运行。您甚至可以在此之后导航到任何域,并继续对父窗口进行postMessage调用。

答案 2 :(得分:4)

有一些iframe解决方法,但我见过的只有iframe发送消息。

以下是来自iframe接收消息的示例:

父页(http://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>

子页面(http://second-domain.com/send-message.html)

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

答案 3 :(得分:1)

我发现,如果我启动另一个窗口,启动我的窗口,然后关闭另一个窗口,那么我可以正常使用我的窗口。

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');