Проблемы с перекрестными сообщениями документов между IFrame & Parent
У меня есть приложение, работающее внутри iframe на "чужой" странице (другой домен и т.д.). Чтобы разрешить некоторую базовую связь между iframe и родителем, я загружаю часть script на родительскую страницу и использую 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, Chrome 43 на Android 5, Chrome 53 на Win и Android 6, Mobile Safari 10,...)
- Поэтому это не похоже на проблему, связанную с конкретными браузерами или версиями.
- Тайм-аут в 500 мс - это просто какое-то волшебное число, которое я выбрал, который, как я думал, будет полностью безопасным...
Ответы
Ответ 1
Проблема находится в вашем PluginOnParent.js, где вы отправляете свой ответ. Вместо использования "e.origin" (который после проверки в инструментах разработчика возвращал "нуль" ) - попробуйте использовать литерал '*', как указано в следующей документации в postMessage в описании для targetOrigin):
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
Кроме того, в качестве бонуса я просто тестировал это на двух разных доменах, и он также работает. Я поместил Parent.html на один веб-сервер доменов и изменил 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>
Ответ 2
В большинстве случаев это общение работает по назначению, но иногда я см. некоторые ошибки, сообщаемые моему инструменту отслеживания ошибок, и не может почему они происходят.
Что происходит здесь, когда сообщается об ошибках таймаута и об ошибке?
Я не могу самостоятельно контролировать родительскую страницу
Не знаете, что делает функция errorTracking.report
при вызове, хотя не появляется, что происходит фактическая ошибка, связанная с событием message
?
Тайм-аут в 500 мс - это просто волшебное число, которое я выбрал, который я мысль была бы полностью безопасной...
С duration
, установленным в 500
, setTimeout
можно вызвать до того, как событие message
запустится в window
.
timeoutId = window.setTimeout(function() {
errorTracking.report('Communication with parent page failed');
window.removeEventListener('message', messageHandler);
}, 500);
Отрегулируйте duration
от setTimeout
дольше.
Альтернативно замените onerror
обработчик или window.addEventListener
для setTimeout
Примечания
При возникновении ошибки синтаксиса (?) в script, загруженной из другое происхождение, подробности синтаксической ошибки не сообщаются предотвратить утечку информации (см. ошибка 363897). Вместо этого ошибка Сообщается, что это просто ошибка Script. "Такое поведение можно переоценить в некоторые браузеры, используя crossorigin атрибут и имеющие сервер отправляет соответствующие CORS
заголовки ответов HTTP. Обходной путь заключается в том, чтобы изолировать ошибку "Script". и справиться с этим, зная, что деталь ошибки отображается только в консоли браузера, а не доступный через JavaScript.
Например
// handle errors
onerror = function messageErrorHandlerAtAppInsideIFrame(e) {
console.error("Error at messageErrorIndex", e)
}
для обработки любых фактических ошибок при общении между различными контекстами или происхождением.
Используйте postMessage
в load
событии iframe
для связи с message
обработчиками в родительском window
.
http://plnkr.co/edit/M85MDHF1kPPwTE2E0UGt?p=preview