【问题标题】:postMessage still broken on IE11?postMessage 在 IE11 上仍然损坏?
【发布时间】:2014-01-30 23:23:48
【问题描述】:

似乎window.postMessage在IE 11上仍然坏消息时

  • 在窗口和带有 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 更新:我正在考虑在窗口/选项卡标题中传递我的数据,并定期从父级检查此标题...但这将是一个相当肮脏的把戏。 p>

更新 16/01:真正糟糕的部分是,即使消息是从同一个域发送的,但在被另一个域重定向之后,它也会中断。

示例如下: http://jsfiddle.net/L4YzG/13/ 打开弹出窗口 http://jsbin.com/eQeSeros/4/edit 重定向到 http://jsfiddle.net/mxS8Q/2/(发布消息)

如果您直接通过最终 url 重定向到 http://jsfiddle.net/mxS8Q/2/show 更改 url 弹出窗口,这适用于 IE,因为在打开和发布之间没有其他域

我仍在研究我的窗口标题肮脏的把戏。当它在另一个域上时,我们无法接收窗口的标题,但如果它在 jsfiddle 上返回,则标题可用(postMessage 没有以前的问题)。下面是示例:http://jsfiddle.net/L4YzG/14/ ...这可能是一个替代解决方案,但我刚刚看到了一些关于在 cookie 中传递数据的内容,它只需要进行测试。

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

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

【问题讨论】:

  • 你能分享一些关于你的cookie解决方案的细节吗?

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


【解决方案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');

【讨论】:

    【解决方案2】:

    提到了一些iframe 解决方法,但我看到的唯一方法是iframe 发送一条消息。

    这是一个接收来自iframe的消息的示例:

    父页面 (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>
    

    子页面 (http&colon;//second-domain.com/send-message.html)

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

    【讨论】:

    【解决方案3】:

    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 调用。

    【讨论】:

    • 谢谢!这解决了我愚蠢的 IE 的问题!奇怪的是,它可以在某些版本的 IE 11 中运行,而无需此解决方法。必须是 IT 为某些用户制定的安全策略?
    【解决方案4】:

    它坏了吗?嗯,有点。

    我尝试了各种想法,但无法让您的 jsFiddle 中的代码正常工作。查看此MSDN Blog post,我们发现postMessage 仅适用于旧版本 IE 中的 IFrame 之间,尚未针对 IE 11 进行修复。

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

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

    所以看起来这样做的唯一方法是this,其中子节点托管在父节点的 IFrame 中。我根据您的代码创建了一个类似的演示 here。这很简单,但是向 IFrame 的 contentWindow 发送一条消息,然后它会响应。

    我看到了使用 MessageChannel 的建议,但我也想知道使用 Web Workers 是否值得研究,尽管它们的使用当然取决于您的任务的性质。这个question 也有答案,其中使用了 IFrame 方法,但使用了一个 jQuery UI 对话框来显示它——我想如果你愿意的话,你可以在 Bootstrap 中使用模态框做同样的事情。


    供参考:

    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>
    

    【讨论】:

    • 这是一个非常好的解决方法,但是在许多情况下这是不可能的,例如 X-Frame-Options 标头会在能够发送消息之前破坏重定向(不幸的是我的情况)。
    • @bumpmann 您是否可以控制链中的每个网站?因为看起来您可以设置 X-Frame-Options 以允许来自特定来源的页面托管它。
    • @bumpmann 我觉得我正在尝试用 oauth 做类似你的事情,你找到什么了吗?
    • postMessage 在 IE 11 中是否仍然损坏?我似乎无法让它工作。有人帮忙吗?
    猜你喜欢
    • 2021-03-06
    • 2011-11-10
    • 2016-08-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-25
    • 2013-04-20
    相关资源
    最近更新 更多