【问题标题】:Is cross-origin postMessage broken in IE10?IE10 中的跨域 postMessage 是否损坏?
【发布时间】:2013-04-20 01:35:20
【问题描述】:

我正在尝试制作一个简单的 postMessage 示例工作...

  • 在 IE10 中
  • 在窗口/选项卡之间(与 iframe 相比)
  • 跨域

删除其中任何一个条件,一切正常:-)

但据我所知,窗口间postMessage 仅在两个窗口共享原点时才在 IE10 中工作。 (嗯,事实上——而且奇怪的是——这种行为比这稍微宽松一些:共享一个 主机 的两个不同来源似乎也可以工作。

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

(注意:This question 涉及到这些问题,但its answer 是关于 IE8 和 IE9——而不是 10)


更多细节 + 示例...

启动页面demo

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

启动页面demo

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

这适用于:http://jsbin.com/ahuzir/1——因为两个页面都托管在同一个来源 (jsbin.com)。但是将第二页移到其他任何地方,它在 IE10 中失败。

【问题讨论】:

  • 请考虑将接受的答案更改为回答问题的答案,而不是在 MessageChannel 需要 postMessage 才能使其正常工作时将 MessageChannel 列为最佳选择的答案。我花了一个多小时玩 MessageChannel 才发现一个可行的解决方案是 iframe 代理。
  • 如果你的 window.open 只是一个弹出对话框,你可以完全避免它并在 js modal 中使用 iframe。 jQuery DialogBootstrap Modal 之类的东西是我实现它的方式。然后就可以在IE中使用window.parent.postMessage了。
  • @Bosh 因为我的答案似乎在 2018 年有效并且不需要代理框架,您是否介意将其设置为公认的答案,因为它似乎对我们有帮助,不幸的是那些仍然必须支持旧的即

标签: html cross-browser compatibility internet-explorer-10 postmessage


【解决方案1】:

当我最初发布这个答案时我弄错了:它实际上在 IE10 中不起作用。显然人们出于其他原因发现这很有用,所以我将它留给后代。原答案如下:


值得注意的是:您链接到的那个答案中的链接指出postMessage 不是 IE8 和 IE9 中单独窗口的交叉来源——但是,它也是在 2009 年 IE10 出现之前编写的。所以我不会认为它已在 IE10 中修复。

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

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

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

MessageChannel 上的文档:http://msdn.microsoft.com/en-us/library/windows/apps/hh441303.aspx

【讨论】:

  • 您将如何创建一个有效的基于MessageChannel 的代码路径?您仍然需要运行 postMessage 才能将通道的端口连接到另一个窗口。
  • postMessage 与新的MessageChannel API 一起使用将适用于新的窗口和来源。我猜工作有点尴尬,但基本上:postMessage('foo', '*') 不好,postMessage('foo', [messageChannel.port2]) 很好。
  • 我无法让 postMessage 使用最新版本的 IE (11) 和 MessageChannel API 处理跨域弹出窗口。老实说,除了这个表明这个特定场景应该有效的答案之外,我在 InterWebs 上找不到其他任何地方。谁能指出一个证明它有效的例子?我将永远感激不尽。
  • MessageChannel 不应该工作,原因与 postMessage 不能工作的原因相同。 Microsoft 需要修复跨进程编组。 blogs.msdn.com/b/ieinternals/archive/2009/09/15/…
  • 这个答案很烦人,因为它给了我们虚假的希望。答案是:没有代理它就无法工作,因为 postMessage 是 必需 才能让 MessageChannel 工作(至少在我见过的每个演示中)。除非有人向我展示了没有 postMessage 或 postMessage('name', '', [messageChannel.port2]) 跨域工作的 MessageChannel 演示(我无法让它工作),否则我不会相信这一点无需代理框架即可工作。
【解决方案2】:

在与启动器相同的主机上创建代理页面。代理页面有一个iframe,源设置为远程页面。跨域 postMessage 现在可以在 IE10 中运行,如下所示:

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

示例(所有 URL 均为虚构)

启动器页面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
  • 另外,您的示例页面现在链接到未找到的 Godaddy 页面。
  • 嗯? ...所有网址都是示例,并不意味着实际指向现有页面...从列出的来源中,您可以确定需要做什么才能使其正常工作..
  • 感谢您的澄清。 :-)
【解决方案3】:

== 2020 年不使用 iframe 的可行解决方案 ==

基于 tangle 的回答,我使用以下 sn-p 在 IE11 [和模拟 IE10 模式] 中取得了成功:

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

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

    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)

【讨论】:

  • 我只需要说 - 以防其他人在查看这个解决方法时认为“没办法,这不可能修复它” - 是的,它实际上修复了无法将消息发送到窗口的问题。 IE11 中的开瓶器。难以置信。
  • 没办法...我尝试了 2 天,这“解决”了问题
  • 作品像魅力和神秘! (IE10.0.9200, win7)
  • 整个技巧就是解决方案开头提到的3行
  • @SidJonnala 不是真的,但我会推荐。如果您立即重新分配到实际的远程页面并且您的页面需要 3-4 秒才能加载[可能会不时发生],那么您的 window.open('/') 页面可能会加载并混淆用户
【解决方案4】:

基于 LyphTEC 和 Akrikos 的答案,另一种解决方法是在空白弹出窗口中创建一个 &lt;iframe&gt;,这样就不需要单独的代理页面,因为空白弹出窗口与其开启者具有相同的来源.

启动器页面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 中运行。

【讨论】:

  • ... 现在它在 IE 11(11.0.9600.18036,更新版本 11.0.23 (KB3087038))中不起作用(弹出窗口中的静默失败 &lt;iframe&gt; 方向)。可能涉及最近的安全更新 (KB3087038)。
【解决方案5】:

现在,(2014-09-02),最好的办法是使用 msdn 博客文章中提到的代理框架,该文章详细介绍了此问题的解决方法:https://blogs.msdn.microsoft.com/ieinternals/2009/09/15/html5-implementation-issues-in-ie8-and-later/

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

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

【讨论】:

    【解决方案6】:

    此解决方案涉及将站点添加到 Internet Explorer 的受信任站点,而在本地 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 > 站点 > 高级)中。 重新启动浏览器并再次测试。

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

    更新

    如果这不起作用,您可以尝试在工具 > Internet 选项 > 高级设置 > 重置 Internet Explorer 设置中重置所有设置,然后重置:谨慎使用它!然后,您将需要重新启动系统。之后将站点添加到受信任的站点。

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

    更新

    我在公司内部网中,有时它可以工作,有时它不能(自动配置?我什至开始责怪公司代理)。最后我使用了这个解决方案https://stackoverflow.com/a/36630058/2692914

    【讨论】:

      【解决方案7】:

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

      https://easyxdm.net/

      它使用 VBObject 包装器和您永远不想处理的所有类型的东西来在窗口或框架之间发送跨域消息,其中 window.postMessage 对于各种 IE 版本(可能还有边缘,仍然不确定 100%在 Edge 的支持上,但它似乎也需要 .postMessage 的解决方法)

      【讨论】:

        【解决方案8】:

        MessageChannel 不适用于窗口/选项卡之间的 IE 9-11,因为它依赖于 postMessage,在这种情况下它仍然被破坏。 “最佳”解决方法是通过 window.opener 调用函数(即 window.opener.somefunction("somedata") )。

        Workaround in more detail here

        【讨论】:

        • 这在跨域设置中不起作用,这是问题中的先决条件之一。
        猜你喜欢
        • 2013-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-30
        • 2014-07-01
        • 2011-12-24
        • 2013-08-13
        相关资源
        最近更新 更多