Posts
163
Comments
179
Trackbacks
5
Javascript & HTML 5 - Chiamate cross-domain usando iFrame, postMessage e localStorage

L’esigenza penso che possa essere comune a molti: supponiamo di avere due pagine, A e B memorizzate su domini differenti. Supponiamo di aprire B da A  e di voler refreshare A quando un determinato evento si verifica in B (ad esempio l’utente preme sul classico bottone “Salva”).

Come fare? Il solito Google ci viene in aiuto con una miriade di hack e trucchi vari che però nel nostro scenario non sono tutti applicabili. Se la pagina B viene aperta con il classico window.open, una soluzione banale è quella di impostare il document.domain di entrambe la pagine ad un valore comune. Facendo questo la pagina B può richiamare una funzione qualsiasi del chiamante usando la sintassi: window.opener.MyFunction.

Questa soluzione è efficace nei casi più semplici, ma può generare effetti spiacevoli, soprattutto se A o B (o entrambe) richiamano a loro volta altre funzioni o altre pagine. In questi casi infatti il document.domain va impostato su tutte le pagine, altrimenti avremo problemi di sicurezza in altre chiamate.

La soluzione che propongo in questo post è un po’ più complessa ed è un mix delle varie tecniche che si possono trovare in giro:

  1. Al verificarsi dell’evento desiderato nella pagina B, andremo ad impostare un valore usando il localStorage.       
               
       1: localStorage.setItem("MyProperty", 'myValue');

    Il localStorage è una delle tante novità di HTML 5 ed è supportato su tutti i browser moderni (per IE dalla versione 8 in su). In alternativa al localStorage si può usare il sessionStorage oppure per maggiore compatibilità anche i “vecchi” cookie.

    Il valore impostato sarà disponibile per il dominio B, ma non per A che quindi, se ci accede, lo troverà vuoto.

  2. Nella pagina A inseriamo un iFrame che punterà ad una nuova pagina C, memorizzata nello stesso dominio di B. La pagina C può essere una semplice pagina HTML che conterrà il seguente codice javascript:
       
       1: setInterval(function () {      
       2:     var storageValue = localStorage.getItem("MyProperty");
       3:     if (storageValue != null) {
       4:         window.parent.postMessage(storageValue, "*");
       5:         // Remove the property
       6:         localStorage.removeItem("MyProperty");
       7:     }
       8: }, 400);    

    Sostanzialmente viene creato un timer che ciclamente controlla la proprietà andando a leggere il localStorage. Nel caso venisse trovata valorizzata la proprietà cercata, verrà postato un messaggio al parent utilizzando il metodo postMessage. Il metodo consente di inviare messaggi cross domain tra le finestre del browser. Nell’esempio window.parent referenzierà la pagina A che è quella che sta aprendo C all'interno dell’iframe. Il secondo parametro del metodo postMessage è l’origine del messaggio e andrebbe valorizzato con il giusto dominio, nel nostro caso con quello della pagina A. Una volta inviato il messaggio alla pagina parent la proprietà verrà rimossa dal localStorage per evitare messaggi multipli.
  3. Nella pagina A va aggiunto un listener che rimarrà in attesa di eventuali messaggi provenienti dall’iFrame:    
       
       1: var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
       2: var eventer = window[eventMethod];
       3: var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
       4:  
       5: // Listen to message from child IFrame window
       6: eventer(messageEvent, function (e) {    
       7:     alert(e.data);
       8: }, false);

    I messaggi inviati alla pagina A vengono gestiti dal listener sull’evento onmessage (o sul vecchio message nel caso si usi attachEvent). Nell’esempio ogni qual volta B invia un messaggio, la pagina A gestirà tale messaggio e la proprietà e.data conterrà il valore MyValue. Chiaramente all’interno della funzione che riceve il messaggio è possibile implementare qualsiasi tipo di logica ed è anche possibile distinguere le eventuali origini dei messaggi utilizzando la proprietà origin dell’oggetto e.


Una soluzione simile, ma più semplice, è applicabile anche quando B è direttamente "hostata" dentro un iFrame di A. In questo caso sarà possibile scambiare messaggi tra A e B senza passare dal localStorage, ma usando direttamente la postMessage con relativa gestione attraverso il listener.

posted on martedì 4 giugno 2013 19:31 Print
Comments have been closed on this topic.
News
Se volete sapere con chi avete a che fare eccomi qui in uno "scatto" lavorativo.

La mia foto

Logo MCAD
Logo MCTS