HTML5 - Web Messaging
HTML5 - Web Messaging
Section titled “HTML5 - Web Messaging”Web messaging defines mechanisms that allow scripts in different browsing contexts (like separate windows, tabs, iframes, or workers) to communicate with each other, even if they originate from different origins (domains, protocols, or ports), overcoming the limitations of the Same-Origin Policy in a controlled way.
This enables secure cross-document and cross-origin communication, essential for scenarios like:
- A page communicating with an embedded
<iframe>from a different domain (e.g., interacting with an embedded map, video player, or advertisement). - A page opening a new window (
window.open()) and exchanging data with it. - A page communicating with its Web Workers or Service Workers.
- Different
<iframe>elements on the same page communicating with each other via the parent page.
Cross-Document Messaging (postMessage)
Section titled “Cross-Document Messaging (postMessage)”The most common form of web messaging uses the window.postMessage() method. It allows a window (or frame, or worker) to safely send data messages to another window (or frame, or worker).
Sending Messages
Section titled “Sending Messages”To send a message, you need a reference to the target window object (otherWindow) and call its postMessage method:
otherWindow.postMessage(message, targetOrigin, [transfer]);message: The data to send. This can be any object that can be structured cloned (strings, numbers, booleans, arrays, objects, Blobs, ArrayBuffers, etc.).targetOrigin: A crucial security parameter. It specifies the origin (e.g.,'https://example.com','*') that the target window must have for the message to be delivered. Use'*'only if you don’t need to restrict the recipient origin (less secure). Always specify a specific origin when sending sensitive data.transfer(Optional): An array of Transferable objects (likeArrayBuffer,MessagePort,ImageBitmap) whose ownership is transferred to the receiving context, potentially improving performance by avoiding copying large data.
Receiving Messages
Section titled “Receiving Messages”To receive messages, the target window needs to listen for the message event:
window.addEventListener('message', receiveMessage, false);
function receiveMessage(event) { // 1. SECURITY CHECK: Verify the origin of the sender if (event.origin !== 'https://expected-sender.com') { console.warn('Message received from unexpected origin:', event.origin); return; // Ignore messages from untrusted origins }
// 2. Access the data const receivedData = event.data; console.log('Message received:', receivedData);
// 3. Optional: Access the source window (for sending replies) const sourceWindow = event.source; if (sourceWindow) { // Example: Send a reply back sourceWindow.postMessage('Message received successfully!', event.origin); }
// 4. Optional: Access transferred MessagePorts (see Channel Messaging) if (event.ports && event.ports.length > 0) { // Handle MessagePorts }
// Process the receivedData...}The event object in the handler provides:
data: The actual message object sent bypostMessage.origin: The origin of the window that sent the message (e.g.,'https://sender.com'). Crucial for security checks.source: A reference to thewindowobject that sent the message. Useful for sending replies.ports: An array ofMessagePortobjects transferred with the message (used for Channel Messaging).
Example: Page Communicating with an Iframe
Section titled “Example: Page Communicating with an Iframe”parent.html (Main Page):
<!DOCTYPE html><html><head><title>Parent Page</title></head><body> <h1>Parent Page</h1> <iframe id="childFrame" src="https://other-domain.com/child.html" width="400" height="200"></iframe> <button onclick="sendMessageToChild()">Send Message to Iframe</button> <div id="response"></div>
<script> const childFrame = document.getElementById('childFrame'); const responseDiv = document.getElementById('response'); const childOrigin = 'https://other-domain.com'; // IMPORTANT: Set the correct origin
function sendMessageToChild() { const message = { text: 'Hello from parent!', timestamp: Date.now() }; // Get the iframe's contentWindow if (childFrame.contentWindow) { console.log(`Sending message to ${childOrigin}:`, message); childFrame.contentWindow.postMessage(message, childOrigin); } }
// Listen for messages from the child iframe window.addEventListener('message', (event) => { // SECURITY CHECK! if (event.origin !== childOrigin) { console.warn('Ignoring message from unexpected origin:', event.origin); return; } console.log('Message received from iframe:', event.data); responseDiv.textContent = `Response from iframe: ${JSON.stringify(event.data)}`; }); </script></body></html>child.html (Iframe Content - hosted on https://other-domain.com):
<!DOCTYPE html><html><head><title>Child Iframe</title></head><body style="background-color: lightblue;"> <h2>Iframe Content</h2> <div id="received">Waiting for message...</div>
<script> const receivedDiv = document.getElementById('received'); const parentOrigin = 'https://parent-domain.com'; // IMPORTANT: Set the correct origin of the parent
window.addEventListener('message', (event) => { // SECURITY CHECK! if (event.origin !== parentOrigin) { console.warn('Ignoring message from unexpected origin:', event.origin); return; }
console.log('Message received from parent:', event.data); receivedDiv.textContent = `Received: ${JSON.stringify(event.data)}`;
// Send a reply back to the parent window (event.source) if (event.source) { const reply = { confirmation: 'Message processed by iframe!' }; console.log(`Sending reply to ${event.origin}:`, reply); event.source.postMessage(reply, event.origin); } }); </script></body></html>Channel Messaging (MessageChannel)
Section titled “Channel Messaging (MessageChannel)”Channel Messaging provides a more direct, two-way communication channel between two browsing contexts. It uses the MessageChannel API.
Creating a MessageChannel instance gives you two connected MessagePort objects (port1 and port2). One port remains in the originating context, while the other port is transferred to the target context using the optional third argument of postMessage.
Once the port is transferred, both contexts can communicate directly through their respective ports using port.postMessage() and listening with port.onmessage (or port.addEventListener('message', ...)). This avoids the need for window references and origin checks for subsequent messages on that specific channel.
Conceptual Example: Page to Iframe using MessageChannel
Section titled “Conceptual Example: Page to Iframe using MessageChannel”parent.html:
// ... (get iframe reference as before)
function setupChannel() { const channel = new MessageChannel(); const port1 = channel.port1; const port2 = channel.port2;
// Send port2 to the iframe, transferring its ownership if (childFrame.contentWindow) { console.log(`Sending port2 to ${childOrigin}`); childFrame.contentWindow.postMessage('init-channel', childOrigin, [port2]); }
// Listen for messages on port1 (from the iframe) port1.onmessage = (event) => { console.log('Message received via channel port1:', event.data); responseDiv.textContent = `Channel response: ${JSON.stringify(event.data)}`;
// Send message back via channel port1.postMessage({ reply: 'Got your channel message!' }); };
// Start the port (needed for onmessage to fire) port1.start();}
// Call setupChannel() when iframe is loaded, e.g.:// childFrame.addEventListener('load', setupChannel);child.html:
// ... (setup parentOrigin)let channelPort = null;
window.addEventListener('message', (event) => { // SECURITY CHECK on origin! if (event.origin !== parentOrigin) return;
// Check if this is the channel initiation message if (event.data === 'init-channel' && event.ports && event.ports.length > 0) { channelPort = event.ports[0]; console.log('Received channel port2');
// Listen for messages on the received port channelPort.onmessage = (portEvent) => { console.log('Message received via channel port2:', portEvent.data); receivedDiv.textContent = `Channel msg: ${JSON.stringify(portEvent.data)}`; };
// Start the port channelPort.start();
// Send an initial message back through the channel channelPort.postMessage({ status: 'Channel ready in iframe!' }); } else { // Handle regular postMessage events if needed console.log('Regular message received from parent:', event.data); }});Channel Messaging is particularly useful for establishing dedicated communication lines, especially when dealing with multiple iframes or workers, as it avoids potential conflicts with general window message listeners.