Czy postMessage z różnych źródeł jest uszkodzony w IE10?

91

Próbuję sprawić, by trywialny postMessageprzykład zadziałał ...

  • w IE10
  • między oknami / kartami (w porównaniu z ramkami iframe)
  • z różnych źródeł

Usuń jeden z tych warunków i wszystko działa dobrze :-)

Ale o ile wiem, między oknami postMessagewydaje się działać tylko w IE10, gdy oba okna mają wspólne źródło. (Cóż, w rzeczywistości - i co dziwne - zachowanie jest nieco bardziej pobłażliwe: dwa różne źródła, które współdzielą hosta, też wydają się działać).

Czy to udokumentowany błąd? Jakieś obejścia lub inne porady?

(Uwaga: to pytanie dotyka problemów, ale odpowiedź dotyczy IE8 i IE9 - nie 10)


Więcej szczegółów + przykład ...

Demo strony uruchamiania

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

uruchomiono demo strony

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

Działa to pod adresem: http://jsbin.com/ahuzir/1 - ponieważ obie strony są hostowane w tym samym miejscu pochodzenia (jsbin.com). Ale przenieś drugą stronę gdziekolwiek indziej, aw IE10 nie działa.

Brednie
źródło
5
Proszę rozważyć zmianę zaakceptowanej odpowiedzi na taką, która odpowiada na pytanie, a nie taką, która wymienia MessageChannel jako najlepszy zakład, gdy MessageChannel wymaga postMessage, aby działał. Spędziłem ponad godzinę grając z MessageChannel tylko po to, aby stwierdzić, że jedynym realnym rozwiązaniem jest serwer proxy iframe.
Akrikos,
1
Jeśli twój window.open jest tylko wyskakującym oknem dialogowym, możesz go całkowicie uniknąć i użyć iframe w modalnym js. Coś w rodzaju jQuery Dialog lub Bootstrap Modal jest sposobem, w jaki to zaimplementowałem. Następnie możesz użyć window.parent.postMessagew IE.
styfle
@Bosh jako moją odpowiedź wydaje się działać w 2018 roku i nie wymaga ramki proxy, czy mogłabyś ustawienie thatone jak Zaakceptowanych odpowiedź, jak się wydaje, aby pomóc nam, nieszczęśliwych, którzy nadal muszą wspierać stare IE
Bruno Laurinec

Odpowiedzi:

62

Pomyliłem się, kiedy pierwotnie opublikowałem tę odpowiedź: tak naprawdę nie działa w IE10. Najwyraźniej ludzie uznali to za przydatne z innych powodów, więc zostawiam to potomności. Oryginalna odpowiedź poniżej:


Warto zauważyć: odsyłacz w tej odpowiedzi łączył się ze stanami, które postMessagenie są krzyżowe dla oddzielnych okien w IE8 i IE9 - jednak został również napisany w 2009 r., Zanim pojawił się IE10. Więc nie wziąłbym tego jako wskazówkę, że jest to naprawione w IE10.

Jeśli chodzi o postMessagesiebie, http://caniuse.com/#feat=x-doc-messaging w szczególności wskazuje, że nadal jest uszkodzony w IE10, który wydaje się pasować do twojego demo. Strona caniuse prowadzi do tego artykułu , który zawiera bardzo trafny cytat:

Internet Explorer 8+ częściowo obsługuje przesyłanie wiadomości między dokumentami: obecnie działa z ramkami iframe, ale nie z nowymi oknami. Jednak Internet Explorer 10 będzie obsługiwał MessageChannel. Firefox obsługuje obecnie przesyłanie wiadomości między dokumentami, ale nie obsługuje MessageChannel.

Więc najlepszym rozwiązaniem jest prawdopodobnie posiadanie MessageChannelścieżki kodowej opartej na kodach i powrót do niej, postMessagejeśli ona nie istnieje. Nie zapewni ci obsługi IE8 / IE9, ale przynajmniej będzie działać z IE10.

Dokumenty na MessageChannel: http://msdn.microsoft.com/en-us/library/windows/apps/hh441303.aspx

ShZ
źródło
8
Jak utworzyłbyś działającą MessageChannelścieżkę kodową? Nadal musisz działać, postMessageaby przenieść port kanału do innego okna.
balpha
1
Używanie postMessagez nowym MessageChannelinterfejsem API będzie działać w nowych oknach i źródłach. Wydaje mi się, że praca jest trochę niezręczna, ale w zasadzie: postMessage('foo', '*')jest zła, postMessage('foo', [messageChannel.port2])jest dobra.
ShZ
3
Nie mogę przez całe życie uzyskać postMessage działającego z wyskakującym okienkiem między domenami przy użyciu najnowszej wersji IE (11) i MessageChannel API. I szczerze mówiąc, nie mogę znaleźć nigdzie indziej w InterWebs poza tą odpowiedzią wskazującą, że ten konkretny scenariusz powinien działać. Czy ktoś może wskazać na przykład udowadniający, że to działa? Byłbym wdzięczny na zawsze.
Todd Menier
4
MessageChannel nie powinien działać z tego samego powodu, dla którego postMessage nie. Firma Microsoft musi naprawić krosowanie między procesami. blogs.msdn.com/b/ieinternals/archive/2009/09/15/…
EricLaw
9
Ta odpowiedź jest irytująca, ponieważ daje nam fałszywą nadzieję. Odpowiedź brzmi: nie zadziała bez proxy, ponieważ postMessage jest wymagane, aby MessageChannel działał (przynajmniej w każdym demo, które widziałem). O ile ktoś nie pokaże mi wersji demonstracyjnej usługi MessageChannel działającej bez postMessage lub postMessage („name”, „<domain>”, [messageChannel.port2]) działającej w wielu domenach (nie mogłem zmusić go do działania), nie uwierzę w to działa bez ramki proxy.
Akrikos
30

Utwórz stronę proxy na tym samym hoście, co program uruchamiający. Strona proxy ma iframeze źródłem ustawionym na stronę zdalną. PostMessage z innych źródeł będzie teraz działać w IE10 w następujący sposób:

  • Strona zdalna służy window.parent.postMessagedo przekazywania danych do strony proxy. Ponieważ używa ramek iframe, jest obsługiwany przez IE10
  • Strona proxy używa window.opener.postMessagedo przekazywania danych z powrotem na stronę programu uruchamiającego. Ponieważ jest to w tej samej domenie - nie ma problemów związanych z różnymi źródłami. Może również bezpośrednio wywoływać globalne metody na stronie programu uruchamiającego, jeśli nie chcesz używać postMessage - np.window.opener.someMethod(data)

Przykład (wszystkie adresy URL są fikcyjne)

Strona uruchamiania pod adresem 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>

Strona proxy pod adresem 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>

Strona zdalna pod adresem 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
źródło
Twoja odpowiedź byłaby lepsza, gdyby zawierała łącze do przykładowej strony firmy Microsoft i strony obejścia, do której odsyłacz znajduje się w innych odpowiedziach. Obejście: blogs.msdn.com/b/ieinternals/archive/2009/09/16/… Przykład (ze strony obejścia): debugtheweb.com/test/xdm/origin
Akrikos
Twoja przykładowa strona zawiera teraz linki do nieznalezionej strony godaddy.
Akrikos
8
co? ... wszystkie adresy URL są PRZYKŁADAMI i nie mają w rzeczywistości wskazywać na istniejące strony ... z podanego źródła możesz określić, co należy zrobić, aby działało ..
LyphTEC
Dziękuję za wyjaśnienie. :-)
Akrikos
29

== ROZWIĄZANIE PRACY W 2020 ROKU bez iframe ==

Opierając się na odpowiedzi przez plątaninę, odniosłem sukces w IE11 [i emulowanym trybie IE10], używając następującego fragmentu kodu:

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

Potem mogłem komunikować się za pomocą typowego stosu postMessage, w moim scenariuszu używam jednego globalnego statycznego komunikatora (choć nie przypuszczam, że ma to jakiekolwiek znaczenie, dołączam również moją klasę komunikatora)

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

Bez względu na to, jak bardzo się starałem, nie mogłem sprawić, by wszystko działało na IE9 i IE8

Moja konfiguracja, gdzie działa:
wersja IE: 11.0.10240.16590, wersje aktualizacji: 11.0.25 (KB3100773)

Bruno Laurinec
źródło
6
Muszę tylko powiedzieć - na wypadek, gdyby ktoś inny patrzył na to obejście, myśląc "nie ma mowy, to nie może tego naprawić" - tak, faktycznie naprawił niemożność wysłania wiadomości do window.opener w IE11. Nie do wiary.
dkr88
1
Nie ma mowy ... Byłem jak 2 dni próbując i to "rozwiązuje" problem
lmiguelmh
działa jak wdzięki i tajemnicze !! (IE10.0.9200, win7)
Simon
Czy możesz podać pełny przykład tego obejścia?
msm2020
1
@SidJonnala niezupełnie, ale polecam to. Jeśli natychmiast zmienisz przypisanie do rzeczywistej strony zdalnej, a załadowanie strony zajmuje 3-4 sekundy [może się to zdarzyć], ryzykujesz, że strona window.open („/”) zostanie załadowana i
zmyli
2

Opierając się na odpowiedziach LyphTEC i Akrikos, innym obejściem jest utworzenie <iframe>w pustym wyskakującym okienku, co pozwala uniknąć potrzeby oddzielnej strony proxy, ponieważ puste okienko ma to samo źródło co otwieracz.

Strona uruchamiania pod adresem 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>

Strona zdalna pod adresem 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>

Nie jestem pewien, jak delikatne jest to, ale działa w IE 11 i Firefox 40.0.3.

splot
źródło
1
... a teraz nie działa (cicha awaria w wyskakującym okienku w <iframe>kierunku) w IE 11 ( 11.0.9600.18036aktualizacja wersji 11.0.23 (KB3087038)). Prawdopodobnie chodzi o ostatnią aktualizację zabezpieczeń ( KB3087038 ).
plątanina
1

W tej chwili (02.09.2014) najlepszym rozwiązaniem jest użycie ramki proxy, jak wspomniano w poście na blogu msdn, który szczegółowo opisuje obejście tego problemu: https://blogs.msdn.microsoft.com/ieinternals/2009 / 09/15 / html5-Implementation Issues-in-ie8-and-later /

Oto działający przykład: http://www.debugtheweb.com/test/xdm/origin/

Musisz ustawić ramkę proxy na swojej stronie, która ma to samo źródło co wyskakujące okienko. Wyślij informacje z wyskakującego okienka do ramki proxy za pomocą window.opener.frames[0]. Następnie użyj postMessage z ramki proxy do strony głównej.

Akrikos
źródło
1

To rozwiązanie polega na dodaniu witryny do zaufanych witryn programu Internet Explorer, a nie do witryn w lokalnym intranecie. Testowałem to rozwiązanie w Windows 10 / IE 11.0.10240.16384, Windows 10 / Microsoft Edge 20.10240.16384.0 i Windows 7 SP1 / IE 10.0.9200.17148. Strona nie może znajdować się w strefie intranetu .

Otwórz więc konfigurację Internet Explorera (Narzędzia> Opcje internetowe> Zabezpieczenia> Zaufane witryny> Witryny) i dodaj stronę, tutaj używam * do dopasowania wszystkich subdomen. Upewnij się, że strona nie jest wyświetlana w lokalnych witrynach intranetowych (Narzędzia> Opcje internetowe> Zabezpieczenia> Lokalny intranet> Witryny> Zaawansowane). Uruchom ponownie przeglądarkę i ponownie przetestuj.

Dodaj do zaufanych witryn w przeglądarce Internet Explorer

W systemie Windows 10 / Microsoft Edge tę konfigurację znajdziesz w Panelu sterowania> Opcje internetowe.

AKTUALIZACJA

Jeśli to nie zadziała, możesz spróbować zresetować wszystkie ustawienia w menu Narzędzia> Opcje internetowe> Ustawienia zaawansowane> Zresetuj ustawienia programu Internet Explorer, a następnie Resetuj: używaj go ostrożnie ! Następnie musisz ponownie uruchomić system. Następnie dodaj witryny do zaufanych witryn.

Zobacz, w której strefie znajduje się Twoja strona w Plik> Właściwości lub klikając prawym przyciskiem myszy.

Właściwości strony w programie Internet Explorer

AKTUALIZACJA

Jestem w firmowym intranecie i czasami to działa, a czasami nie (automatyczna konfiguracja? Zacząłem nawet obwiniać korporacyjny serwer proxy). W końcu skorzystałem z tego rozwiązania https://stackoverflow.com/a/36630058/2692914 .

lmiguelmh
źródło
0

Ta Q jest stara, ale do tego właśnie służy easyXDM. Może sprawdź ją jako potencjalną rezerwę, gdy wykryjesz przeglądarkę, która nie obsługuje html5 .postMessage:

https://easyxdm.net/

Używa opakowania VBObject i wszystkich rodzajów rzeczy, z którymi nigdy nie chciałbyś mieć do czynienia, aby wysyłać wiadomości między domenami między oknami lub ramkami, w których window.postMessage nie działa dla różnych wersji IE (i być może krawędzi, nadal nie jestem pewien 100% wsparcia) Edge ma, ale wydaje się, że potrzebuje również obejścia dla .postMessage)

OG Sean
źródło
-3

MessageChannel nie działa w IE 9-11 między oknami / kartami, ponieważ opiera się na postMessage, który nadal jest uszkodzony w tym scenariuszu. „Najlepszym” obejściem jest wywołanie funkcji przez window.opener (np. Window.opener.somefunction („somedata”)).

Więcej szczegółów obejścia tutaj

user1337489
źródło
1
To nie działa w ustawieniach krzyżowych, co jest jednym z warunków wstępnych w pytaniu.
PhistucK