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

sua*_*kim 10 javascript iframe messaging cross-domain

我有一个在"外国"页面(不同的域等)上的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);
// ...
Run Code Online (Sandbox Code Playgroud)

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' }, '*');
// ...
Run Code Online (Sandbox Code Playgroud)

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

我的更多信息和想法:

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

Mic*_*len 1

问题似乎出在您发送响应的PluginOnParent.js中。不要使用“e.origin”(在开发人员工具中检查后返回“null”)——尝试使用文字“*”,如以下关于postMessage用法的文档中所述(在targetOrigin的描述中):

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

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

父级.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>
Run Code Online (Sandbox Code Playgroud)

孩子.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>
Run Code Online (Sandbox Code Playgroud)

希望有帮助!