Le message postMessage d'origine croisée est-il interrompu dans IE10?

91

J'essaie de faire fonctionner un postMessageexemple trivial ...

  • dans IE10
  • entre les fenêtres / onglets (par rapport aux iframes)
  • à travers les origines

Supprimez l'une de ces conditions et les choses fonctionnent bien :-)

Mais pour autant que je sache, entre les fenêtres postMessagene semble fonctionner dans IE10 que lorsque les deux fenêtres partagent une origine. (Eh bien, en fait - et bizarrement - le comportement est légèrement plus permissif que cela: deux origines différentes qui partagent un hôte semblent également fonctionner).

Est-ce un bogue documenté? Des solutions de contournement ou d'autres conseils?

(Remarque: cette question touche aux problèmes, mais sa réponse concerne IE8 et IE9 - pas 10)


Plus de détails + exemple ...

démo de la page du lanceur

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

Démo de la page lancée

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

Cela fonctionne à: http://jsbin.com/ahuzir/1 - car les deux pages sont hébergées à la même origine (jsbin.com). Mais déplacez la deuxième page n'importe où ailleurs, et cela échoue dans IE10.

Étalages
la source
5
Veuillez envisager de remplacer la réponse acceptée par celle qui répond à la question plutôt que celle qui répertorie MessageChannel comme votre meilleur choix lorsque MessageChannel nécessite postMessage pour le faire fonctionner. J'ai passé plus d'une heure à jouer avec MessageChannel pour constater que la seule solution viable est un proxy iframe.
Akrikos
1
Si votre window.open n'est qu'une boîte de dialogue contextuelle, vous pouvez l'éviter complètement et utiliser un iframe dans un modal js. Quelque chose comme jQuery Dialog ou Bootstrap Modal est la façon dont je l'ai implémenté. Ensuite, vous pouvez utiliser window.parent.postMessagedans IE.
styfle
@Bosh car ma réponse semble fonctionner en 2018 et ne nécessite pas de cadre proxy, pourriez-vous en définir une comme réponse acceptée car elle semble nous aider, malheureux qui doivent encore soutenir l'ancien ie
Bruno Laurinec

Réponses:

62

Je me suis trompé lorsque j'ai initialement publié cette réponse: cela ne fonctionne pas réellement dans IE10. Apparemment, les gens ont trouvé cela utile pour d'autres raisons, alors je laisse ça à la postérité. Réponse originale ci-dessous:


À noter: le lien dans cette réponse que vous avez lié à des états qui postMessagene sont pas d'origine croisée pour des fenêtres séparées dans IE8 et IE9 - cependant, il a également été écrit en 2009, avant IE10. Je ne prendrais donc pas cela comme une indication que c'est corrigé dans IE10.

Quant à postMessagelui-même, http://caniuse.com/#feat=x-doc-messaging indique notamment qu'il est toujours cassé dans IE10, ce qui semble correspondre à votre démo. La page caniuse renvoie à cet article , qui contient une citation très pertinente:

Internet Explorer 8+ prend partiellement en charge la messagerie entre documents: il fonctionne actuellement avec les iframes, mais pas avec les nouvelles fenêtres. Cependant, Internet Explorer 10 prend en charge MessageChannel. Firefox prend actuellement en charge la messagerie cross-document, mais pas MessageChannel.

Donc, votre meilleur pari est probablement d'avoir un MessageChannelcodepath basé, et de revenir postMessagesi cela n'existe pas. Cela ne vous offrira pas de support IE8 / IE9, mais au moins cela fonctionnera avec IE10.

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

ShZ
la source
8
Comment MessageChannelcréeriez- vous un chemin de code basé sur un code qui fonctionne? Vous devez toujours fonctionner postMessagepour obtenir le port du canal vers l'autre fenêtre.
balpha
1
L'utilisation postMessageavec la nouvelle MessageChannelAPI fonctionnera sur de nouvelles fenêtres et origines. Le travail est un peu gênant je suppose, mais fondamentalement: postMessage('foo', '*')c'est mauvais, postMessage('foo', [messageChannel.port2])c'est bien.
ShZ
3
Je ne peux pas pour la vie de moi faire fonctionner postMessage avec une fenêtre contextuelle inter-domaines en utilisant la dernière version d'IE (11) et l'API MessageChannel. Et honnêtement, je ne trouve nulle part ailleurs sur InterWebs que cette réponse indiquant que ce scénario spécifique devrait fonctionner. Quelqu'un peut-il citer un exemple prouvant que cela fonctionne? Je serais éternellement reconnaissant.
Todd Menier
4
MessageChannel ne devrait pas fonctionner pour la même raison que postMessage. Microsoft doit corriger le marshalling inter-processus. blogs.msdn.com/b/ieinternals/archive/2009/09/15/…
EricLaw
9
Cette réponse est agaçante car elle nous donne de faux espoirs. La réponse est: cela ne fonctionnera pas sans proxy car postMessage est nécessaire pour que MessageChannel fonctionne (au moins dans chaque démo que j'ai vue). À moins que quelqu'un ne me montre une démo de MessageChannel fonctionnant sans postMessage ou postMessage ('name', '<domain>', [messageChannel.port2]) travaillant sur plusieurs domaines (je n'ai pas pu le faire fonctionner), je ne le croirai pas fonctionne sans cadre proxy.
Akrikos
30

Créez une page proxy sur le même hôte que le lanceur. La page proxy a une page iframeavec source définie sur la page distante. PostMessage d'origine croisée fonctionnera désormais dans IE10 comme suit:

  • La page distante utilise window.parent.postMessagepour transmettre les données à la page proxy. Comme cela utilise des iframes, il est pris en charge par IE10
  • La page proxy utilise window.opener.postMessagepour renvoyer les données à la page de lancement. Comme il s'agit du même domaine, il n'y a pas de problèmes d'origine croisée. Il peut également appeler directement des méthodes globales sur la page de lancement si vous ne souhaitez pas utiliser postMessage - par exemple.window.opener.someMethod(data)

Exemple (toutes les URL sont fictives)

Page de lancement sur 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>

Page proxy sur 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>

Page distante sur 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
la source
Votre réponse serait meilleure si elle incluait un lien vers la page d'exemple et la page de contournement Microsoft liées à d'autres réponses. Solution de contournement: blogs.msdn.com/b/ieinternals/archive/2009/09/16 / ... Exemple (à partir de la page de contournement): debugtheweb.com/test/xdm/origin
Akrikos
De plus, votre page d'exemple renvoie désormais à une page godaddy introuvable.
Akrikos
8
hein? ... toutes les URL sont des EXEMPLES et ne sont pas destinées à pointer vers des pages existantes ... à partir de la source répertoriée, vous pouvez déterminer ce qui doit être fait pour le faire fonctionner ..
LyphTEC
Merci pour la clarification. :-)
Akrikos
29

== SOLUTION DE TRAVAIL EN 2020 sans iframe ==

En m'appuyant sur la réponse par enchevêtrement, j'ai eu du succès dans IE11 [et émulé le mode IE10] en utilisant l'extrait de code suivant:

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

Ensuite, j'ai pu communiquer en utilisant une pile postMessage typique, j'utilise un messager statique global dans mon scénario (bien que je ne suppose pas que ce soit significatif, j'attache également ma classe de messagerie)

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

Peu importe mes efforts, je n'ai pas pu faire fonctionner les choses sur IE9 et IE8

Ma configuration là où elle fonctionne:
version IE: 11.0.10240.16590, versions de mise à jour: 11.0.25 (KB3100773)

Bruno Laurinec
la source
6
Je dois juste dire - au cas où quelqu'un d'autre regarderait cette solution de contournement en pensant "pas du tout, cela ne pourrait pas résoudre le problème" - oui, cela a en fait corrigé l'incapacité de posterMessage sur window.opener dans IE11. Incroyable.
dkr88
1
Pas question ... J'étais comme 2 jours à essayer et cela "résoudre" le problème
lmiguelmh
fonctionne comme des charmes et mystérieux !! (IE10.0.9200, win7)
Simon
Pouvez-vous fournir un exemple complet pour cette solution de contournement?
msm2020
1
@SidJonnala pas vraiment, mais je le recommanderais. Si vous réaffectez immédiatement à la page distante réelle et que le chargement de votre page prend 3-4s [cela peut arriver de temps en temps], vous risquez que votre page window.open ('/') se charge et déroute l'utilisateur
Bruno Laurinec
2

En s'appuyant sur les réponses de LyphTEC et Akrikos, une autre solution consiste à créer une <iframe>fenêtre contextuelle vide dans une fenêtre contextuelle, ce qui évite d'avoir à utiliser une page proxy séparée, car la fenêtre contextuelle vide a la même origine que son ouvreur.

Page de lancement sur 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>

Page distante sur 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>

Je ne sais pas à quel point c'est fragile, mais cela fonctionne dans IE 11 et Firefox 40.0.3.

enchevêtrement
la source
1
... et maintenant cela ne fonctionne pas (échec silencieux dans la fenêtre contextuelle vers la <iframe>direction) dans IE 11 ( 11.0.9600.18036, versions de mise à jour 11.0.23 (KB3087038)). Peut - être la récente mise à jour de sécurité ( KB3087038 est impliqué).
enchevêtrement du
1

Pour le moment (02/09/2014), votre meilleur pari est d'utiliser un cadre proxy comme indiqué dans le billet de blog msdn qui détaille une solution de contournement pour ce problème: https://blogs.msdn.microsoft.com/ieinternals/2009 / 09/15 / html5-implementation-issues-in-ie8-and-later /

Voici l'exemple de travail: http://www.debugtheweb.com/test/xdm/origin/

Vous devez configurer un cadre proxy sur votre page qui a la même origine que le popup. Envoyez les informations de la fenêtre contextuelle au cadre proxy en utilisant window.opener.frames[0]. Ensuite, utilisez postMessage depuis le cadre proxy vers la page principale.

Akrikos
la source
1

Cette solution consiste à ajouter le site aux sites de confiance d'Internet Explore et non aux sites intranet locaux. J'ai testé cette solution sous Windows 10 / IE 11.0.10240.16384, Windows 10 / Microsoft Edge 20.10240.16384.0 et Windows 7 SP1 / IE 10.0.9200.17148. La page ne doit pas être incluse dans la zone intranet .

Alors ouvrez la configuration d'Internet Explorer (Outils> Options Internet> Sécurité> Sites de confiance> Sites), et ajoutez la page, ici j'utilise * pour faire correspondre tous les sous-domaines. Assurez-vous que la page n'est pas répertoriée dans les sites intranet locaux (Outils> Options Internet> Sécurité> Intranet local> Sites> Avancé). Redémarrez votre navigateur et testez à nouveau.

Ajouter aux sites de confiance dans Internet Explorer

Dans Windows 10 / Microsoft Edge, vous trouverez cette configuration dans Panneau de configuration> Options Internet.

METTRE À JOUR

Si cela ne fonctionne pas, vous pouvez essayer de réinitialiser tous vos paramètres dans Outils> Options Internet> Paramètres avancés> Réinitialiser les paramètres d'Internet Explorer puis Réinitialiser: utilisez-le avec prudence ! Ensuite, vous devrez redémarrer votre système. Après cela, ajoutez les sites aux sites de confiance.

Voyez dans quelle zone se trouve votre page dans Fichier> Propriétés ou en utilisant le clic droit.

Propriétés de la page dans Internet Explorer

METTRE À JOUR

Je suis dans un intranet d'entreprise et parfois cela fonctionne et parfois non (configuration automatique? J'ai même commencé à blâmer le proxy d'entreprise). À la fin, j'ai utilisé cette solution https://stackoverflow.com/a/36630058/2692914 .

lmiguelmh
la source
0

Ce Q est ancien mais c'est à cela que sert easyXDM, peut-être le vérifier comme une solution de secours potentielle lorsque vous détectez un navigateur qui ne prend pas en charge html5 .postMessage:

https://easyxdm.net/

Il utilise le wrapper VBObject et tous les types de choses que vous ne voudriez jamais avoir à gérer pour envoyer des messages interdomaines entre des fenêtres ou des cadres où window.postMessage échoue pour diverses versions d'IE (et peut-être Edge, toujours pas sûr à 100% du support Edge a, mais il semble également avoir besoin d'une solution de contournement pour .postMessage)

OG Sean
la source
-3

MessageChannel ne fonctionne pas pour IE 9-11 entre les fenêtres / onglets car il repose sur postMessage, qui est toujours interrompu dans ce scénario. La "meilleure" solution de contournement est d'appeler une fonction via window.opener (c'est-à-dire window.opener.somefunction ("somedata")).

Solution de contournement plus en détail ici

utilisateur1337489
la source
1
Cela ne fonctionne pas dans les paramètres d'origine croisée, ce qui est l'un des prérequis de la question.
PhistucK