postMessage在IE11上仍然中断



当消息是时,IE 11上的window.postMessage似乎仍然被破坏

  • 在窗口和带有window.open的子弹出窗口/选项卡之间
  • 当它从不同的域发送时[或者在某些情况下从同一域发送,c.f.更新16/01]

IE 8/9/10也有类似的问题,但该功能在IE 11中被标记为"支持",而在IE 10 中被标记为

有一个代码的例子可以在chrome/ff上工作,但不能在IE上工作:

开场白(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');
});
});

子弹出窗口(jsbin):(如果不被jsfiddle打开,将不起作用)

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

我从帖子中读到了交叉源帖子IE10中的消息被破坏了吗?我们可以使用MessageChannel而不是postMessage,但在阅读文档时,我没有找到如何在我的真实案例中使用它,因为你必须将端口传递给子窗口。

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

有替换的想法吗?

更新14/01:我正在考虑在窗口/选项卡标题中传递我的数据,并定期从父级检查此标题。。。但这将是一个相当肮脏的把戏。

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

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

如果您直接更改url弹出菜单,则最终url重定向到http://jsfiddle.net/mxS8Q/2/show这在IE上有效,因为在打开&后

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

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

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

它坏了吗?嗯,有点。

我尝试了各种想法,但无法使jsFiddle中的代码发挥作用。看看MSDN博客的这篇文章,我们发现postMessage只在旧版本的IE中的IFrame之间工作,而这一点在IE 11中尚未得到修复。

这篇文章链接到这个问题的演示。有几个解决方法涉及在window.opener上调用脚本。然而,正如博客所说(重点是我的):

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

因此,看起来唯一的方法是这样做,其中子级托管在父级的IFrame中。我在这里创建了一个类似的演示,基于您的代码。这很简单,但会向IFrame的contentWindow发布一条消息,后者会做出响应。

我看到了使用MessageChannel的建议,但我也想知道使用Web Workers是否值得研究,尽管它们的使用当然取决于任务的性质。这个问题还有答案,使用了IFrame方法,但使用了jQueryUI对话框来显示它——如果你喜欢的话,我想你可以在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>

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

有几次提到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&colon;//second-domain.com/send-message.html)

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

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

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

最新更新