HTML 5 Web Messaging: window.postMessage

Web Messaging, Cross-origin Messaging o Cross-document messaging son los nombres por los que se reconocen a esta Api de HTML 5, ya en su versión Candidate Recommendation a Mayo del 2012. El objetivo de la misma es proporcionar al desarrollador un mecanismo para la comunicación entre dos contextos del navegador, ya sea entre un documento y un iframe o una nueva ventana. Los navegadores web, por razones de seguridad, previenen la interacción a través de script, con el objetivo de no afectar a un sitio web con el contenido, malicioso o no, de nuestro sitio o viceversa.

¿Cómo funciona?

El mecanismo de mensajes entre dos contextos es bastante sencillo, donde sólo debemos tener en cuenta dos escenarios: el envío y la recepción de mensajes. Para el primero de los casos actuaremos de la siguiente manera:

En primer lugar, debemos comenzar la comunicación en cualquiera de los dos extremos. Para ello, he creado una página HTML que tiene como único contenido la referencia a mi script source.js:

<!DOCTYPE html>
<html>
<head>
    <title>Cross-origin Communication - Sender</title>
</head>
<body>
    <script src="scripts/source.js"></script>
</body>
</html>
var domain = 'http://localhost:23907';
var myPopup = window.open(domain + '/listener.html', 'Listener Window');
var counter = 1;
setInterval(function () {
    var message = 'Hello guest number ' + counter + '!';
    console.log('Sending message: ' + message);
    myPopup.postMessage(message, domain);
    counter++;
}, 5000);
window.addEventListener('message', function (event) {
    if (event.origin !== domain) return;
    console.log('Received response: ', event.data);
}, false);

El código JavaScript que se presenta es bastante sencillo: se ha almacenado la url de la aplicación en una variable, se crea una nueva ventana donde se muestra la página listener.html (nuestro receptor) y por último se inicializa un contador. Se crea una función anónima dentro de un intervalo de 5 segundos que nos permite mandar a la ventana recientemente creada un mensaje con el número actual del contador. En este punto estaríamos mandando mensajes a nuestra segunda ventana a través de [myPopUp].postMessage.

Como podemos ver en el ejemplo, postMessage recibe dos parámetros: el mensaje (un string, objeto, array, etcétera), el dominio del receptor (podría ser un * pero no es recomendable por seguridad) y podría recibir un tercer parámetro opcional reconocido en la especificación como transfer, el cual se utiliza para Channel Messaging, no abarcado en este post.

¿Puedo esperar contestación? Por supuesto 😀 para ello debemos manejar el evento message de window y recibiremos como parámetro una serie de valores:

event.data
Devuelve la información que se envió a través de postMessage desde el emisor del mensaje.
event.origin
Se trata de la información relativa al remitente: schema, hostname y puerto. Por ejemplo: http://localhost:23907
event.source
Nos da acceso a la propiedad window de la fuente del evento.
event.lastEventId
Cadena con un identificador único del mensaje.
event.ports
Se devuelve un array con los puertos para channel messaging.

El resultado que podemos esperar como parte del registro en consola es el siguiente:

window.postmessage-source

Una vez que ya tenemos el emisor estamos listos para implementar al receptor 🙂 Para ello basta con escuchar también el evento message en el otro extremo:

//respond to events
window.addEventListener('message', function (event) {
    if (event.origin != 'http://localhost:23907') return;
    console.log('message received: ' + event.data, event);
    event.source.postMessage('hello back!!', event.origin);
}, false);

Lo que hacemos en primer lugar es comprobar el origen/responsable de la invocación de este evento. Es muy importante validar esta información antes de procesar el contenido por motivos de seguridad. Una vez verificado, mostramos por consola el contenido del mensaje, además de adjuntar el objeto event para poder visualizar un contenido similar al siguiente:

window.postmessage-target

Compatibilidad

A día de hoy podemos disfrutar de esta Api en Internet Explorer 8+, Chrome, Opera, Safari y Maxthon.

Espero que sea de utilidad 🙂

¡Saludos!