2016-10-12 1 views
10

J'ai une application exécutée à l'intérieur d'un iframe sur une page "étrangère" (domaine différent, etc.). Pour permettre une communication de base entre le parent iframe &, je charge un de mes scripts sur la page parente et utilise postMessage pour faire des messages inter-documents.Problèmes de messagerie documentaire croisée entre IFrame et parent

La plupart du temps, cette communication fonctionne comme prévu, mais parfois des erreurs sont signalées à mon outil de suivi des erreurs et je n'arrive pas à comprendre pourquoi elles se produisent.

Voici un code exemplaire:

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' }, '*'); 
// ... 

Qu'est-ce qui se passe ici, lorsque les coups de délai d'attente et l'erreur est signalée?

Un peu plus d'info & pensées de la mine:

  • Je ne contrôle pas sur la page parent moi-même
  • Il ne semble pas être un problème "de configuration" générale (CORS etc.) puisque l'erreur se produit sur la même page où il fonctionne la plupart du temps
  • Nous ne soutenons pas IE < 10 et d'autres versions de navigateur « anciens » du tout, donc ce sont pas question ici
  • Mon erreur outil de reporting fait état d'une multitude de différents navigateurs parmi lesquels sont les dernières versions d'entre eux (49 FF, Chrome 43 sur Android 5, Chrome 53 sur Win et Android 6, Mobile Safari 10, ...)
    • Par conséquent, il doesn Il ne semble pas que ce soit un problème lié à des navigateurs spécifiques ou à des versions.
  • Le délai d'attente de 500 ms est juste un nombre magique que j'ai choisi que je pensais être complètement sûr ...
+0

Est-ce qui se passe dans un environnement de test ou dans la nature? –

+0

Ça se passe dans la nature et je ne suis pas capable de le reproduire (du moins jusqu'à maintenant). – suamikim

+0

dans ce cas, je me demande si certains utilisateurs finissent sur votre page intérieure en dehors d'un iframe et c'est pourquoi la messagerie ne fonctionne pas? Vous pouvez ajouter du code qui vérifie que la page interne est dans un iframe, un rapide Google a donné quelques réponses SO sur la façon de le faire. –

Répondre

1

Le problème semble être dans votre PluginOnParent.js, où vous êtes envoyer votre réponse. Au lieu d'utiliser "e.origin" (qui lors de l'inspection dans les outils de développeur revenait "null") - essayez d'utiliser le littéral '*', comme il l'indique dans la documentation suivante sur postMessage usage (dans la description pour targetOrigin):

https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

en outre, comme un bonus, je viens de tester ce dans deux domaines différents et il fonctionne aussi bien. J'ai placé Parent.html sur un serveur web de domaines, et j'ai changé le src de iframe pour qu'il soit child.html sur un domaine complètement différent, et ils communiquaient ensemble très bien.

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, '*'); 
 
       } 
 
       // ... 
 
      }) 
 
\t \t } 
 
    </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>

Child.html

<html> 
 
<head> 
 
    <script type="text/javascript"> 
 
     function childInitialize() { 
 
      // ... 
 
      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 
 
        var obj = document.getElementById("status"); 
 
        obj.value = e.data.responseData; 
 
       } 
 
      } 
 

 
      timeoutId = window.setTimeout(function() { 
 
       var obj = document.getElementById("status"); 
 
       obj.value = 'Communication with parent page failed'; 
 
       window.removeEventListener('message', messageHandler); 
 
      }, 500); 
 

 
      window.addEventListener('message', messageHandler, false); 
 
      window.parent.postMessage({ type: 'iFrameRequest' }, '*'); 
 
      // ... 
 
     } 
 
    </script> 
 
</head> 
 
<body style="background-color: rgb(0, 148, 255);" onload="javascript: childInitialize();"> 
 
    <textarea type="text" style="width:400px; height:250px;" id="status" /> 
 
</body> 
 
</html>

J'espère que cela pourra aider!

+0

Malheureusement, je n'ai pas été en mesure d'essayer vos suggestions maintenant, mais comme ils semblent les plus prometteurs dans ce fil je vous donne néanmoins la générosité. De toute façon, je garderai ce numéro à jour une fois que j'en saurai plus. – suamikim

+0

Merci - aussi, au cas où vous seriez intéressé, il y avait un fil similaire à celui-ci sur lequel je me suis également attelé, mais ils voulaient une minuterie en cours communiquant entre les deux pages. J'ai mentionné cet exemple à cette personne et j'ai emprunté un peu de votre code comme exemple, et je l'ai modifié pour son scénario. Cela pourrait aussi vous être utile, alors j'ai pensé que je le mentionnerais: http://stackoverflow.com/questions/14028372/iframe-cross-domain-issue?noredirect=1&lq=1 - si rien d'autre, il montre une erreur constante -Free communication chaque seconde ou plus. –

1

La plupart du temps cette communication fonctionne comme prévu, mais parfois je voir quelques erreurs signalées à mon outil de suivi des erreurs et ne peuvent pas comprendre pourquoi ils se produisent. Que se passe-t-il ici, lorsque le délai d'expiration est atteint et que l'erreur est signalée?

Je ne contrôle pas sur la page parent moi-même

Pas certain que la fonction errorTracking.report ne lorsqu'il est appelé, mais ne semble pas qu'une erreur réelle relative à message événement se produit?

Le délai d'attente de 500 ms est juste un nombre magique que j'ai choisi que je pensais être complètement sûr ...

Avec duration ensemble à 500, setTimeout pourrait être appelé avant un événement se déclenche message au window.

timeoutId = window.setTimeout(function() { 
    errorTracking.report('Communication with parent page failed'); 
    window.removeEventListener('message', messageHandler); 
}, 500); 

Régler le duration de setTimeout à une plus grande durée.


En variante substituer gestionnaire onerror ou window.addEventListener pour setTimeout

Remarques

Quand une syntaxe (?) Erreur se produit dans un script, chargé à partir d'un different origin, les détails de l'erreur de syntaxe ne sont pas signalés à pour éviter les fuites d'informations (voir bug 363897). Au lieu de cela, l'erreur signalée est simplement "Erreur de script". Ce comportement peut être annulé dans certains navigateurs utilisant l'attribut crossorigin sur et ayant le serveur envoie les en-têtes de réponse HTTP CORS appropriés. Une solution de contournement consiste à isoler "Erreur de script". et gérer en sachant que le détail de l'erreur est visible uniquement dans la console du navigateur et non accessible par JavaScript.lors de la communication entre les différents contextes, ou origines

Par exemple

// handle errors 
onerror = function messageErrorHandlerAtAppInsideIFrame(e) { 
    console.error("Error at messageErrorIndex", e) 
} 

pour gérer les erreurs réelles.

Utilisation postMessage à l'événement load de iframe pour communiquer avec les gestionnaires message au parent window.

http://plnkr.co/edit/M85MDHF1kPPwTE2E0UGt?p=preview