Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
143023864X_HT5.pdf
Скачиваний:
8
Добавлен:
21.02.2016
Размер:
7.98 Mб
Скачать

CHAPTER 6 USING THE COMMUNICATION APIS

a trusted source, it should be treated with the same caution as any other external input. The following two examples show a method of injecting content that can lead to trouble, as well as a safer alternative.

//Dangerous: e.data is evaluated as markup! element.innerHTML = e.data;

//Better

element.textContent = e.data;

As a best practice, never evaluate strings received from third parties. Furthermore, avoid using eval with strings originating from your own application. Instead, you can use JSON with window.JSON or the json.org parser. JSON is a data language that is meant to be safely consumed by JavaScript, and the json.org parser is designed to be paranoid.”

Browser Support for Cross Document Messaging

All major browsers, including Internet Explorer 8 and later, support the postMessage API. It is always a good idea to first test if HTML5 Cross Document Messaging is supported, before you use it. The section “Checking for Browser Support” later in this chapter will show you how you can programmatically check for browser support.

Using the postMessage API

In this section, we’ll explore the use of the HTML5 postMessage API in more detail.

Checking for Browser Support

Before you call postMessage, it is a good idea to check if the browser supports it. The following example shows one way to check for postMessage support:

if (typeof window.postMessage === “undefined”) { // postMessage not supported in this browser

}

Sending Messages

To send messages, invoke postMessage on the target window object, as shown in the following example:

window.postMessage(“Hello, world”, “portal.example.com”);

The first argument contains the data to send, and the second argument contains the intended target. To send messages to iframes, you can invoke postMessage on the iframe’s contentWindow, as shown in the following example:

document.getElementsByTagName(“iframe”)[0].contentWindow.postMessage(“Hello, world”, “chat.example.net”);

139

CHAPTER 6 USING THE COMMUNICATION APIS

Listening for Message Events

A script receives messages by listening for events on the window object, as shown in Listing 6-2. In the event listener function, the receiving application can decide to accept or ignore the message.

Listing 6-2. Listening for Message Events and Comparing Origins Against a Whitelist

var originWhiteList = [“portal.example.com”, “games.example.com”, “www.example.com”];

function checkWhiteList(origin) {

for (var i=0; i<originWhiteList.length; i++) { if (origin === originWhiteList[i]) {

return true;

}

}

return false;

}

function messageHandler(e) { if(checkWhiteList(e.origin)) {

processMessage(e.data);

}else {

//ignore messages from unrecognized origins

}

window.addEventListener(“message”, messageHandler, true);

Note The MessageEvent interface defined by HTML5 is also part of HTML5 WebSockets and HTML5 Web Workers. The communication features of HTML5 have consistent APIs for receiving messages. Other communication APIs, such as the EventSource API and Web Workers, also use MessageEvent to deliver messages.

Building an Application Using the postMessage API

Let’s say that you wanted to build the aforementioned portal application with its cross-origin chat widget. You can use Cross Document Messaging to communicate between the portal page and the chat widget, as shown in Figure 6-4.

140

CHAPTER 6 USING THE COMMUNICATION APIS

Figure 6-4. Portal page with cross-origin chat widget iframe

In this example, we show how a portal might embed widgets from third parties in iframes. Our example shows a single widget from chat.example.net. The portal page and widget then communicate using postMessage. In this case, the iframe represents a chat widget that wants to notify the user by blinking the title text. This is a common UI technique found in applications that receive events in the background. However, because the widget is isolated in an iframe served from a different origin than the parent page, changing the title would be a security violation. Instead, the widget uses postMessage to request that the parent page perform the notification on its behalf.

The example portal also sends messages to the iframe to inform the widget that the user has changed his or her status. Using postMessage in this way allows a portal such as this to coordinate with widgets across the combined application. Of course, because the target origin is checked when the message is sent, and the event origin is checked when it is received, there is no chance that data leaks out accidentally or is spoofed.

Note In this example application, the chat widget is not connected to a live chat system, and notifications are driven by the application’s users clicking Send Notification. A working chat application could use Web Sockets, as described in Chapter 7.

141

CHAPTER 6 USING THE COMMUNICATION APIS

For the sake of illustration, we created a few simple HTML pages: postMessagePortal.html and postMessageWidget.html. The following steps highlight the important parts of building the portal page and the chat widget page. The sample code for the following examples is located in the code/communication folder.

Building the Portal Page

First, add the chat widget iframe hosted at the different origin:

<iframe id="widget" src="http://chat.example.net:9999/postMessageWidget.html"></iframe>

Next, add an event listener messageHandler to listen for message events coming from the chat widget. As shown in the following example code, the widget will ask the portal to notify the user, which can be done by flashing the title. To make sure the message comes from the chat widget, the message’s origin is verified; if it does not come from http://chat.example.net:9999, the portal page simply ignores it.

var trustedOrigin = "http://chat.example.net:9999";

function messageHandler(e) {

if (e.origin == trustedOrigin) { notify(e.data);

}else {

//ignore messages from other origins

}

Next, add a function to communicate with the chat widget. It uses postMessage to send a status update to the widget iframe contained in the portal page. In a live chat application, it could be used to communicate the user’s status (available, away, and so on).

function sendString(s) { document.getElementById("widget").contentWindow.postMessage(s, targetOrigin);

}

Building the Chat Widget Page

First, add an event listener messageHandler to listen for message events coming from the portal page. As shown in the following example code, the chat widget listens for incoming status-change messages. To make sure the message comes from the portal page, the message’s origin is verified; if it does not come from http://portal.example.com:9999, the widget simply ignores it.

var trustedOrigin = "http://portal.example.com:9999"; function messageHandler(e) {

if (e.origin === trustedOrigin { document.getElementById("status").textContent = e.data;

}else {

//ignore messages from other origins

142

CHAPTER 6 USING THE COMMUNICATION APIS

}

Next, add a function to communicate with the portal page. The widget will ask the portal to notify the user on its behalf and uses postMessage to send a message to the portal page when a new chat message is received, as shown in the following example:

function sendString(s) { window.top.postMessage(s, trustedOrigin);

}

The Final Code

Listing 6-3 shows the complete code for the Portal page postMessagePortal.html.

Listing 6-3. Contents of postMessagePortal.html

<!DOCTYPE html>

<title>Portal [http://portal.example.com:9999]</title> <link rel="stylesheet" href="styles.css">

<style> iframe {

height: 400px; width: 800px;

}

</style>

<link rel="icon" href="http://apress.com/favicon.ico"> <script>

var defaultTitle = "Portal [http://portal.example.com:9999]"; var notificationTimer = null;

var trustedOrigin = "http://chat.example.net:9999";

function messageHandler(e) {

if (e.origin == trustedOrigin) { notify(e.data);

}else {

//ignore messages from other origins

}

function sendString(s) { document.getElementById("widget").contentWindow.postMessage(s, trustedOrigin);

}

function notify(message) { stopBlinking(); blinkTitle(message, defaultTitle);

}

143

CHAPTER 6 USING THE COMMUNICATION APIS

function stopBlinking() {

if (notificationTimer !== null) { clearTimeout(notificationTimer);

}

document.title = defaultTitle;

}

function blinkTitle(m1, m2) { document.title = m1;

notificationTimer = setTimeout(blinkTitle, 1000, m2, m1)

}

function sendStatus() {

var statusText = document.getElementById("statusText").value; sendString(statusText);

}

function loadDemo() {

document.getElementById("sendButton").addEventListener("click", sendStatus, true); document.getElementById("stopButton").addEventListener("click", stopBlinking, true); sendStatus();

}

window.addEventListener("load", loadDemo, true); window.addEventListener("message", messageHandler, true);

</script>

<h1>Cross-Origin Portal</h1>

<p><b>Origin</b>: http://portal.example.com:9999</p> Status <input type="text" id="statusText" value="Online"> <button id="sendButton">Change Status</button>

<p>

This uses postMessage to send a status update to the widget iframe contained in the portal page.

</p>

<iframe id="widget" src="http://chat.example.net:9999/postMessageWidget.html"></iframe> <p>

<button id="stopButton">Stop Blinking Title</button>

</p>

Listing 6-4 shows the code for the portal page postMessageWidget.html.

Listing 6-4. Contents of postMessageWidget.html

<!DOCTYPE html> <title>widget</title>

<link rel="stylesheet" href="styles.css"> <script>

var trustedOrigin = "http://portal.example.com:9999";

144

CHAPTER 6 USING THE COMMUNICATION APIS

function messageHandler(e) {

if (e.origin === "http://portal.example.com:9999") { document.getElementById("status").textContent = e.data;

}else {

//ignore messages from other origins

}

function sendString(s) { window.top.postMessage(s, trustedOrigin);

}

function loadDemo() { document.getElementById("actionButton").addEventListener("click",

function() {

var messageText = document.getElementById("messageText").value; sendString(messageText);

}, true);

}

window.addEventListener("load", loadDemo, true); window.addEventListener("message", messageHandler, true);

</script>

<h1>Widget iframe</h1>

<p><b>Origin</b>: http://chat.example.net:9999</p>

<p>Status set to: <strong id="status"></strong> by containing portal.<p>

<div>

<input type="text" id="messageText" value="Widget notification."> <button id="actionButton">Send Notification</button>

</div>

<p>

This will ask the portal to notify the user. The portal does this by flashing the title. If the message comes from an origin other than http://chat.example.net:9999, the portal page will ignore it.

</p>

The Application in Action

To see this example in action, there are two prerequisites: the pages have to be served up by a web server and the pages have to be served up from two different domains. If you have access to multiple web servers (for example, two Apache HTTP servers) on separate domains, you can host the example files on those servers and run the demo. Another way to accomplish this on your local machine is to use Python SimpleHTTPServer, as shown in the following steps.

1.Update the path to the Windows hosts file (C:\Windows\system32\drivers\etc\hosts) and the Linux version (/etc/hosts) by adding two entries pointing to your localhost (IP address 127.0.0.1), as shown in the following example:

145