跨域postMessage在IE10中是否损坏?


91

我正在尝试做一个简单的postMessage例子...

  • 在IE10中
  • 窗口/标签页之间(相对于iframe)
  • 跨起源

删除这些条件之一,一切正常:-)

但是据我所知,postMessage仅当两个窗口共享一个原点时,窗口间才似乎在IE10中起作用。(事实上​​,而且很奇怪,这种行为比这更宽松:共享宿主的两个不同来源似乎也起作用)。

这是有记录的错误吗?有任何解决方法或其他建议吗?

(注意:此问题涉及问题,但答案是关于IE8和IE9的,而不是10)


更多详细信息和示例...

启动器页面演示

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

启动页面演示

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

这可以在以下网址使用:http : //jsbin.com/ahuzir/1 -因为两个页面都位于相同的来源(jsbin.com)。但是将第二页移动到其他任何地方,在IE10中失败。



5
请考虑将接受的答案更改为回答问题的答案,而不是将MessageChannel列为最佳选择(当MessageChannel需要postMessage才能使其正常工作时)。我花了一个多小时与MessageChannel一起玩,才发现一个可行的解决方案是iframe代理。
Akrikos 2014年

1
如果您的window.open只是一个弹出对话框,则可以完全避免使用它,并在js模式中使用iframe。我是如何实现的,例如jQuery DialogBootstrap Modal。然后,您可以window.parent.postMessage在IE中使用。
styfle 2015年

@Bosh作为我的答案似乎可以在2018年使用,并且不需要代理框架,您是否可以将其设置为公认的答案,因为它似乎对我们有帮助,不幸的是,那些人仍然必须支持旧的,即
Bruno Laurinec

Answers:


62

当我最初发布此答案时,我错了:它实际上在IE10中不起作用。显然,由于其他原因,人们发现此功能很有用,因此我将其留给后代使用。原始答案如下:


值得注意的是:该答案中的链接链接到的状态postMessage不是IE8和IE9中单独窗口的交叉来源-但是,它也写于2009年,即IE10出现之前。因此,我不会将其表示为已在IE10中修复。

至于postMessage它本身,http://caniuse.com/#feat=x-doc-messaging明显表明它在IE10中仍然被破坏,这似乎与您的演示相匹配。caniuse页面链接到本文,其中包含一个非常相关的引用:

Internet Explorer 8+部分支持跨文档消息传递:它目前适用于iframe,但不适用于新窗口。但是,Internet Explorer 10将支持MessageChannel。Firefox当前支持跨文档消息传递,但不支持MessageChannel。

因此,最好的选择是拥有一个MessageChannel基本的代码路径,postMessage如果不存在,则回退到该路径。它不会获得IE8 / IE9的支持,但至少可以与IE10一起使用。

上的文档MessageChannelhttp : //msdn.microsoft.com/zh-cn/library/windows/apps/hh441303.aspx


8
您将如何创建有效的MessageChannel基于代码的路径?您仍然需要起作用postMessage才能将通道的端口连接到另一个窗口。
balpha

1
使用postMessageMessageChannel的API将跨越新的窗口和起源工作。我猜工作有点尴尬,但是基本上:postMessage('foo', '*')不好,postMessage('foo', [messageChannel.port2])很好。
2013年

3
我一生都无法使用最新版本的IE(11)和MessageChannel API在跨域弹出窗口中使用postMessage。老实说,我只能在InterWebs上找到其他任何地方,而这个答案表明该特定情况应该可以解决。谁能指出一个证明其有效的例子?我将永远感激不已
Todd Menier 2014年

4
MessageChannel不应出于postMessage无法使用的相同原因而工作。Microsoft需要修复跨进程编组。blogs.msdn.com/b/ieinternals/archive/2009/09/15/...
EricLaw

9
这个答案很烦人,因为它给我们带来了虚假的希望。答案是:没有代理就无法工作,因为必须使用postMessage 才能使MessageChannel工作(至少在我所看到的每个演示中)。除非有人向我展示一个不带postMessage或没有postMessage('name','<domain>',[messageChannel.port2])跨域工作的MessageChannel演示(我无法正常工作),否则我不会相信无需代理框架即可工作。
Akrikos

30

在与启动器相同的主机上创建代理页面。代理页面的iframe来源设置为远程页面。跨域postMessage现在将在IE10中工作,如下所示:

  • 远程页面用于window.parent.postMessage将数据传递到代理页面。由于使用iframe,因此IE10支持
  • 代理页面用于window.opener.postMessage将数据传递回启动器页面。由于这是同一域,因此没有跨域问题。如果您不想使用postMessage-例如,它也可以直接在启动器页面上调用全局方法。window.opener.someMethod(data)

样本(所有网址均为虚构)

的启动器页面位于 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>

代理页面位于 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>

远程页面位于 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>

如果包含其他答案中链接到的Microsoft示例页面和解决方法页面的链接,您的答案会更好。解决方法:blogs.msdn.com/b/ieinternals/archive/2009/09/16/…示例(从解决方法页面):debugtheweb.com/test/xdm/origin
Akrikos 2014年

另外,您的示例页面现在链接到未找到的哥达页面。
Akrikos

8
?? ......所有的网址为例子,并不意味着实际上点到现有的网页...从上市源头就可以确定需要做得到它的工作是什么..
LyphTEC

感谢您的澄清。:-)
Akrikos 2014年

29

==在没有iframe的情况下2020年的解决方案==

基于纠结的答案,我使用以下代码段在IE11中成功[并模拟了IE10模式]:

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

然后,我能够使用典型的postMessage堆栈进行通信,在我的场景中使用了一个全局静态Messenger(尽管我不认为它有任何意义,我还附加了Messenger类)

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

无论我多么努力,都无法在IE9和IE8上正常运行

我的配置在哪里运行:
IE版本:11.0.10240.16590,更新版本:11.0.25(KB3100773)


6
我只想说-如果其他人正在考虑这种解决方法,认为“没办法,那不可能解决”-是的,它实际上解决了无法将POSTMessage绑定到IE11中的window.opener的问题。难以置信的。
dkr88

1
没办法...我就像经过2天的尝试,这“解决”了问题
lmiguelmh

像魅力和神秘作品!(IE10.0.9200,win7)
西蒙(Simon

您能否提供此解决方案的完整示例?
msm2020

1
@SidJonnala并不是真的,但我建议您这样做。如果您立即重新分配给实际的远程页面,并且页面需要3-4秒的时间加载(可能会不时发生),则您可能会冒险加载window.open('/')页面并使用户感到困惑
Bruno Laurinec

2

在LyphTEC和Akrikos的答案的基础上,另一种解决方法是<iframe>在空白弹出窗口中创建一个,这避免了需要单独的代理页面的需要,因为空白弹出窗口与打开程序的起源相同。

的启动器页面位于 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>

远程页面位于 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>

我不确定这有多脆弱,但是它可以在IE 11和Firefox 40.0.3中运行。


1
...现在<iframe>在IE 11(11.0.9600.18036更新版本11.0.23 (KB3087038))中不起作用(在弹出窗口中出现定向失败)。可能牵涉到最近的安全更新(KB3087038)。
纠结

1

目前,(2014-09-02),最好的选择是使用msdn博客文章中所述的代理框架,其中详细介绍了此问题的解决方法: https //blogs.msdn.microsoft.com/ieinternals/2009 / 09/15 / html5-implementation-issues-in-ie8-及更高版本/

这是工作示例:http : //www.debugtheweb.com/test/xdm/origin/

您需要在页面上设置与弹出窗口具有相同来源的代理框架。使用将信息从弹出窗口发送到代理框架window.opener.frames[0]。然后使用postMessage从代理框架到主页。


1

此解决方案涉及将站点添加到Internet Explore的“受信任的站点”,而不是添加到“本地Intranet”站点中。我在Windows 10 / IE 11.0.10240.16384,Windows 10 / Microsoft Edge 20.10240.16384.0和Windows 7 SP1 / IE 10.0.9200.17148中测试了此解决方案。该页面不得包含在Intranet区域中

因此,打开Internet Explorer配置(“工具”>“ Internet选项”>“安全性”>“受信任的站点”>“站点”),然后添加页面,这里我使用*来匹配所有子域。确保该页面在本地Intranet站点中列出(工具> Internet选项>安全性>本地Intranet>站点>高级)。重新启动浏览器,然后再次测试。

在Internet Explorer中添加到受信任的站点

Windows 10 / Microsoft Edge中,您可以在控制面板> Internet选项中找到此配置。

更新

如果这不起作用,你可以尝试重置所有的设置工具> Internet选项>高级设置>重置Internet Explorer设置,然后复位:有使用它小心!然后,您将需要重新引导系统。之后,将站点添加到“受信任的站点”。

在“文件”>“属性”中或使用右键单击,查看页面位于哪个区域。

Internet Explorer中的页面属性

更新

我在公司的Intranet中,有时可以工作,有时却不能(自动配置?我什至开始怪罪公司代理)。最后,我使用了此解决方案https://stackoverflow.com/a/36630058/2692914


0

这个Q很旧,但是这就是easyXDM的用途,当您检测到不支持html5 .postMessage的浏览器时,可以将其检查为潜在的备用:

https://easyxdm.net/

它使用VBObject包装器和所有您不需要处理的东西来在窗口或框架之间发送跨域消息,其中window.postMessage对于各种IE版本均失败(并且边缘,仍然不确定100%支持) Edge有,但似乎还需要针对.postMessage的变通办法)


-3

由于MessageChannel依赖于postMessage,因此MessageChannel不适用于Windows 9 / Windows之间的IE 9-11,在这种情况下,MessageChannel仍然是无效的。“最佳”解决方法是通过window.opener(即window.opener.somefunction(“ somedata”))调用函数。

解决方法在这里更详细


1
这在跨原点设置中不起作用,这是问题中的先决条件之一。
PhistucK 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.