IFrame与&之间的跨文档消息传递问题父

时间:2016-10-12 06:39:48

标签: javascript iframe cross-domain messaging

我在“外国”页面(不同的域等)上的iframe内运行应用程序。允许iframe和&之间的一些基本通信。父母,我在父页面上加载了我的一些脚本,并使用postMessage进行跨文档消息传递。

大多数情况下,此通信按预期工作,但有时我会看到一些错误报告给我的错误跟踪工具,但无法弄清楚它们发生的原因。

以下是一些示例代码:

PluginOnParent.js

// ...
window.addEventListener('message', function(e) {
    // Check message origin etc...
    if (e.data.type === 'iFrameRequest') {
        e.source.postMessage({
            type: 'parentResponse',
            responseData: someInterestingData
        }, e.origin);
    }
    // ...
}, false);
// ...

AppInsideIFrame.js

// ...
var timeoutId;


try {
    if (window.self === window.top) {
        // We're not inside an IFrame, don't do anything...
        return;
    }
} catch (e) {
    // Browsers can block access to window.top due to same origin policy.
    // See http://stackoverflow.com/a/326076
    // If this happens, we are inside an IFrame...
}

function messageHandler(e) {
    if (e.data && (e.data.type === 'parentResponse')) {
        window.clearTimeout(timeoutId);
        window.removeEventListener('message', messageHandler);
        // Do some stuff with the sent data
    }
}

timeoutId = window.setTimeout(function() {
    errorTracking.report('Communication with parent page failed');
    window.removeEventListener('message', messageHandler);
}, 500);

window.addEventListener('message', messageHandler, false);
window.parent.postMessage({ type: 'iFrameRequest' }, '*');
// ...

当超时命中并报告错误时,会发生什么?

更多信息&我的想法:

  • 我自己无法控制父页面
  • 它似乎不是一般的“配置”问题(CORS等),因为错误发生在大多数时间工作的同一页面上
  • 我们不支持IE< 10和其他“遗留”浏览器版本,所以这里没有问题
  • 我的错误报告工具报告了众多不同的浏览器,其中包括最新版本(FF 49,Android 5上的Chrome 43,Win和Android 6上的Chrome 53,Mobile Safari 10,...)
    • 因此,它似乎不是与特定浏览器或版本相关的问题。
  • 500毫秒的超时只是我选择的一些神奇数字,我认为这是完全安全的......

2 个答案:

答案 0 :(得分:1)

问题似乎出现在 PluginOnParent.js 中,您将在此处发送回复。而不是使用" e.origin" (在开发人员工具中检查后返回" null") - 尝试使用文字' *',如下面关于 postMessage 用法(在 targetOrigin 的描述中):

https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

另外,作为奖励,我只是在两个不同的域中测试了它,它也可以。我将Parent.html放在一个域名网络服务器上,并将iframe的src更改为一个完全不同的域名上的child.html,他们一起沟通得很好。

<强> Parent.html

&#13;
&#13;
<html>
<head>
    <script type="text/javascript">
        function parentInitialize() {
            window.addEventListener('message', function (e) {
                // Check message origin etc...
                if (e.data.type === 'iFrameRequest') {
                    var obj = {
                        type: 'parentResponse',
                        responseData: 'some response'
                    };
                    e.source.postMessage(obj, '*');
                }
                // ...
            })
		}
    </script>
</head>
<body style="background-color: rgb(72, 222, 218);" onload="javascript: parentInitialize();">
    <iframe src="child.html" style="width: 500px; height:350px;"></iframe>
</body>
</html>
&#13;
&#13;
&#13;

<强> Child.html

&#13;
&#13;
<html>
<head>
    <script type="text/javascript">
        function childInitialize() {
            // ...
            var timeoutId;


            try {
                if (window.self === window.top) {
                    // We're not inside an IFrame, don't do anything...
                    return;
                }
            } catch (e) {
                // Browsers can block access to window.top due to same origin policy.
                // See http://stackoverflow.com/a/326076
                // If this happens, we are inside an IFrame...
            }

            function messageHandler(e) {
                if (e.data && (e.data.type === 'parentResponse')) {
                    window.clearTimeout(timeoutId);
                    window.removeEventListener('message', messageHandler);
                    // Do some stuff with the sent data
                    var obj = document.getElementById("status");
                    obj.value = e.data.responseData;
                }
            }

            timeoutId = window.setTimeout(function () {
                var obj = document.getElementById("status");
                obj.value = 'Communication with parent page failed';
                window.removeEventListener('message', messageHandler);
            }, 500);

            window.addEventListener('message', messageHandler, false);
            window.parent.postMessage({ type: 'iFrameRequest' }, '*');
            // ...
        }
    </script>
</head>
<body style="background-color: rgb(0, 148, 255);" onload="javascript: childInitialize();">
    <textarea type="text" style="width:400px; height:250px;" id="status" />
</body>
</html>
&#13;
&#13;
&#13;

希望有所帮助!

答案 1 :(得分:1)

  

大部分时间此通信都按预期工作,但有时我   看到我的错误跟踪工具报告的一些错误,无法想象   了解他们为什么会这样。

     

当超时命中并报告错误时,会发生什么?

     

我自己无法控制父页面

不确定函数errorTracking.report在调用时的作用,但似乎没有出现与message事件相关的实际错误?

  

500毫秒的超时只是我选择的一些神奇数字   以为是完全安全的......

duration设置为500setTimeout事件可能会在message点击window之前被调用。

timeoutId = window.setTimeout(function() {
    errorTracking.report('Communication with parent page failed');
    window.removeEventListener('message', messageHandler);
}, 500);

duration的{​​{1}}调整为更长的持续时间。

或者替换onerror处理程序或window.addEventListener代替setTimeout

  

注释

     

当脚本中出现语法(?)错误时,从a加载   different origin,不会报告语法错误的详细信息   防止泄露信息(见bug 363897)。而是错误   据报道只是“脚本错误”。可以覆盖此行为   某些浏览器使用crossorigin上的属性并拥有   服务器发送相应的CORS HTTP响应头。一个   解决方法是隔离“脚本错误”。并知道这一点处理它   错误详细信息只能在浏览器控制台中查看而不能查看   可通过JavaScript访问。

例如

setTimeout

在不同的上下文或来源之间进行通信时处理任何实际错误。

// handle errors onerror = function messageErrorHandlerAtAppInsideIFrame(e) { console.error("Error at messageErrorIndex", e) } postMessage事件load上使用iframe与父message的{​​{1}}处理程序进行通信。

http://plnkr.co/edit/M85MDHF1kPPwTE2E0UGt?p=preview