
- •Contents at a Glance
- •Contents
- •Foreword
- •About the Authors
- •About the Technical Reviewer
- •Acknowledgments
- •Introduction
- •Who This Book Is For
- •An Overview of This Book
- •Example Code and Companion Web Site
- •Contacting the Authors
- •Overview of HTML5
- •The Story So Far—The History of HTML5
- •The Myth of 2022 and Why It Doesn’t Matter
- •Who Is Developing HTML5?
- •A New Vision
- •Compatibility and Paving the Cow Paths
- •Utility and the Priority of Constituencies
- •Interoperability Simplification
- •Universal Access
- •A Plugin–Free Paradigm
- •What’s In and What’s Out?
- •What’s New in HTML5?
- •New DOCTYPE and Character Set
- •New and Deprecated Elements
- •Semantic Markup
- •Simplifying Selection Using the Selectors API
- •JavaScript Logging and Debugging
- •window.JSON
- •DOM Level 3
- •Monkeys, Squirrelfish, and Other Speedy Oddities
- •Summary
- •Using the Canvas API
- •Overview of HTML5 Canvas
- •History
- •What Is a Canvas?
- •Canvas Coordinates
- •When Not to Use Canvas
- •Fallback Content
- •CSS and Canvas
- •Browser Support for HTML5 Canvas
- •Using the HTML5 Canvas APIs
- •Checking for Browser Support
- •Adding a Canvas to a Page
- •Applying Transformations to Drawings
- •Working with Paths
- •Working with Stroke Styles
- •Working with Fill Styles
- •Filling Rectangular Content
- •Drawing Curves
- •Inserting Images into a Canvas
- •Using Gradients
- •Using Background Patterns
- •Scaling Canvas Objects
- •Using Canvas Transforms
- •Using Canvas Text
- •Applying Shadows
- •Working with Pixel Data
- •Implementing Canvas Security
- •Building an Application with HTML5 Canvas
- •Practical Extra: Full Page Glass Pane
- •Practical Extra: Timing Your Canvas Animation
- •Summary
- •Working with Scalable Vector Graphics
- •Overview of SVG
- •History
- •Understanding SVG
- •Scalable Graphics
- •Creating 2D Graphics with SVG
- •Adding SVG to a Page
- •Simple Shapes
- •Transforming SVG Elements
- •Reusing Content
- •Patterns and Gradients
- •SVG Paths
- •Using SVG Text
- •Putting the Scene Together
- •Building an Interactive Application with SVG
- •Adding Trees
- •Adding the updateTrees Function
- •Adding the removeTree Function
- •Adding the CSS Styles
- •The Final Code
- •Summary
- •Working with Audio and Video
- •Overview of Audio and Video
- •Video Containers
- •Audio and Video Codecs
- •Audio and Video Restrictions
- •Browser Support for Audio and Video
- •Using the Audio and Video API
- •Checking for Browser Support
- •Accessibility
- •Understanding Media Elements
- •Working with Audio
- •Working with Video
- •Practical Extras
- •Summary
- •Using the Geolocation API
- •About Location Information
- •Latitude and Longitude Coordinates
- •Where Does Location Information Come From?
- •IP Address Geolocation Data
- •GPS Geolocation Data
- •Wi-Fi Geolocation Data
- •Cell Phone Geolocation Data
- •User–Defined Geolocation Data
- •Browser Support for Geolocation
- •Privacy
- •Triggering the Privacy Protection Mechanism
- •Dealing with Location Information
- •Using the Geolocation API
- •Checking for Browser Support
- •Position Requests
- •Building an Application with Geolocation
- •Writing the HTML Display
- •Processing the Geolocation Data
- •The Final Code
- •Practical Extras
- •What’s My Status?
- •Show Me on a Google Map
- •Summary
- •Using the Communication APIs
- •Cross Document Messaging
- •Understanding Origin Security
- •Browser Support for Cross Document Messaging
- •Using the postMessage API
- •Building an Application Using the postMessage API
- •XMLHttpRequest Level 2
- •Cross-Origin XMLHttpRequest
- •Progress Events
- •Browser Support for HTML5 XMLHttpRequest Level 2
- •Using the XMLHttpRequest API
- •Building an Application Using XMLHttpRequest
- •Practical Extras
- •Structured Data
- •Framebusting
- •Summary
- •Using the WebSocket API
- •Overview of WebSocket
- •Real-Time and HTTP
- •Understanding WebSocket
- •Writing a Simple Echo WebSocket Server
- •Using the WebSocket API
- •Checking for Browser Support
- •Basic API Usage
- •Building a WebSocket Application
- •Coding the HTML File
- •Adding the WebSocket Code
- •Adding the Geolocation Code
- •Putting It All Together
- •The Final Code
- •Summary
- •Using the Forms API
- •Overview of HTML5 Forms
- •HTML Forms Versus XForms
- •Functional Forms
- •Browser Support for HTML5 Forms
- •An Input Catalog
- •Using the HTML5 Forms APIs
- •New Form Attributes and Functions
- •Checking Forms with Validation
- •Validation Feedback
- •Building an Application with HTML5 Forms
- •Practical Extras
- •Summary
- •Working with Drag-and-Drop
- •Web Drag-and-Drop: The Story So Far
- •Overview of HTML5 Drag-and-Drop
- •The Big Picture
- •Events to Remember
- •Drag Participation
- •Transfer and Control
- •Building an Application with Drag-and-Drop
- •Getting Into the dropzone
- •Handling Drag-and-Drop for Files
- •Practical Extras
- •Customizing the Drag Display
- •Summary
- •Using the Web Workers API
- •Browser Support for Web Workers
- •Using the Web Workers API
- •Checking for Browser Support
- •Creating Web Workers
- •Loading and Executing Additional JavaScript
- •Communicating with Web Workers
- •Coding the Main Page
- •Handling Errors
- •Stopping Web Workers
- •Using Web Workers within Web Workers
- •Using Timers
- •Example Code
- •Building an Application with Web Workers
- •Coding the blur.js Helper Script
- •Coding the blur.html Application Page
- •Coding the blurWorker.js Web Worker Script
- •Communicating with the Web Workers
- •The Application in Action
- •Example Code
- •Summary
- •Using the Storage APIs
- •Overview of Web Storage
- •Browser Support for Web Storage
- •Using the Web Storage API
- •Checking for Browser Support
- •Setting and Retrieving Values
- •Plugging Data Leaks
- •Local Versus Session Storage
- •Other Web Storage API Attributes and Functions
- •Communicating Web Storage Updates
- •Exploring Web Storage
- •Building an Application with Web Storage
- •The Future of Browser Database Storage
- •The Web SQL Database
- •The Indexed Database API
- •Practical Extras
- •JSON Object Storage
- •A Window into Sharing
- •Summary
- •Overview of HTML5 Offline Web Applications
- •Browser Support for HTML5 Offline Web Applications
- •Using the HTML5 Application Cache API
- •Checking for Browser Support
- •Creating a Simple Offline Application
- •Going Offline
- •Manifest Files
- •The ApplicationCache API
- •Application Cache in Action
- •Building an Application with HTML5 Offline Web Applications
- •Creating a Manifest File for the Application Resources
- •Creating the HTML Structure and CSS for the UI
- •Creating the Offline JavaScript
- •Check for ApplicationCache Support
- •Adding the Update Button Handler
- •Add Geolocation Tracking Code
- •Adding Storage Code
- •Adding Offline Event Handling
- •Summary
- •The Future of HTML5
- •Browser Support for HTML5
- •HTML Evolves
- •WebGL
- •Devices
- •Audio Data API
- •Touchscreen Device Events
- •Peer-to-Peer Networking
- •Ultimate Direction
- •Summary
- •Index
CHAPTER 10 USING THE WEB WORKERS API
log("error: " + e.message);
}
Coding the blurWorker.js Web Worker Script
Next, we add the code that our workers use to communicate with the page to the file blurWorker.js (see Listing 10-6). As the Web Workers finish blocks of computation, they can use postMessage to inform the page that they have made progress. We will use this information to update the image displayed on the main page. After creation, our Web Workers wait for a message containing image data and the instruction to commence blurring. This message is a JavaScript object containing the type of message and the image data represented as an array of Numbers.
Listing 10-6. Sending and Handling Image Data in the File blurWorker.js
function sendStatus(statusText) { postMessage({"type" : "status",
"statusText" : statusText} );
}
function messageHandler(e) {
var messageType = e.data.type; switch (messageType) {
case ("blur"):
sendStatus("Worker started blur on data in range: " +
e.data.startX + "-" + (e.data.startX+e.data.width)); var imageData = e.data.imageData;
imageData = boxBlur(imageData, e.data.width, e.data.height, e.data.startX);
postMessage({"type" : "progress", "imageData" : imageData, "width" : e.data.width, "height" : e.data.height, "startX" : e.data.startX
});
sendStatus("Finished blur on data in range: " +
e.data.startX + "-" + (e.data.width+e.data.startX));
break;
default:
sendStatus("Worker got message: " + e.data);
}
}
addEventListener("message", messageHandler, true);
Communicating with the Web Workers
In the file blur.html, we can use our workers by sending them some data and arguments that represent a blur task. This is done by using postMessage to send a JavaScript object containing the Array of RGBA image data, the dimensions of the source image, and the range of pixels for which the worker is responsible. Each worker processes a different section of the image based on the message it receives:
254

CHAPTER 10 USING THE WEB WORKERS API
function sendBlurTask(worker, i, chunkWidth) { var chunkHeight = image.height;
var chunkStartX = i * chunkWidth; var chunkStartY = 0;
var data = ctx.getImageData(chunkStartX, chunkStartY, chunkWidth, chunkHeight).data;
worker.postMessage({'type' : 'blur', 'imageData' : data, 'width' : chunkWidth, 'height' : chunkHeight, 'startX' : chunkStartX});
}
Canvas Image Data
Frank says: “postMessage is specified to allow efficient serialization of imageData objects for use with the canvas API. Some browsers that include the Worker and postMessage APIs may not support the extended serialization capabilities of postMessage yet.
Because of this, our image processing example presented in this chapter sends imageData.data (which serializes like a JavaScript Array) instead of sending the imageData object itself. As the Web Workers compute their tasks, they communicate their status and results back to the page. Listing 10-6 shows how data is sent from the worker(s) to the page after the blur filter has processed it. Again, the message contains a JavaScript object with fields for image data and coordinates marking the boundaries of the processed section.”
On the HTML page side, a message handler consumes this data and uses it to update the canvas with the new pixel values. As processed image data comes in, the result is immediately visible. We now have a sample application that can process images while potentially taking advantage of multiple CPU cores. Moreover, we didn’t lock up the UI and make it unresponsive while the Web Workers were active. Figure 10-7 shows the application in action.
255

CHAPTER 10 USING THE WEB WORKERS API
Figure 10-7. The blur application in action
The Application in Action
To see this example in action, the page blur.html has to be served up by a web server (for example, Apache or Python’s SimpleHTTPServer). To following steps show how you can use Python SimpleHTTPServer to run the application:
1.Install Python.
256
v
CHAPTER 10 USING THE WEB WORKERS API
2.Navigate to the directory that contains the example file (blur.html).
3.Start Python as follows:
python -m SimpleHTTPServer 9999
4.Open a browser and navigate to http://localhost:9999/blur.html. You should now see the page shown in Figure 10-7.
5.If you leave it running for a while, you will see the different quadrants of the image blur slowly. The number of quadrants that blur at the same time depends on the number of workers you started.
Example Code
For completeness, Listings 10-7, 10-8, and 10-9 contain the full code for the example application.
Listing 10-7. Content of the File blur.html
<!DOCTYPE html>
<title>Web Workers</title>
<link rel="stylesheet" href = "styles.css">
<h1>Web Workers</h1>
<p id="status">Your browser does not support Web Workers.</p>
<button id="startBlurButton" disabled>Blur</button> <button id="stopButton" disabled>Stop Workers</button>
<button onclick="document.location = document.location;">Reload</button>
<label for="workerCount">Number of Workers</label> <select id="workerCount">
<option>1</option>
<option selected>2</option> <option>4</option> <option>8</option> <option>16</option>
</select>
<div id="imageContainer"></div> <div id="logOutput"></div> <script>
var imageURL = "example2.png"; var image;
var ctx;
var workers = [];
function log(s) {
var logOutput = document.getElementById("logOutput");
257
CHAPTER 10 USING THE WEB WORKERS API
logOutput.innerHTML = s + "<br>" + logOutput.innerHTML;
}
function setRunningState(p) {
// while running, the stop button is enabled and the start button is not document.getElementById("startBlurButton").disabled = p; document.getElementById("stopButton").disabled = !p;
}
function initWorker(src) {
var worker = new Worker(src); worker.addEventListener("message", messageHandler, true); worker.addEventListener("error", errorHandler, true); return worker;
}
function startBlur() {
var workerCount = parseInt(document.getElementById("workerCount").value); var width = image.width/workerCount;
for (var i=0; i<workerCount; i++) {
var worker = initWorker("blurWorker.js"); worker.index = i;
worker.width = width; workers[i] = worker;
sendBlurTask(worker, i, width);
}
setRunningState(true);
}
function sendBlurTask(worker, i, chunkWidth) { var chunkHeight = image.height;
var chunkStartX = i * chunkWidth; var chunkStartY = 0;
var data = ctx.getImageData(chunkStartX, chunkStartY, chunkWidth, chunkHeight).data;
worker.postMessage({'type' : 'blur', 'imageData' : data, 'width' : chunkWidth, 'height' : chunkHeight, 'startX' : chunkStartX});
}
function stopBlur() {
for (var i=0; i<workers.length; i++) { workers[i].terminate();
}
setRunningState(false);
}
258
CHAPTER 10 USING THE WEB WORKERS API
function messageHandler(e) {
var messageType = e.data.type; switch (messageType) {
case ("status"): log(e.data.statusText); break;
case ("progress"):
var imageData = ctx.createImageData(e.data.width, e.data.height);
for (var i = 0; i<imageData.data.length; i++) { var val = e.data.imageData[i];
if (val === null || val > 255 || val < 0) { log("illegal value: " + val + " at " + i); return;
}
imageData.data[i] = val;
}
ctx.putImageData(imageData, e.data.startX, 0);
// blur the same tile again
sendBlurTask(e.target, e.target.index, e.target.width); break;
default:
break;
}
}
function errorHandler(e) { log("error: " + e.message);
}
function loadImageData(url) {
var canvas = document.createElement('canvas'); ctx = canvas.getContext('2d');
image = new Image(); image.src = url;
document.getElementById("imageContainer").appendChild(canvas);
image.onload = function(){ canvas.width = image.width; canvas.height = image.height; ctx.drawImage(image, 0, 0);
window.imgdata = ctx.getImageData(0, 0, image.width, image.height); n = ctx.createImageData(image.width, image.height); setRunningState(false);
log("Image loaded: " + image.width + "x" + image.height + " pixels");
};
}
259
CHAPTER 10 USING THE WEB WORKERS API
function loadDemo() { log("Loading image data");
if (typeof(Worker) !== "undefined") {
document.getElementById("status").innerHTML = "Your browser supports Web Workers";
document.getElementById("stopButton").onclick = stopBlur; document.getElementById("startBlurButton").onclick = startBlur;
loadImageData(imageURL);
document.getElementById("startBlurButton").disabled = true; document.getElementById("stopButton").disabled = true;
}
}
window.addEventListener("load", loadDemo, true); </script>
Listing 10-8. Content of the File blurWorker.js
importScripts("blur.js");
function sendStatus(statusText) { postMessage({"type" : "status",
"statusText" : statusText} );
}
function messageHandler(e) {
var messageType = e.data.type; switch (messageType) {
case ("blur"):
sendStatus("Worker started blur on data in range: " +
e.data.startX + "-" + (e.data.startX+e.data.width)); var imageData = e.data.imageData;
imageData = boxBlur(imageData, e.data.width, e.data.height, e.data.startX);
postMessage({"type" : "progress", "imageData" : imageData, "width" : e.data.width, "height" : e.data.height, "startX" : e.data.startX
});
sendStatus("Finished blur on data in range: " +
e.data.startX + "-" + (e.data.width+e.data.startX));
break;
default:
sendStatus("Worker got message: " + e.data);
}
}
260