Проблемы с перекрестными сообщениями документов между 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