Не работает ли postMessage из разных источников в IE10?

91

Я пытаюсь заставить postMessageработать тривиальный пример ...

  • в IE10
  • между окнами / вкладками (по сравнению с iframe)
  • через происхождение

Удалите любое из этих условий, и все заработает :-)

Но, насколько я могу судить, между окнами работает postMessageтолько IE10, когда оба окна имеют общий источник. (Что ж, на самом деле - и это странно - поведение немного более снисходительно, чем это: два разных происхождения, которые имеют общий хост, похоже, тоже работают).

Это задокументированная ошибка? Какие-нибудь обходные пути или другие советы?

(Примечание: этот вопрос касается проблем, но его ответ касается IE8 и IE9, а не 10)


Подробнее + пример ...

демонстрация страницы запуска

<!DOCTYPE html>
<html>
  <script>
    window.addEventListener("message", function(e){
      console.log("Received message: ", e);
    }, false);
  </script>
  <button onclick="window.open('http://jsbin.com/ameguj/1');">
    Open new window
  </button>
</html>

запущенная демонстрация страницы

<!DOCTYPE html>
<html>
  <script>
    window.opener.postMessage("Ahoy!", "*");
  </script>
</html>

Это работает по адресу: http://jsbin.com/ahuzir/1 - потому что обе страницы размещены в одном и том же источнике (jsbin.com). Но переместите вторую страницу в другое место, и в IE10 она не удастся.

Чушь
источник
5
Пожалуйста, подумайте об изменении принятого ответа на тот, который отвечает на вопрос, а не на тот, в котором MessageChannel указан как лучший вариант, когда MessageChannel требует postMessage, чтобы заставить его работать. Я потратил больше часа, играя с MessageChannel, и обнаружил, что единственным жизнеспособным решением является прокси-сервер iframe.
Акрикос
1
Если ваш window.open - это просто всплывающее диалоговое окно, вы можете вообще избежать его и использовать iframe в модальном js-окне. Что-то вроде jQuery Dialog или Bootstrap Modal - вот как я это реализовал. Тогда вы можете использовать window.parent.postMessageв IE.
Styfle 07
@Bosh, поскольку мой ответ, похоже, работает в 2018 году и не требует прокси-фрейма, не могли бы вы установить его в качестве принятого ответа, поскольку он, похоже, помогает нам, несчастным, которые все еще должны поддерживать старый, то есть
Бруно Лауринек

Ответы:

62

Я ошибся, когда изначально опубликовал этот ответ: на самом деле он не работает в IE10. Очевидно, люди сочли это полезным по другим причинам, поэтому я оставляю это для потомков. Оригинальный ответ ниже:


Стоит отметить: ссылка в этом ответе, которую вы связали с состояниями, postMessageне является перекрестным источником для отдельных окон в IE8 и IE9, однако она также была написана в 2009 году, до появления IE10. Поэтому я бы не стал считать это признаком того, что это исправлено в IE10.

Что касается postMessageсамого себя, http://caniuse.com/#feat=x-doc-messaging заметно указывает на то, что он все еще не работает в IE10, что, похоже, соответствует вашей демонстрации. На странице caniuse есть ссылка на эту статью , которая содержит очень актуальную цитату:

Internet Explorer 8+ частично поддерживает обмен сообщениями между документами: в настоящее время он работает с iframe, но не с новыми окнами. Однако Internet Explorer 10 будет поддерживать MessageChannel. В настоящее время Firefox поддерживает обмен сообщениями между документами, но не поддерживает канал MessageChannel.

Так что лучше всего, вероятно, иметь MessageChannelоснованный кодовый путь и вернуться к нему, postMessageесли его не существует. Он не даст вам поддержки IE8 / IE9, но, по крайней мере, он будет работать с IE10.

Документы на MessageChannel: http://msdn.microsoft.com/en-us/library/windows/apps/hh441303.aspx

ШЗ
источник
8
Как бы вы создали MessageChannelработающий кодовый путь? Вам все еще нужно работать, postMessageчтобы передать порт канала в другое окно.
balpha
1
Использование postMessageс новым MessageChannelAPI будет работать в новых окнах и источниках. Работа, наверное, немного неудобная, но в основном: postMessage('foo', '*')плохая, postMessage('foo', [messageChannel.port2])хорошая.
ShZ
3
Я не могу, хоть убей, заставить postMessage работать с междоменным всплывающим окном, используя последнюю версию IE (11) и API MessageChannel. И, честно говоря, я не могу найти нигде в InterWebs, кроме этого ответа, указывающего, что этот конкретный сценарий должен работать. Может ли кто-нибудь указать на пример, доказывающий, что это работает? Я буду вечно благодарен.
Todd Menier
4
MessageChannel не должен работать по той же причине, что и postMessage. Microsoft необходимо исправить кросс-процессный маршаллинг. blogs.msdn.com/b/ieinternals/archive/2009/09/15/…
EricLaw 02
9
Этот ответ раздражает, потому что дает нам ложную надежду. Ответ: он не будет работать без прокси, потому что postMessage требуется для работы MessageChannel (по крайней мере, в каждой демонстрации, которую я видел). Если кто-то не покажет мне демонстрацию работы MessageChannel без postMessage или postMessage ('name', '<domain>', [messageChannel.port2]), работающего в перекрестном домене (я не смог заставить его работать), я не поверю этому работает без фрейма прокси.
Акрикос 02
30

Создайте прокси-страницу на том же хосте, что и панель запуска. Страница прокси имеет iframeисточник, установленный на удаленную страницу. Сообщение postMessage с перекрестным происхождением теперь будет работать в IE10 следующим образом:

  • Удаленная страница используется window.parent.postMessageдля передачи данных на прокси-страницу. Поскольку здесь используются фреймы, он поддерживается IE10.
  • Страница прокси используется window.opener.postMessageдля передачи данных обратно на страницу запуска. Поскольку это в том же домене - проблем с перекрестным происхождением нет. Он также может напрямую вызывать глобальные методы на странице запуска, если вы не хотите использовать postMessage - например.window.opener.someMethod(data)

Образец (все URL фиктивные)

Страница запуска на http://example.com/launcher.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Test launcher page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function log(msg) {
            if (!msg) return;

            var logger = document.getElementById('logger');
            logger.value += msg + '\r\n';
        }            

        function toJson(obj) {
            return JSON.stringify(obj, null, 2);
        }

        function openProxy() {
            var url = 'proxy.htm';
            window.open(url, 'wdwProxy', 'location=no');
            log('Open proxy: ' + url);
        }

        window.addEventListener('message', function(e) {
            log('Received message: ' + toJson(e.data));
        }, false);
    </script>
    
    <button onclick="openProxy();">Open remote</button> <br/>
    <textarea cols="150" rows="20" id="logger"></textarea>

    </body>
</html>

Страница прокси на http://example.com/proxy.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Proxy page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function toJson(obj) {
            return JSON.stringify(obj, null, 2);
        }

        window.addEventListener('message', function(e) {
            console.log('Received message: ' + toJson(e.data));

            window.opener.postMessage(e.data, '*');
            window.close(self);
        }, false);
    </script>

    <iframe src="http://example.net/remote.htm" frameborder="0" height="300" width="500" marginheight="0" marginwidth="0" scrolling="auto"></iframe>

    </body>
</html>

Удаленная страница в http://example.net/remote.htm

<!DOCTYPE html>
<html>
    <head>
        <title>Remote page</title>
        <link rel="stylesheet" href="/css/style.css" />
    </head>
    <body>

    <script>
        function remoteSubmit() {
            var data = {
                message: document.getElementById('msg').value
            };

            window.parent.postMessage(data, '*');
        }
    </script>
    
    <h2>Remote page</h2>

    <input type="text" id="msg" placeholder="Type a message" /><button onclick="remoteSubmit();">Close</button>

    </body>
</html>
LyphTEC
источник
Ваш ответ был бы лучше, если бы он содержал ссылку на страницу примера Microsoft и страницу обходного пути, на которую есть ссылки в других ответах. Обходной путь : blogs.msdn.com/b/ieinternals/archive/2009/09/16/… Пример (со страницы обходного пути): debugtheweb.com/test/xdm/origin
Akrikos 02
Кроме того, ваш пример страницы теперь ссылается на страницу godaddy, которая не найдена.
Акрикос 02
8
а? ... все URL-адреса являются ПРИМЕРАМИ и не предназначены для того, чтобы на самом деле указывать на существующие страницы ... из перечисленного источника вы можете определить, что нужно сделать, чтобы он
заработал
Благодарю за разъяснение. :-)
Акрикос
29

== РАБОЧЕЕ РЕШЕНИЕ В 2020 ГОДУ без iframe ==

Основываясь на ответе путаницей, я добился успеха в IE11 [и эмулировал режим IE10], используя следующий фрагмент:

var submitWindow = window.open("/", "processingWindow");
submitWindow.location.href = 'about:blank';
submitWindow.location.href = 'remotePage to comunicate with';

Затем я смог общаться, используя типичный стек postMessage, в моем сценарии я использую один глобальный статический мессенджер (хотя я не думаю, что это имеет какое-то значение, я также прикрепляю свой класс мессенджера)

var messagingProvider = {
    _initialized: false,
    _currentHandler: null,

    _init: function () {
        var self = this;
        this._initialized = true;
        var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
        var eventer = window[eventMethod];
        var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";

        eventer(messageEvent, function (e) {
            var callback = self._currentHandler;
            if (callback != null) {
                var key = e.message ? "message" : "data";
                var data = e[key];
                callback(data);
            }
        }, false);
    },

    post: function (target, message) {
        target.postMessage(message, '*');
    },

    setListener: function (callback) {
        if (!this._initialized) {
            this._init();
        }

        this._currentHandler = callback;
    }
}

Как бы я ни старался, мне не удавалось заставить все работать в IE9 и IE8

Моя конфигурация, в которой он работает:
версия IE: 11.0.10240.16590, версии обновления: 11.0.25 (KB3100773)

Бруно Лауринек
источник
6
Я просто должен сказать - на случай, если кто-то еще смотрит на этот обходной путь, думая, что «нет, это невозможно исправить» - да, он действительно исправил невозможность postMessage в window.opener в IE11. Невероятно.
dkr88
1
Ни в коем случае ... Я пытался 2 дня и это «решило» проблему
lmiguelmh
работает как чары и загадочно !! (IE10.0.9200, win7)
Саймон
Можете ли вы предоставить полный пример этого обходного пути?
msm2020,
1
@SidJonnala не совсем, но я бы рекомендовал это. Если вы сразу переназначаете фактическую удаленную страницу и ваша страница загружается через 3-4 секунды [может происходить время от времени], вы рискуете, что ваша страница window.open ('/') загрузится и
запутает
2

Основываясь на ответах LyphTEC и Akrikos, еще одним решением является создание <iframe>пустого всплывающего окна, что позволяет избежать необходимости в отдельной прокси-странице, поскольку пустое всплывающее окно имеет то же происхождение, что и его открывающее средство.

Страница запуска на http://example.com/launcher.htm

<html>
  <head>
    <title>postMessage launcher</title>
    <script>
      function openWnd() {
        var w = window.open("", "theWnd", "resizeable,status,width=400,height=300"),
            i = w.document.createElement("iframe");

        i.src = "http://example.net/remote.htm";
        w.document.body.appendChild(i);

        w.addEventListener("message", function (e) {
          console.log("message from " + e.origin + ": " + e.data);

          // Send a message back to the source
          e.source.postMessage("reply", e.origin);
        });
      }
    </script>
  </head>
  <body>
    <h2>postMessage launcher</h2>
    <p><a href="javascript:openWnd();">click me</a></p>
  </body>
</html>

Удаленная страница в http://example.net/remote.htm

<html>
  <head>
    <title>postMessage remote</title>
    <script>
      window.addEventListener("message", function (e) {
        alert("message from " + e.origin + ": " + e.data);
      });

      // Send a message to the parent window every 5 seconds
      setInterval(function () {
        window.parent.postMessage("hello", "*");
      }, 5000);
    </script>
  </head>
  <body>
    <h2>postMessage remote</h2>
  </body>
</html>

Я не уверен, насколько это хрупко, но он работает в IE 11 и Firefox 40.0.3.

клубок
источник
1
... и теперь это не работает (тихий сбой во всплывающем окне для <iframe>направления) в IE 11 ( 11.0.9600.18036, версии обновления 11.0.23 (KB3087038)). Возможно, замешано последнее обновление безопасности ( KB3087038 ).
tangle
1

Прямо сейчас (02.09.2014) лучше всего использовать фрейм прокси, как указано в сообщении блога msdn, в котором подробно описывается обходной путь для этой проблемы: https://blogs.msdn.microsoft.com/ieinternals/2009 / 09/15 / html5-implementation-issues-in-ie8-and-later /

Вот рабочий пример: http://www.debugtheweb.com/test/xdm/origin/

Вам необходимо настроить прокси-фрейм на своей странице, который имеет то же происхождение, что и всплывающее окно. Отправьте информацию из всплывающего окна в фрейм прокси, используя window.opener.frames[0]. Затем используйте postMessage из фрейма прокси на главную страницу.

Акрикос
источник
1

Это решение включает добавление сайта в доверенные сайты Internet Explore, а не в сайты локальной интрасети. Я тестировал это решение в Windows 10 / IE 11.0.10240.16384, Windows 10 / Microsoft Edge 20.10240.16384.0 и Windows 7 SP1 / IE 10.0.9200.17148. Страница не должна быть включена в зону интрасети .

Итак, откройте конфигурацию Internet Explorer (Инструменты> Свойства обозревателя> Безопасность> Надежные сайты> Сайты) и добавьте страницу, здесь я использую *, чтобы сопоставить все поддомены. Убедитесь, что страницы нет в списке сайтов местной интрасети (Инструменты> Свойства обозревателя> Безопасность> Локальная интрасеть> Сайты> Дополнительно). Перезагрузите браузер и повторите попытку.

Добавить в надежные сайты в Internet Explorer

В Windows 10 / Microsoft Edge эту конфигурацию можно найти в Панели управления> Свойства обозревателя.

ОБНОВИТЬ

Если это не сработает, вы можете попробовать сбросить все свои настройки в Инструменты> Свойства обозревателя> Дополнительные настройки> Сбросить настройки Internet Explorer, а затем Сброс: используйте его с осторожностью. ! Затем вам нужно будет перезагрузить вашу систему. После этого добавьте сайты в Надежные сайты.

Посмотрите, в какой зоне находится ваша страница, в меню «Файл»> «Свойства» или щелкните правой кнопкой мыши.

Свойства страницы в Internet Explorer

ОБНОВИТЬ

Я нахожусь в корпоративной интрасети, и иногда это работает, а иногда нет (автоматическая настройка? Я даже начал обвинять корпоративный прокси). В итоге я использовал это решение https://stackoverflow.com/a/36630058/2692914 .

lmiguelmh
источник
0

Этот Q старый, но это то, для чего предназначен easyXDM, возможно, проверьте его как потенциальный запасной вариант, когда вы обнаружите браузер, который не поддерживает html5 .postMessage:

https://easyxdm.net/

Он использует оболочку VBObject и все типы вещей, с которыми вы никогда не захотите иметь дело, для отправки междоменных сообщений между окнами или фреймами, где window.postMessage не работает для различных версий IE (и, возможно, край, все еще не уверен на 100% в поддержке Edge имеет, но, похоже, также нуждается в обходном пути для .postMessage)

OG Шон
источник
-3

MessageChannel не работает для IE 9-11 между окнами / вкладками, поскольку он полагается на postMessage, который все еще не работает в этом сценарии. «Лучший» обходной путь - вызвать функцию через window.opener (например, window.opener.somefunction («somedata»)).

Подробнее об обходном пути здесь

user1337489
источник
1
Это не работает в настройках перекрестного происхождения, что является одним из предварительных условий в вопросе.
PhistucK