Firefox may be the most successful product of the Mozilla project. It was first released in 2004, and grew dramatically in popularity at a time when the leading Web browser, Microsoft® Internet Explorer (MSIE), was not undergoing much development, and was suffering from heavy security problems. Mostly in response to the Firefox phenomenon, Microsoft resumed pouring resources into MSIE, but this didn't slow the steady growth of the new competitor. According to the Mozilla Foundation, there are almost 200 million Firefox users worldwide, due in part to its availability in 45 languages. Mozilla is planning to make even more news by setting a Guinness world record for most software downloads in a 24-hour period, which would be a new category. The target software for this record attempt is Firefox 3.0, a major new version of the browser planned for release in the summer of 2008. It's already available in release candidate form, which means that developers are confident that the next release could be the full, final 3.0 version. Firefox 3 makes Web development even more of a pleasure, and with the addition of offline support, opens up the browser as a more viable general-purpose application platform. This article is your guide to getting the most benefit out of these new Firefox 3.0 updates.
The reason for all the excitement around Firefox 3.0 is that it promises an impressive array of improvements for the Web user and developer alike. It's an important milestone because Web developers have often used Firefox as a favorite development platform, even though they know they have to make eventual concessions toward cross-browser compatibility. Firefox's popularity among developers stems from the vibrant community, the rigorous standards support, and the constant innovation in the platform. The platform's innovation gives developers a head start on Web trends, and its standards support means that even on the cutting edge of these trends, Firefox offers transparency in its features that accelerates adoption and improves compatibility. Firefox 3 has even more to offer. As the release notes says:
Firefox 3 is based on the Gecko 1.9 Web rendering platform, which has been under development for the past 33 months. Building on the previous release, Gecko 1.9 has more than 14,000 updates including some major re-architecting to provide improved performance, stability, rendering correctness, and code simplification and sustainability. Firefox 3 has been built on top of this new platform resulting in a more secure, easier to use, more personal product with a lot more under the hood to offer website and Firefox add-on developers.
"14,000" is one of those numbers stuck in for its "wow-factor," but there is an impressive number of useful updates, many of which are worth exploring.
Several security enhancements are included, from closing script vulnerabilities to phishing protection, but most of them won't affect your work unless you happen to be writing malicious code. Other updates make life easier for users, such as better download and password managers, a neat ability to zoom on pages, and more native behavior in Mac, Windows®, or Linux®. The new bookmarks are very powerful, and allow you to use multiple tags rather than strict hierarchy. Bookmarks are combined with richer history features that provide what are called Places, allowing a way to manage and search your personal path through the Web.
Firefox 3 gives you the ability to support continued work while the user is offline, based on the HTML5 working draft. A user can have a static Web page online several ways: from using the browser cache, to having a local caching proxy, to just downloading the Web content to disk. More and more of what we do on the Web is dynamic, including reading Web mail or Web feeds, browsing our favorite shops, or using our favorite social networks. Even the most standard productivity applications have gone Web-happy with online versions of word processors, spreadsheets, and presentation tools, as well as new categories of these applications such as Wikis. These applications would be even more valuable if you could continue to use them when no network is available. Firefox 3 makes it possible for you to offer this capability in your Web application. Let's take a close look at this new feature, perhaps the most interesting new feature for Web developers.
The first piece of the online/offline puzzle is that your Web application can instruct the user's browser to save resources needed by the application in a local cache, called the application cache, which is available offline. You'll generally use this cache for the main Web page, stylesheets, script files, and other files you need that aren't changed very often. You register a URI for your application, which governs a bundle of resources that Firefox manages in sync with the online equivalents. Use the manifest attribute on the document element (for example, html) to establish a cache for your application.
The second puzzle piece is knowing when the user goes online or offline so you can
perform any tasks not covered by the automatic application cache management.
Firefox 2 introduced a Boolean script property navigator.onLine, which gets updated whenever the user manually
switches into offline mode, generally through the Firefox menu. Starting with
Firefox 3, this property is now also updated when the browser automatically detects that the network is unreachable. Additionally, script events are invoked when the online state changes. By listening for such events, you can take full control of application behavior in offline circumstances.
An offline application example
Soon after offline support became available in Firefox 3 pre-alpha versions, Mozilla developer Mark Finkle put together a neat little example for developers. It's a to-do list tool that uses script to embed the user's entries into the application cache version of the main service Web page. It also saves the to-do list on the server side. That way, the page is available and the user's data is maintained whether the user is online or offline. With Mark's permission, I updated his example for this article. Listing 1 (todo.html) is the main HTML file for the application.
Listing 1. To-do list application main HTML (todo.js)
<html manifest="todo.manifest">
<head>
<title>Todo tool</title>
<script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="todo.js"></script>
<style type="text/css">
body { font-family: verdana,tahoma, arial; }
div#container { width: 300px; }
div#title { font-size: 120%; }
div#subtitle { font-size: 80%; }
div#tasklist { margin-bottom: .5em; }
div#log { font-size: 90%; background-color: lightgray; margin-top: 1em;
white-space: pre; }
</style>
</head>
<body onload="loaded();">
<div id="container">
<div id="title">Todo tool <span id="status">online*</span></div>
<div id="subtitle">Simple online/offline demo for Firefox 3</div>
<hr />
<div id="tasklist">
</div>
<input type="text" id="data" size="35" />
<input type="button" value="Add" onclick="addItem();"/>
<hr />
<input type="button" value="Remove" onclick="removeItems();"/>
<input type="button" value="Complete" onclick="completeItems();"/>
<div id="log"><strong>Event Log</strong>
</div>
</div>
</body>
</html>
|
Pay attention to the attribute on the first line, manifest="todo.manifest". The value is a relative URL that is combined with the base URL of the HTML page to specify the manifest URL, the contents of which are in Listing 2.
Listing 2. Resource cache manifest for the to-do list application (todo.manifest)
CACHE MANIFEST
# v1
todo.html
json2.js
|
The format is fairly simple. The main thing I want to call attention to is the
v1 comment. If you update any of the files in the
manifest, update this text (for example, to v2 and so on). When the browser notices that the manifest file has changed, it automatically gets new versions of all the listed files (even those that have not changed). Back in Listing 1, the to-do list items are presented within the
<div id="tasklist"></div>, updated dynamically
through scripting. One of the loaded scripts, json2.js,
is a popular library that provides useful code for JSON processing. But, I'll
provide more on that later. The second script, in Listing 3, contains the specific processing for the to-do list tool, including the offline processing bits.
Listing 3. To-do list application script (todo.js)
//Uses offline features from WHAT Web applications draft specification
//(Firefox 3-specific at present)
//JSON form of the task list items, for direct transport purposes
var taskStorage = "[]";
//Web app's domain is used as a key to the application cache data (globalStorage)
var storageDomain = location.hostname;
//Invoked when the browser page is loaded (i.e. onLoad attribute on the body)
function loaded() {
//Load the to-do list data from app cache or web app, depending on offline status
updateOnlineStatus("initial load", false);
//Set up listeners to handle online/offline transitions
document.body.addEventListener("offline",
function () { updateOnlineStatus("offline", true) }, false);
document.body.addEventListener("online",
function () { updateOnlineStatus("online", true) }, false);
//Load initial to-do list data saved in the application cache, if available
//This will cause the browser to prompt the user for permission
if (typeof globalStorage != "undefined") {
var storage = globalStorage[storageDomain];
if (storage && storage.taskStorage) {
taskStorage = storage.taskStorage;
}
}
//See if we can load an updated task list
fetchList();
}
//Invoked when the user's online/offline status changes
function updateOnlineStatus(msg, allowUpdate) {
//Update the online status indicator in the subtitle
var status = document.getElementById("status");
status.innerHTML = (navigator.onLine ? "[online]" : "[offline]");
//Record the change in the log area of the Web page
var log = document.getElementById("log");
log.appendChild(document.createTextNode("Event: " + msg + "\n"));
//If online, try to push any task list changes back to the server
if (navigator.onLine && allowUpdate) {
update();
log.appendChild(document.createTextNode("Updated server\n"));
}
}
//Execute HTTP request to the server, either to get the task list or to push updates
function httpRequest(type, data, callback) {
var httpreq = new XMLHttpRequest();
httpreq.onreadystatechange = function() {
if (httpreq.readyState == 4)
callback(httpreq.readyState, httpreq.status, httpreq.responseText);
}; //close function()
httpreq.open(type, "/todo-app", true);
if (type == "POST") {
httpreq.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
}
httpreq.send(data);
}
//Put updated task list from the server on the Web page, and in the application cache
function loadList(readyState, status, responseText) {
if (readyState == 4) {
if (status == 200) {
taskStorage = responseText;
var tasks = JSON.parse(taskStorage);
var html = "";
//Update the fields on the Web page
for (var i=0; i<tasks.length; i++) {
html += "<input type='checkbox' id='" + tasks[i].name + "'/><label for='"
html += tasks[i].name + "'>" + tasks[i].data + "</label><br/>";
}
document.getElementById("tasklist").innerHTML = html;
//Update the application cache
if (typeof globalStorage != "undefined") {
globalStorage[storageDomain].taskStorage = taskStorage;
}
}
}
}
//Load an updated task list either from the server or from application cache
function fetchList() {
if (navigator.onLine) {
httpRequest("GET", null, loadList);
}
else {
loadList(4, 200, taskStorage);
}
}
//Invoked when the user adds a new task list item
function addItem() {
var data = document.getElementById("data").value;
document.getElementById("data").value = "";
//Turn the stored task list into a JavaScript list
//Use present timestamp as a simple ID ("name")
var tasks = JSON.parse(taskStorage);
tasks.push({"name": Date.now(), "data": data });
//Convert back to JSON and update the task list variable
taskStorage = JSON.stringify(tasks);
//Try to push any task list changes back to the server
update();
}
//Invoked when the user removes a task list item
function removeItems() {
var tasks = JSON.parse(taskStorage);
var newTasks = [];
//See which boxes are checked and only copy back those that are not
var items = document.getElementById("tasklist").getElementsByTagName("input");
for (var i=0; i<items.length; i++) {
if (items[i].checked == false) {
newTasks.push(tasks[i]);
}
}
taskStorage = JSON.stringify(newTasks);
update();
}
//Invoked when the user marks new task list item as completed
function completeItems() {
var tasks = JSON.parse(taskStorage);
//See which boxes are checked add strikeout tags to those that are
var items = document.getElementById("tasklist").getElementsByTagName("input");
for (var i=0; i<items.length; i++) {
if (items[i].checked) {
var task = tasks[i].data;
if (task.indexOf("<strike>") != -1) {
task = task.replace("<strike>", "");
task = task.replace("</strike>", "");
}
else {
task = "<strike>" + task + "</strike>";
}
tasks[i].data = task;
}
}
taskStorage = JSON.stringify(tasks);
update();
}
//Try to push any task list changes back to the server
function update() {
if (navigator.onLine) {
var post = "action=update&tododata=" + encodeURIComponent(taskStorage);
httpRequest("POST", post, function(readyState, status, json) { fetchList(); });
}
else {
loadList(4, 200, taskStorage);
}
}
|
I've heavily commented the code, but I'll make a few general notes about it. The to-do list data is embedded into the page, but it's transported around in JSON form, maintained in the taskStorage variable. When you first load the Web app's page, Firefox tries to load this data from the application cache. If there is no entry for the application, Firefox will prompt the user for permission to set one up. This is illustrated in Figure 1.
Figure 1. To-do tool on initial load

If the user clicks allow, Firefox sets up the application cache. As a developer, you'll probably need to reset things so that Firefox asks for permission once again. You can do this in the Preferences or Options window, specifically the Advanced tab, and Network sub-tab. Figure 2 shows the relevant window section on Mac OS X. You can see the offline permissions entry for local host. To reset this, select it and click Remove....
Figure 2. Managing offline storage

Implementing the to-do list server
After the user has opened the Web page, he can add or remove entries, or mark entries as completed, which keeps them displayed in the list, but crossed out. Figure 3 shows how the page looks after I added a few test entries while online.
Figure 3. To-do tool online, with a few added entries

Whenever the to-do list is updated while online, the script sends an update to the server. I wrote some code in Python for a simple server that handles this. Mark Finkle also wrote some code in PHP to handle this situation, so if that's your preference, see Resources for a link to his original code, although you'll still need to use the updated script in Listing 3 for things to work in Firefox 3 release candidates or the final version. Listing 4 (todohandler.py) is the Python server code.
Listing 4. To-do list application server code (todohandler.py)
import os, sys
import cherrypy #http://www.cherrypy.org/
from webob import Request, Response #http://pythonpaste.org/webob/
#For now just use a single file for all sessions
DATAFILE = 'test.json'
#The handler function
def todo_handler_application(environ, start_response):
req = Request(environ)
if req.POST.get("action") == "update":
#Handle POSTs to update the to-do list entries. Write to the file
f = open(DATAFILE, 'w')
f.write(req.POST["tododata"])
f.close()
resp = Response(body='', content_type='application/json')
else:
#Handle GETs to pull the to-do list entries. Read from the file
f = open(DATAFILE, 'r')
data = f.read()
f.close()
resp = Response(body=data, content_type='application/json')
#Send the response
return resp(environ, start_response)
#Configure the server to handle the Web form in todo.html
cherrypy.tree.graft(todo_handler_application, '/todo-app')
#Configure the server to serve up the regular, static files from the current directory
server_config = {
'/': {'tools.staticdir.on': True, 'tools.staticdir.dir': os.getcwd() }
}
#Create a CherryPy handler. Doesn't do anything because the meat is in the WSGI app
class DummyHandler: pass
cherrypy.quickstart(DummyHandler(), '/', config=server_config)
|
Again, the code is pretty well commented, and the only thing I want to mention is
that to simplify the example, the code uses a single file (specified as DATAFILE) for all application users, which of course would have to be
changed for a real-world application. You will want to use separate files or database rows per user. I bootstrap this data using the file in Listing 5 (test.json).
Listing 5. To-do list application script (test.json)
[{"name":1171640861226,"data":"<strike>Example entry</strike>"},
{"name":1212604738536,"data":"Say \"Hello\""},{"name":1212604795352,"data":"
<strike>Say \"Good night\"</strike>"}]
|
Now that I've exercised the online behavior a bit, it's time to take things offline. In Firefox you can do so using Firefox menu in the Firefox menu. The title immediately changes to "Todo tool [offline]," thanks to the script handlers for online/offline transition. The user can still use the application normally. After some offline manipulation (in this case marking tasks as completed), and switching back online, the display might look something like Figure 4.
Figure 4. To-do tool back online after offline manipulations

As a note, I wanted to mention the JSON processing in Listing 3. I use Douglas
Crockford's json2.js library, which is based on an API he recommended for a
JavaScript enhancement, which seems likely to be carried. Listing 3 uses JSON.stringify and JSON.parse from this
API. Firefox 3 adds new, high-performance JSON processing, nsIJSON, but for now it's not yet exposed for use in the default
browser setup. It will probably get built into Firefox soon, and it should be
compatible with json2.js. When this processing is ready, you will gain performance and eliminate one external dependency by eliminating the separate JavaScript file.
I encourage you to explore a few other new features in Firefox. Web-based protocol
handlers let you define new URI types beyond built-ins, such as http: and mailto:. For XML developers, Firefox 3 adds support for many EXSLT extensions, which greatly increase the power of XSLT transforms. It also improves support for the XML-based vector graphics standard SVG . I'll discuss more of this and other XML processing improvements in a separate article. Firefox 3 makes Web development even more of a pleasure, and with the addition of offline support, opens up the browser as a more viable general-purpose application platform. Obviously most of the browser's new capabilities cannot be used in other browsers yet, which is unfortunate, but at least most of them are based on standards, so you can reasonably expect other Web software to catch up soon.
Learn
-
Much of the code in this article is adapted from Mark Finkle’s example, Firefox 3 - Offline App Demo. He writes a server in PHP, similar to the Python code in this article.
-
Check out the updated developer features for Firefox 3.0, including the offline features and nsIJSON. For more general information, see the release notes.
-
Learn more about offline support in Offline Events, by John Resig.
-
Learn more about HTML 5 in ""The future of HTML,
Part 1: WHATWG" (developerWorks, December 2005) and "New elements in HTML 5"
(developerWorks, August 2007).
-
See some other articles focusing on Firefox for XML developers.
- "XML in Firefox 1.5, Part 1: Overview of XML features" (developerWorks, September 2005)
- "XML in Firefox 1.5, Part 2: Basic XML processing" (developerWorks, March 2006)
- "XML in Firefox 1.5, Part 3: JavaScript meets XML in Firefox" (developerWorks, August 2006)
- "Firefox 2.0 and XML" (developerWorks, October 2007)
- Stay current with developerWorks technical events and Webcasts.
-
Expand your Web development skills with articles and tutorials that specialize
in Web technologies in the developerWorks Web development zone.
Get products and technologies
- Get Firefox, the Mozilla-based Web browser that offers standards compliance, performance, security, and solid XML features. Learn about the plan to set a Guinness World Record for Firefox 3.0 on the Download Day page.
-
You need Douglas Crockford's json2.js for the code in this article. John Resig discusses this, and how it might evolve into native JSON spport for Firefox 3 in The State of JSON.
Discuss
-
Participate in developerWorks
blogs and get involved in the developerWorks community.

Uche Ogbuji is Partner at Zepheira, LLC, a solutions firm specializing in the next generation of Web technologies. Mr. Ogbuji is lead developer of 4Suite, an open source platform for XML, RDF and knowledge-management applications, the Jacqard agile methodology for team Web development, and the Versa RDF query language. He is a computer engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. You can find more about Mr. Ogbuji at his blog Copia.
