web-dev-qa-db-fra.com

postMessage toujours cassé sur IE11?

Il semble que window.postMessage soit toujours cassé sur IE 11 lorsque le message est

  • entre une fenêtre et un popup/onglet enfant avec window.open
  • quand il est envoyé depuis différents domaines [ou le même domaine dans certains cas, c.f. mise à jour 16/01]

Il y avait des problèmes similaires avec IE 8/9/10 mais cette fonctionnalité a été marquée comme "prise en charge" dans IE 11 à partir de "partiellement prise en charge" dans IE 10

Il y a un exemple du code qui fonctionne sur chrome/ff mais pas sur IE:

Le ouvre (jsfiddle) :

$(document).ready(function() {
    $('#log').append('listening...');
    window.addEventListener("message", function(e){
        $('#log').append("Received message: " + JSON.stringify(e.data));
    }, false);
    $('button').click(function() {
        window.open('http://jsbin.com/eQeSeros/1', 'popup','menubar=no, status=no, scrollbars=no, menubar=no, width=200, height=100');
    });
});

Le popup enfant (jsbin) : (ne fonctionnera pas s'il n'est pas ouvert par jsfiddle)

$(document).ready(function() {
   $('body').append('sending...');
   window.opener.postMessage("Hello?", "http://fiddle.jshell.net");
   $('body').append('sent...');
});

J'ai lu l'article Le postMessage cross-Origin est-il cassé dans IE10? que nous pouvons utiliser un MessageChannel au lieu de postMessage, mais en lisant le doc, je n'ai pas trouvé comment l'utiliser dans mon cas réel, car vous devez passer le port à la fenêtre enfant.

Il y a une chaîne de redirection avant que je doive envoyer mon message, donc même si je pouvais envoyer un port, je perdrais tout objet js envoyé initialement/avant les redirections.

Une idée pour un remplacement?

Mise à jour 14/01: Je pense transmettre mes données dans le titre de la fenêtre/de l’onglet et vérifier régulièrement ce titre auprès du parent ... mais ce serait un tour assez sale.

Mise à jour 16/01: Le problème, c’est que le message est cassé même si le message est envoyé depuis le même domaine, mais après avoir été redirigé par un autre domaine.

voici l'exemple: http://jsfiddle.net/L4YzG/13/ ouvre la fenêtre contextuelle http://jsbin.com/eQeSeros/4/edit qui redirige vers http : //jsfiddle.net/mxS8Q/2/ (qui poste le message)

Si vous modifiez le popup directement par la dernière URL redirigée vers http://jsfiddle.net/mxS8Q/2/show cela fonctionne sur IE car il n'y a pas d'autre domaine entre open & post

Je travaille toujours sur le sale tour de mon titre de fenêtre. nous ne pouvons pas recevoir le titre de la fenêtre lorsqu'elle se trouve sur un autre domaine, mais si elle revient sur jsfiddle, le titre est disponible (il n'y a pas le problème précédent avec postMessage). Voici l'exemple: http://jsfiddle.net/L4YzG/14/ ... C'est peut-être une solution alternative, mais je viens de voir quelque chose à propos de la transmission des données dans un cookie, il doit simplement être testé .

Mise à jour 04/02: Passer les infos dans le titre ne suffit pas, si cela fonctionne bien si les domaines finaux sont les mêmes mais ne sont pas inter-domaines. Je voulais injecter un iframe du même domaine pour transmettre ces informations, mais je ne peux pas partager l'objet window enfant non plus (postMessage a besoin d'un objet sérialisable).

Enfin, j'ai essayé de partager un cookie (créé et reçu en js) entre l'iframe injecté et la fenêtre enfant, cela fonctionne bien sur chrome & ff mais je ne pouvais toujours pas le recevoir correctement avec IE. Après avoir ajouté les en-têtes P3P, cela a bien fonctionné, cela semble être la vraie solution. Safari semble avoir quelques problèmes avec cette technique, je la garde donc comme solution de secours.

24
bumpmann

Mise à jour 16/01: Le problème, c'est que le message est cassé même si le message est envoyé depuis le même domaine, mais après avoir été redirigé par un autre domaine.

De manière hilarante, cette "fonction de sécurité" peut être utilisée à l’inverse pour contourner totalement la restriction interdomaine.

Dans la fenêtre parente à example.com:

<script>
  window.open("http://example.com/dummy_redirect");
  window.addEventListener('message', function(ev) {console.log(ev.data)})
</script>

Sur le serveur example.com:

GET /dummy_redirect 302 http://jsfiddle.net/b6yfbunw/

Une fenêtre s'ouvrira sur votre domaine, se redirigera vers jsfiddle et l'appel postMessage fonctionnera dans IE. Vous pouvez même accéder à n'importe quel domaine après cela et continuer à effectuer des appels postMessage vers la fenêtre parente.

14
Neil Sarkar

Est-ce cassé? Eh bien, en quelque sorte.

J'ai essayé diverses idées et je n'ai pas pu obtenir le code dans votre jsFiddle au travail. En examinant cet article de blog MSDN }, nous constatons que postMessage ne fonctionne qu'entre des IFrames dans des versions plus anciennes d'IE, ce qui n'a pas encore été corrigé pour IE 11.

Cet article est lié à une démo du problème. Il existe quelques solutions de contournement impliquant l’appel de scripts sur le window.opener. Cependant, comme l'indique ce blog (c'est moi qui souligne):

Malheureusement, cette solution de contournement est souvent impossible, car same-Origin-policy indique que la fenêtre contextuelle et la page window.opener doivent appartenir à la même origine pour pouvoir appeler les fonctions de script de l'autre.

Il semble donc que la seule façon de faire cela ressemble à quelque chose comme this , où l'enfant est hébergé dans un IFrame du parent. J'ai créé une démo similaire ici , basée sur le code contenu dans le vôtre. C'est assez simple, mais envoie un message à la contentWindow du IFrame, qui à son tour répond.

Je vois la recommandation d’utiliser plutôt MessageChannel, mais je me demande également si l’utilisation de Web Workers devrait mériter d’être explorée, bien que leur utilisation dépende bien entendu de la nature de votre tâche. Il y a aussi la réponse à cette question , où l'approche IFrame a été utilisée, mais une boîte de dialogue jQuery UI a été utilisée pour l'afficher - J'imagine que vous pourriez faire la même chose avec les modaux dans Bootstrap si vous préférez que .


Pour référence:

HTML

<iframe id="iframe" src="http://jsbin.com/iLapokOS/7/"></iframe>
<div id="log"></div>
<button id="post-message-button">Post message to window</button>

Script parent

var wnd;

$(document).ready(function() {
    $('#log').append('listening...');

    wnd = $('#iframe')[0].contentWindow;

    window.addEventListener('message', function(e){
      $('#log').append('<br/>Received message: ' + JSON.stringify(e.data));
    }, false);

    $('#post-message-button').click(function() {
        if(!wnd){
            return;
        }
        $('#log').append('<br/>sending...');
        wnd.postMessage('Hello?', 'http://jsbin.com');
    });
});

Child HTML et JS

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  <script>
    $(document).ready(function() {

      window.addEventListener("message", function(e){
        $('body').append('<br/>Origin: ' + e.Origin);        
        $('body').append("<br/>Received message: " + JSON.stringify(e.data));

        e.source.postMessage('Hello yourself', e.Origin);
      }, false);
    });
  </script>


</body>
</html>
14
nick_w

Il existe quelques mentions d'une solution de contournement iframe, mais les seules que j'ai vues sont qui envoient un message à iframe.

Voici un exemple de receive un message de la iframe à la place:

Page parent (http://first-domain.com/receive-message.html)

<html>
  <head>
    <script>
      window.addEventListener('message', console.log.bind(console, 'Got message:'));
    </script>
  </head>
  <body>
    <iframe src="http://second-domain.com/send-message.html"></iframe>
  </body>
</html>

Child-page (http://second-domain.com/send-message.html)

<html>
  <head>
    <script>
      window.parent.postMessage('hi there', '*');
    </script>
  </head>
  <body></body>
</html>
3
c24w

J'ai trouvé, si je lance une autre fenêtre, lance ma fenêtre, puis ferme l'autre fenêtre, je peux travailler avec ma fenêtre très bien.

vvWindow0 = window.open("http://apsed4065:8047/virtualviewer/index.html");
vvWindow = window.open("http://apsed4065:8047/virtualviewer/index.html");
vvWindow0.close(); <!-- to close the window -->

vvWindow.postMessage(message, 'http://apsed4065:8047');

0