IFrame和IFrame之间的跨文档消息传递问题家长

Issues with Cross Document Messaging between IFrame & Parent

本文关键字:IFrame 消息传递 问题家 文档 之间      更新时间:2023-09-26

我有一个应用程序在一个"外国"页面(不同的域等)的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 <和其他"遗留"的浏览器版本,所以这些都不是问题在这里>
  • 我的错误报告工具报告了许多不同的浏览器,其中包括最新版本的浏览器(FF 49, Android 5上的Chrome 43, Win和Android 6上的Chrome 53, Mobile Safari 10,…)
    • 因此,这似乎不是一个与特定浏览器或版本相关的问题。
  • 500毫秒的超时只是一些神奇的数字,我选择了我认为将是完全安全的…

问题似乎是在你的PluginOnParent.js,你在哪里发送你的响应。而不是使用"e.o origin"(在开发人员工具中检查后返回"null")—尝试使用文字"*",正如以下文档中关于postMessage用法的说明(在targetOrigin的描述中):

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

另外,作为奖励,我刚刚在两个不同的域测试了这个,它也有效。我将Parent.html放在一个域的web服务器上,并将iframe的src更改为另一个完全不同域的child.html,它们可以很好地通信。

Parent.html

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

Child.html

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

希望有帮助!

大多数时候,这种交流都是按计划进行的,但有时我看到一些错误报告到我的错误跟踪工具,无法计算找出它们发生的原因。

当超时到达并报告错误时,这里会发生什么?

我自己无法控制父页面

不确定函数errorTracking.report在调用时做什么,虽然没有出现与message事件有关的实际错误?

500ms的超时时间只是我选择的一个神奇的数字本以为会完全安全的…

如果duration设置在500, setTimeout可以在window触发message事件之前被调用。

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

setTimeoutduration调整为更长的持续时间。


或者用onerror处理器或window.addEventListener代替setTimeout

指出

当脚本中出现语法错误时,从不同的来源,语法错误的细节不报告给防止信息泄漏(见bug 363897)。而是错误报告的只是"脚本错误"。中可以重写此行为有些浏览器使用了cross - origin属性服务器发送相应的CORS HTTP响应头。一个解决方法是隔离"脚本错误",并知道如何处理它错误细节只能在浏览器控制台中查看,而不能

例如

// handle errors
onerror = function messageErrorHandlerAtAppInsideIFrame(e) {
  console.error("Error at messageErrorIndex", e)
}

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

iframeload事件中使用postMessage与父windowmessage处理程序通信。

http://plnkr.co/edit/M85MDHF1kPPwTE2E0UGt?p =预览