Merge mozilla-central and mozilla-inbound
authorMatt Brubeck <mbrubeck@mozilla.com>
Sat, 24 Mar 2012 09:05:27 -0700
changeset 93576 27bc8243ec5b9c6cc33603cbbec01580cfd5c85d
parent 93575 2342271b2be16c50307a6b4f0257310fe010faa3 (current diff)
parent 93452 ae8de224173233054d226733679d8712a4c225e4 (diff)
child 93577 6b6084350c40398ef9441715b0bee9b8cddc37ca
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central and mozilla-inbound
b2g/chrome/content/httpd.js
b2g/chrome/content/shell.js
b2g/installer/package-manifest.in
config/autoconf.mk.in
configure.in
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
editor/libeditor/base/nsEditorEventListener.cpp
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -35,46 +35,70 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #filter substitution
 
 pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul");
 pref("browser.chromeURL", "chrome://browser/content/");
 #ifdef MOZ_OFFICIAL_BRANDING
-pref("browser.homescreenURL", "file:///system/home/homescreen.html");
+pref("browser.homescreenURL", "http://homescreen.gaiamobile.org/");
 #else
-pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system/home/homescreen.html");
+pref("browser.homescreenURL", "http://homescreen.gaiamobile.org/");
 #endif
 
+// All the privileged domains
+// XXX TODO : we should read them from a file somewhere
+pref("b2g.privileged.domains", "http://browser.gaiamobile.org,
+	                            http://calculator.gaiamobile.org,
+	                            http://contacts.gaiamobile.org,
+	                            http://camera.gaiamobile.org,
+	                            http://clock.gaiamobile.org,
+	                            http://crystalskull.gaiamobile.org,
+	                            http://cubevid.gaiamobile.org,
+	                            http://dialer.gaiamobile.org,
+	                            http://gallery.gaiamobile.org,
+	                            http://homescreen.gaiamobile.org,
+	                            http://maps.gaiamobile.org,
+	                            http://market.gaiamobile.org,
+	                            http://music.gaiamobile.org,
+	                            http://penguinpop.gaiamobile.org,
+	                            http://settings.gaiamobile.org,
+	                            http://sms.gaiamobile.org,
+	                            http://towerjelly.gaiamobile.org,
+	                            http://video.gaiamobile.org");
+
 // URL for the dialer application.
-pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html,http://localhost:7777/data/local/apps/homescreen/homescreen.html,http://localhost:7777/apps/dialer/dialer.html,http://localhost:7777/apps/homescreen/homescreen.html");
+pref("dom.telephony.app.phone.url", "http://dialer.gaiamobile.org,http://homescreen.gaiamobile.org");
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
 /* disable text selection */
 pref("browser.ignoreNativeFrameTextSelection", true);
 
 /* cache prefs */
+#ifdef MOZ_WIDGET_GONK
 pref("browser.cache.disk.enable", true);
 pref("browser.cache.disk.capacity", 55000); // kilobytes
 pref("browser.cache.disk.parent_directory", "/cache");
+#endif
 pref("browser.cache.disk.smart_size.enabled", false);
 pref("browser.cache.disk.smart_size.first_run", false);
 
 pref("browser.cache.memory.enable", true);
 pref("browser.cache.memory.capacity", 1024); // kilobytes
 
 /* image cache prefs */
 pref("image.cache.size", 1048576); // bytes
 
 /* offline cache prefs */
 pref("browser.offline-apps.notify", false);
-pref("browser.cache.offline.enable", false);
+pref("browser.cache.offline.enable", true);
+pref("offline-apps.allow_by_default", true);
 
 /* protocol warning prefs */
 pref("network.protocol-handler.warn-external.tel", false);
 pref("network.protocol-handler.warn-external.mailto", false);
 pref("network.protocol-handler.warn-external.vnd.youtube", false);
 
 /* http prefs */
 pref("network.http.pipelining", true);
@@ -386,38 +410,38 @@ pref("security.fileuri.strict_origin_pol
 // Temporarily force-enable GL compositing.  This is default-disabled
 // deep within the bowels of the widgetry system.  Remove me when GL
 // compositing isn't default disabled in widget/android.
 pref("layers.acceleration.force-enabled", true);
 
 // screen.enabled and screen.brightness properties.
 pref("dom.screenEnabledProperty.enabled", true);
 pref("dom.screenBrightnessProperty.enabled", true);
-pref("dom.mozScreenWhitelist", "http://localhost:7777");
+pref("dom.mozScreenWhitelist", "http://homescreen.gaiamobile.org,http://settings.gaiamobile.org");
 
 // handle links targeting new windows
 // 1=current window/tab, 2=new window, 3=new tab in most recent window
 pref("browser.link.open_newwindow", 3);
 
 // 0: no restrictions - divert everything
 // 1: don't divert window.open at all
 // 2: don't divert window.open with features
 pref("browser.link.open_newwindow.restriction", 0);
 
 // Enable browser frame
 pref("dom.mozBrowserFramesEnabled", true);
-pref("dom.mozBrowserFramesWhitelist", "http://localhost:7777");
+pref("dom.mozBrowserFramesWhitelist", "http://homescreen.gaiamobile.org,http://browser.gaiamobile.org");
 
 // Temporary permission hack for WebSMS
 pref("dom.sms.enabled", true);
-pref("dom.sms.whitelist", "file://,http://localhost:7777");
+pref("dom.sms.whitelist", "file://,http://homescreen.gaiamobile.org,http://sms.gaiamobile.org");
 
 // Temporary permission hack for WebContacts
 pref("dom.mozContacts.enabled", true);
-pref("dom.mozContacts.whitelist", "http://localhost:7777");
+pref("dom.mozContacts.whitelist", "http://dialer.gaiamobile.org,http://sms.gaiamobile.org");
 
 // Ignore X-Frame-Options headers.
 pref("b2g.ignoreXFrameOptions", true);
 
 // controls if we want camera support
 pref("device.camera.enabled", true);
 pref("media.realtime_decoder.enabled", true);
 
@@ -435,17 +459,17 @@ pref("b2g.remote-js.enabled", true);
 pref("b2g.remote-js.port", 9999);
 
 // Handle hardware buttons in the b2g chrome package
 pref("b2g.keys.menu.enabled", true);
 pref("b2g.keys.search.enabled", false);
 
 // Screen timeout in minutes
 pref("power.screen.timeout", 60);
-pref("dom.power.whitelist", "http://localhost:7777");
+pref("dom.power.whitelist", "http://homescreen.gaiamobile.org,http://settings.gaiamobile.org");
 
 pref("full-screen-api.enabled", true);
 
 pref("media.volume.steps", 10);
 
 // Data connection settings. These will eventually live in the
 // navigator.settings API, or even in a database where we can look
 // it up automatically (bug 729440), but for this will have to do.
deleted file mode 100644
--- a/b2g/chrome/content/httpd.js
+++ /dev/null
@@ -1,5211 +0,0 @@
-/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et: */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the httpd.js server.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2006
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Darin Fisher (v1, netwerk/test/TestServ.js)
- *   Christian Biesinger (v2, netwerk/test/unit/head_http_server.js)
- *   Jeff Walden <jwalden+code@mit.edu> (v3, netwerk/test/httpserver/httpd.js)
- *   Robert Sayre <sayrer@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * An implementation of an HTTP server both as a loadable script and as an XPCOM
- * component.  See the accompanying README file for user documentation on
- * httpd.js.
- */
-
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cr = Components.results;
-const Cu = Components.utils;
-const CC = Components.Constructor;
-
-const PR_UINT32_MAX = Math.pow(2, 32) - 1;
-
-/** True if debugging output is enabled, false otherwise. */
-var DEBUG = false; // non-const *only* so tweakable in server tests
-
-/** True if debugging output should be timestamped. */
-var DEBUG_TIMESTAMP = false; // non-const so tweakable in server tests
-
-var gGlobalObject = this;
-
-/**
- * Asserts that the given condition holds.  If it doesn't, the given message is
- * dumped, a stack trace is printed, and an exception is thrown to attempt to
- * stop execution (which unfortunately must rely upon the exception not being
- * accidentally swallowed by the code that uses it).
- */
-function NS_ASSERT(cond, msg)
-{
-  if (DEBUG && !cond)
-  {
-    dumpn("###!!!");
-    dumpn("###!!! ASSERTION" + (msg ? ": " + msg : "!"));
-    dumpn("###!!! Stack follows:");
-
-    var stack = new Error().stack.split(/\n/);
-    dumpn(stack.map(function(val) { return "###!!!   " + val; }).join("\n"));
-
-    throw Cr.NS_ERROR_ABORT;
-  }
-}
-
-/** Constructs an HTTP error object. */
-function HttpError(code, description)
-{
-  this.code = code;
-  this.description = description;
-}
-HttpError.prototype =
-{
-  toString: function()
-  {
-    return this.code + " " + this.description;
-  }
-};
-
-/**
- * Errors thrown to trigger specific HTTP server responses.
- */
-const HTTP_400 = new HttpError(400, "Bad Request");
-const HTTP_401 = new HttpError(401, "Unauthorized");
-const HTTP_402 = new HttpError(402, "Payment Required");
-const HTTP_403 = new HttpError(403, "Forbidden");
-const HTTP_404 = new HttpError(404, "Not Found");
-const HTTP_405 = new HttpError(405, "Method Not Allowed");
-const HTTP_406 = new HttpError(406, "Not Acceptable");
-const HTTP_407 = new HttpError(407, "Proxy Authentication Required");
-const HTTP_408 = new HttpError(408, "Request Timeout");
-const HTTP_409 = new HttpError(409, "Conflict");
-const HTTP_410 = new HttpError(410, "Gone");
-const HTTP_411 = new HttpError(411, "Length Required");
-const HTTP_412 = new HttpError(412, "Precondition Failed");
-const HTTP_413 = new HttpError(413, "Request Entity Too Large");
-const HTTP_414 = new HttpError(414, "Request-URI Too Long");
-const HTTP_415 = new HttpError(415, "Unsupported Media Type");
-const HTTP_417 = new HttpError(417, "Expectation Failed");
-
-const HTTP_500 = new HttpError(500, "Internal Server Error");
-const HTTP_501 = new HttpError(501, "Not Implemented");
-const HTTP_502 = new HttpError(502, "Bad Gateway");
-const HTTP_503 = new HttpError(503, "Service Unavailable");
-const HTTP_504 = new HttpError(504, "Gateway Timeout");
-const HTTP_505 = new HttpError(505, "HTTP Version Not Supported");
-
-/** Creates a hash with fields corresponding to the values in arr. */
-function array2obj(arr)
-{
-  var obj = {};
-  for (var i = 0; i < arr.length; i++)
-    obj[arr[i]] = arr[i];
-  return obj;
-}
-
-/** Returns an array of the integers x through y, inclusive. */
-function range(x, y)
-{
-  var arr = [];
-  for (var i = x; i <= y; i++)
-    arr.push(i);
-  return arr;
-}
-
-/** An object (hash) whose fields are the numbers of all HTTP error codes. */
-const HTTP_ERROR_CODES = array2obj(range(400, 417).concat(range(500, 505)));
-
-
-/**
- * The character used to distinguish hidden files from non-hidden files, a la
- * the leading dot in Apache.  Since that mechanism also hides files from
- * easy display in LXR, ls output, etc. however, we choose instead to use a
- * suffix character.  If a requested file ends with it, we append another
- * when getting the file on the server.  If it doesn't, we just look up that
- * file.  Therefore, any file whose name ends with exactly one of the character
- * is "hidden" and available for use by the server.
- */
-const HIDDEN_CHAR = "^";
-
-/**
- * The file name suffix indicating the file containing overridden headers for
- * a requested file.
- */
-const HEADERS_SUFFIX = HIDDEN_CHAR + "headers" + HIDDEN_CHAR;
-
-/** Type used to denote SJS scripts for CGI-like functionality. */
-const SJS_TYPE = "sjs";
-
-/** Base for relative timestamps produced by dumpn(). */
-var firstStamp = 0;
-
-/** dump(str) with a trailing "\n" -- only outputs if DEBUG. */
-function dumpn(str)
-{
-  if (DEBUG)
-  {
-    var prefix = "HTTPD-INFO | ";
-    if (DEBUG_TIMESTAMP)
-    {
-      if (firstStamp === 0)
-        firstStamp = Date.now();
-
-      var elapsed = Date.now() - firstStamp; // milliseconds
-      var min = Math.floor(elapsed / 60000);
-      var sec = (elapsed % 60000) / 1000;
-
-      if (sec < 10)
-        prefix += min + ":0" + sec.toFixed(3) + " | ";
-      else
-        prefix += min + ":" + sec.toFixed(3) + " | ";
-    }
-
-    dump(prefix + str + "\n");
-  }
-}
-
-/** Dumps the current JS stack if DEBUG. */
-function dumpStack()
-{
-  // peel off the frames for dumpStack() and Error()
-  var stack = new Error().stack.split(/\n/).slice(2);
-  stack.forEach(dumpn);
-}
-
-
-/** The XPCOM thread manager. */
-var gThreadManager = null;
-
-/** The XPCOM prefs service. */
-var gRootPrefBranch = null;
-function getRootPrefBranch()
-{
-  if (!gRootPrefBranch)
-  {
-    gRootPrefBranch = Cc["@mozilla.org/preferences-service;1"]
-                        .getService(Ci.nsIPrefBranch);
-  }
-  return gRootPrefBranch;
-}
-
-/**
- * JavaScript constructors for commonly-used classes; precreating these is a
- * speedup over doing the same from base principles.  See the docs at
- * http://developer.mozilla.org/en/docs/Components.Constructor for details.
- */
-const ServerSocket = CC("@mozilla.org/network/server-socket;1",
-                        "nsIServerSocket",
-                        "init");
-const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1",
-                                 "nsIScriptableInputStream",
-                                 "init");
-const Pipe = CC("@mozilla.org/pipe;1",
-                "nsIPipe",
-                "init");
-const FileInputStream = CC("@mozilla.org/network/file-input-stream;1",
-                           "nsIFileInputStream",
-                           "init");
-const ConverterInputStream = CC("@mozilla.org/intl/converter-input-stream;1",
-                                "nsIConverterInputStream",
-                                "init");
-const WritablePropertyBag = CC("@mozilla.org/hash-property-bag;1",
-                               "nsIWritablePropertyBag2");
-const SupportsString = CC("@mozilla.org/supports-string;1",
-                          "nsISupportsString");
-
-/* These two are non-const only so a test can overwrite them. */
-var BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
-                           "nsIBinaryInputStream",
-                           "setInputStream");
-var BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
-                            "nsIBinaryOutputStream",
-                            "setOutputStream");
-
-/**
- * Returns the RFC 822/1123 representation of a date.
- *
- * @param date : Number
- *   the date, in milliseconds from midnight (00:00:00), January 1, 1970 GMT
- * @returns string
- *   the representation of the given date
- */
-function toDateString(date)
-{
-  //
-  // rfc1123-date = wkday "," SP date1 SP time SP "GMT"
-  // date1        = 2DIGIT SP month SP 4DIGIT
-  //                ; day month year (e.g., 02 Jun 1982)
-  // time         = 2DIGIT ":" 2DIGIT ":" 2DIGIT
-  //                ; 00:00:00 - 23:59:59
-  // wkday        = "Mon" | "Tue" | "Wed"
-  //              | "Thu" | "Fri" | "Sat" | "Sun"
-  // month        = "Jan" | "Feb" | "Mar" | "Apr"
-  //              | "May" | "Jun" | "Jul" | "Aug"
-  //              | "Sep" | "Oct" | "Nov" | "Dec"
-  //
-
-  const wkdayStrings = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
-  const monthStrings = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
-                        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
-
-  /**
-   * Processes a date and returns the encoded UTC time as a string according to
-   * the format specified in RFC 2616.
-   *
-   * @param date : Date
-   *   the date to process
-   * @returns string
-   *   a string of the form "HH:MM:SS", ranging from "00:00:00" to "23:59:59"
-   */
-  function toTime(date)
-  {
-    var hrs = date.getUTCHours();
-    var rv  = (hrs < 10) ? "0" + hrs : hrs;
-    
-    var mins = date.getUTCMinutes();
-    rv += ":";
-    rv += (mins < 10) ? "0" + mins : mins;
-
-    var secs = date.getUTCSeconds();
-    rv += ":";
-    rv += (secs < 10) ? "0" + secs : secs;
-
-    return rv;
-  }
-
-  /**
-   * Processes a date and returns the encoded UTC date as a string according to
-   * the date1 format specified in RFC 2616.
-   *
-   * @param date : Date
-   *   the date to process
-   * @returns string
-   *   a string of the form "HH:MM:SS", ranging from "00:00:00" to "23:59:59"
-   */
-  function toDate1(date)
-  {
-    var day = date.getUTCDate();
-    var month = date.getUTCMonth();
-    var year = date.getUTCFullYear();
-
-    var rv = (day < 10) ? "0" + day : day;
-    rv += " " + monthStrings[month];
-    rv += " " + year;
-
-    return rv;
-  }
-
-  date = new Date(date);
-
-  const fmtString = "%wkday%, %date1% %time% GMT";
-  var rv = fmtString.replace("%wkday%", wkdayStrings[date.getUTCDay()]);
-  rv = rv.replace("%time%", toTime(date));
-  return rv.replace("%date1%", toDate1(date));
-}
-
-/**
- * Prints out a human-readable representation of the object o and its fields,
- * omitting those whose names begin with "_" if showMembers != true (to ignore
- * "private" properties exposed via getters/setters).
- */
-function printObj(o, showMembers)
-{
-  var s = "******************************\n";
-  s +=    "o = {\n";
-  for (var i in o)
-  {
-    if (typeof(i) != "string" ||
-        (showMembers || (i.length > 0 && i[0] != "_")))
-      s+= "      " + i + ": " + o[i] + ",\n";
-  }
-  s +=    "    };\n";
-  s +=    "******************************";
-  dumpn(s);
-}
-
-/**
- * Instantiates a new HTTP server.
- */
-function nsHttpServer()
-{
-  if (!gThreadManager)
-    gThreadManager = Cc["@mozilla.org/thread-manager;1"].getService();
-
-  /** The port on which this server listens. */
-  this._port = undefined;
-
-  /** The socket associated with this. */
-  this._socket = null;
-
-  /** The handler used to process requests to this server. */
-  this._handler = new ServerHandler(this);
-
-  /** Naming information for this server. */
-  this._identity = new ServerIdentity();
-
-  /**
-   * Indicates when the server is to be shut down at the end of the request.
-   */
-  this._doQuit = false;
-
-  /**
-   * True if the socket in this is closed (and closure notifications have been
-   * sent and processed if the socket was ever opened), false otherwise.
-   */
-  this._socketClosed = true;
-
-  /**
-   * Used for tracking existing connections and ensuring that all connections
-   * are properly cleaned up before server shutdown; increases by 1 for every
-   * new incoming connection.
-   */
-  this._connectionGen = 0;
-
-  /**
-   * Hash of all open connections, indexed by connection number at time of
-   * creation.
-   */
-  this._connections = {};
-}
-nsHttpServer.prototype =
-{
-  classID: Components.ID("{54ef6f81-30af-4b1d-ac55-8ba811293e41}"),
-
-  // NSISERVERSOCKETLISTENER
-
-  /**
-   * Processes an incoming request coming in on the given socket and contained
-   * in the given transport.
-   *
-   * @param socket : nsIServerSocket
-   *   the socket through which the request was served
-   * @param trans : nsISocketTransport
-   *   the transport for the request/response
-   * @see nsIServerSocketListener.onSocketAccepted
-   */
-  onSocketAccepted: function(socket, trans)
-  {
-    dumpn("*** onSocketAccepted(socket=" + socket + ", trans=" + trans + ")");
-
-    dumpn(">>> new connection on " + trans.host + ":" + trans.port);
-
-    const SEGMENT_SIZE = 8192;
-    const SEGMENT_COUNT = 1024;
-    try
-    {
-      var input = trans.openInputStream(0, SEGMENT_SIZE, SEGMENT_COUNT)
-                       .QueryInterface(Ci.nsIAsyncInputStream);
-      var output = trans.openOutputStream(0, 0, 0);
-    }
-    catch (e)
-    {
-      dumpn("*** error opening transport streams: " + e);
-      trans.close(Cr.NS_BINDING_ABORTED);
-      return;
-    }
-
-    var connectionNumber = ++this._connectionGen;
-
-    try
-    {
-      var conn = new Connection(input, output, this, socket.port, trans.port,
-                                connectionNumber);
-      var reader = new RequestReader(conn);
-
-      // XXX add request timeout functionality here!
-
-      // Note: must use main thread here, or we might get a GC that will cause
-      //       threadsafety assertions.  We really need to fix XPConnect so that
-      //       you can actually do things in multi-threaded JS.  :-(
-      input.asyncWait(reader, 0, 0, gThreadManager.mainThread);
-    }
-    catch (e)
-    {
-      // Assume this connection can't be salvaged and bail on it completely;
-      // don't attempt to close it so that we can assert that any connection
-      // being closed is in this._connections.
-      dumpn("*** error in initial request-processing stages: " + e);
-      trans.close(Cr.NS_BINDING_ABORTED);
-      return;
-    }
-
-    this._connections[connectionNumber] = conn;
-    dumpn("*** starting connection " + connectionNumber);
-  },
-
-  /**
-   * Called when the socket associated with this is closed.
-   *
-   * @param socket : nsIServerSocket
-   *   the socket being closed
-   * @param status : nsresult
-   *   the reason the socket stopped listening (NS_BINDING_ABORTED if the server
-   *   was stopped using nsIHttpServer.stop)
-   * @see nsIServerSocketListener.onStopListening
-   */
-  onStopListening: function(socket, status)
-  {
-    dumpn(">>> shutting down server on port " + socket.port);
-    this._socketClosed = true;
-    if (!this._hasOpenConnections())
-    {
-      dumpn("*** no open connections, notifying async from onStopListening");
-
-      // Notify asynchronously so that any pending teardown in stop() has a
-      // chance to run first.
-      var self = this;
-      var stopEvent =
-        {
-          run: function()
-          {
-            dumpn("*** _notifyStopped async callback");
-            self._notifyStopped();
-          }
-        };
-      gThreadManager.currentThread
-                    .dispatch(stopEvent, Ci.nsIThread.DISPATCH_NORMAL);
-    }
-  },
-
-  // NSIHTTPSERVER
-
-  //
-  // see nsIHttpServer.start
-  //
-  start: function(port)
-  {
-    this._start(port, "localhost")
-  },
-
-  _start: function(port, host)
-  {
-    if (this._socket)
-      throw Cr.NS_ERROR_ALREADY_INITIALIZED;
-
-    this._port = port;
-    this._doQuit = this._socketClosed = false;
-
-    this._host = host;
-
-    // The listen queue needs to be long enough to handle
-    // network.http.max-connections-per-server concurrent connections,
-    // plus a safety margin in case some other process is talking to
-    // the server as well.
-    var prefs = getRootPrefBranch();
-    var maxConnections =
-      prefs.getIntPref("network.http.max-connections-per-server") + 5;
-
-    try
-    {
-      var loopback = true;
-      if (this._host != "127.0.0.1" && this._host != "localhost") {
-        var loopback = false;
-      }
-
-      var socket = new ServerSocket(this._port,
-                                    loopback, // true = localhost, false = everybody
-                                    maxConnections);
-      dumpn(">>> listening on port " + socket.port + ", " + maxConnections +
-            " pending connections");
-      socket.asyncListen(this);
-      this._identity._initialize(port, host, true);
-      this._socket = socket;
-    }
-    catch (e)
-    {
-      dumpn("!!! could not start server on port " + port + ": " + e);
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-    }
-  },
-
-  //
-  // see nsIHttpServer.stop
-  //
-  stop: function(callback)
-  {
-    if (!callback)
-      throw Cr.NS_ERROR_NULL_POINTER;
-    if (!this._socket)
-      throw Cr.NS_ERROR_UNEXPECTED;
-
-    this._stopCallback = typeof callback === "function"
-                       ? callback
-                       : function() { callback.onStopped(); };
-
-    dumpn(">>> stopping listening on port " + this._socket.port);
-    this._socket.close();
-    this._socket = null;
-
-    // We can't have this identity any more, and the port on which we're running
-    // this server now could be meaningless the next time around.
-    this._identity._teardown();
-
-    this._doQuit = false;
-
-    // socket-close notification and pending request completion happen async
-  },
-
-  //
-  // see nsIHttpServer.registerFile
-  //
-  registerFile: function(path, file)
-  {
-    if (file && (!file.exists() || file.isDirectory()))
-      throw Cr.NS_ERROR_INVALID_ARG;
-
-    this._handler.registerFile(path, file);
-  },
-
-  //
-  // see nsIHttpServer.registerDirectory
-  //
-  registerDirectory: function(path, directory)
-  {
-    // XXX true path validation!
-    if (path.charAt(0) != "/" ||
-        path.charAt(path.length - 1) != "/" ||
-        (directory &&
-         (!directory.exists() || !directory.isDirectory())))
-      throw Cr.NS_ERROR_INVALID_ARG;
-
-    // XXX determine behavior of nonexistent /foo/bar when a /foo/bar/ mapping
-    //     exists!
-
-    this._handler.registerDirectory(path, directory);
-  },
-
-  //
-  // see nsIHttpServer.registerPathHandler
-  //
-  registerPathHandler: function(path, handler)
-  {
-    this._handler.registerPathHandler(path, handler);
-  },
-
-  //
-  // see nsIHttpServer.registerErrorHandler
-  //
-  registerErrorHandler: function(code, handler)
-  {
-    this._handler.registerErrorHandler(code, handler);
-  },
-
-  //
-  // see nsIHttpServer.setIndexHandler
-  //
-  setIndexHandler: function(handler)
-  {
-    this._handler.setIndexHandler(handler);
-  },
-
-  //
-  // see nsIHttpServer.registerContentType
-  //
-  registerContentType: function(ext, type)
-  {
-    this._handler.registerContentType(ext, type);
-  },
-
-  //
-  // see nsIHttpServer.serverIdentity
-  //
-  get identity()
-  {
-    return this._identity;
-  },
-
-  //
-  // see nsIHttpServer.getState
-  //
-  getState: function(path, k)
-  {
-    return this._handler._getState(path, k);
-  },
-
-  //
-  // see nsIHttpServer.setState
-  //
-  setState: function(path, k, v)
-  {
-    return this._handler._setState(path, k, v);
-  },
-
-  //
-  // see nsIHttpServer.getSharedState
-  //
-  getSharedState: function(k)
-  {
-    return this._handler._getSharedState(k);
-  },
-
-  //
-  // see nsIHttpServer.setSharedState
-  //
-  setSharedState: function(k, v)
-  {
-    return this._handler._setSharedState(k, v);
-  },
-
-  //
-  // see nsIHttpServer.getObjectState
-  //
-  getObjectState: function(k)
-  {
-    return this._handler._getObjectState(k);
-  },
-
-  //
-  // see nsIHttpServer.setObjectState
-  //
-  setObjectState: function(k, v)
-  {
-    return this._handler._setObjectState(k, v);
-  },
-
-
-  // NSISUPPORTS
-
-  //
-  // see nsISupports.QueryInterface
-  //
-  QueryInterface: function(iid)
-  {
-    if (iid.equals(Ci.nsIHttpServer) ||
-        iid.equals(Ci.nsIServerSocketListener) ||
-        iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-
-  // NON-XPCOM PUBLIC API
-
-  /**
-   * Returns true iff this server is not running (and is not in the process of
-   * serving any requests still to be processed when the server was last
-   * stopped after being run).
-   */
-  isStopped: function()
-  {
-    return this._socketClosed && !this._hasOpenConnections();
-  },
-
-  // PRIVATE IMPLEMENTATION
-
-  /** True if this server has any open connections to it, false otherwise. */
-  _hasOpenConnections: function()
-  {
-    //
-    // If we have any open connections, they're tracked as numeric properties on
-    // |this._connections|.  The non-standard __count__ property could be used
-    // to check whether there are any properties, but standard-wise, even
-    // looking forward to ES5, there's no less ugly yet still O(1) way to do
-    // this.
-    //
-    for (var n in this._connections)
-      return true;
-    return false;
-  },
-
-  /** Calls the server-stopped callback provided when stop() was called. */
-  _notifyStopped: function()
-  {
-    NS_ASSERT(this._stopCallback !== null, "double-notifying?");
-    NS_ASSERT(!this._hasOpenConnections(), "should be done serving by now");
-
-    //
-    // NB: We have to grab this now, null out the member, *then* call the
-    //     callback here, or otherwise the callback could (indirectly) futz with
-    //     this._stopCallback by starting and immediately stopping this, at
-    //     which point we'd be nulling out a field we no longer have a right to
-    //     modify.
-    //
-    var callback = this._stopCallback;
-    this._stopCallback = null;
-    try
-    {
-      callback();
-    }
-    catch (e)
-    {
-      // not throwing because this is specified as being usually (but not
-      // always) asynchronous
-      dump("!!! error running onStopped callback: " + e + "\n");
-    }
-  },
-
-  /**
-   * Notifies this server that the given connection has been closed.
-   *
-   * @param connection : Connection
-   *   the connection that was closed
-   */
-  _connectionClosed: function(connection)
-  {
-    NS_ASSERT(connection.number in this._connections,
-              "closing a connection " + this + " that we never added to the " +
-              "set of open connections?");
-    NS_ASSERT(this._connections[connection.number] === connection,
-              "connection number mismatch?  " +
-              this._connections[connection.number]);
-    delete this._connections[connection.number];
-
-    // Fire a pending server-stopped notification if it's our responsibility.
-    if (!this._hasOpenConnections() && this._socketClosed)
-      this._notifyStopped();
-    // Bug 508125: Add a GC here else we'll use gigabytes of memory running
-    // mochitests. We can't rely on xpcshell doing an automated GC, as that
-    // would interfere with testing GC stuff...
-    gc();
-  },
-
-  /**
-   * Requests that the server be shut down when possible.
-   */
-  _requestQuit: function()
-  {
-    dumpn(">>> requesting a quit");
-    dumpStack();
-    this._doQuit = true;
-  }
-};
-
-
-//
-// RFC 2396 section 3.2.2:
-//
-// host        = hostname | IPv4address
-// hostname    = *( domainlabel "." ) toplabel [ "." ]
-// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
-// toplabel    = alpha | alpha *( alphanum | "-" ) alphanum
-// IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
-//
-
-const HOST_REGEX =
-  new RegExp("^(?:" +
-               // *( domainlabel "." )
-               "(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)*" +
-               // toplabel
-               "[a-z](?:[a-z0-9-]*[a-z0-9])?" +
-             "|" +
-               // IPv4 address 
-               "\\d+\\.\\d+\\.\\d+\\.\\d+" +
-             ")$",
-             "i");
-
-
-/**
- * Represents the identity of a server.  An identity consists of a set of
- * (scheme, host, port) tuples denoted as locations (allowing a single server to
- * serve multiple sites or to be used behind both HTTP and HTTPS proxies for any
- * host/port).  Any incoming request must be to one of these locations, or it
- * will be rejected with an HTTP 400 error.  One location, denoted as the
- * primary location, is the location assigned in contexts where a location
- * cannot otherwise be endogenously derived, such as for HTTP/1.0 requests.
- *
- * A single identity may contain at most one location per unique host/port pair;
- * other than that, no restrictions are placed upon what locations may
- * constitute an identity.
- */
-function ServerIdentity()
-{
-  /** The scheme of the primary location. */
-  this._primaryScheme = "http";
-
-  /** The hostname of the primary location. */
-  this._primaryHost = "127.0.0.1"
-
-  /** The port number of the primary location. */
-  this._primaryPort = -1;
-
-  /**
-   * The current port number for the corresponding server, stored so that a new
-   * primary location can always be set if the current one is removed.
-   */
-  this._defaultPort = -1;
-
-  /**
-   * Maps hosts to maps of ports to schemes, e.g. the following would represent
-   * https://example.com:789/ and http://example.org/:
-   *
-   *   {
-   *     "xexample.com": { 789: "https" },
-   *     "xexample.org": { 80: "http" }
-   *   }
-   *
-   * Note the "x" prefix on hostnames, which prevents collisions with special
-   * JS names like "prototype".
-   */
-  this._locations = { "xlocalhost": {} };
-}
-ServerIdentity.prototype =
-{
-  // NSIHTTPSERVERIDENTITY
-
-  //
-  // see nsIHttpServerIdentity.primaryScheme
-  //
-  get primaryScheme()
-  {
-    if (this._primaryPort === -1)
-      throw Cr.NS_ERROR_NOT_INITIALIZED;
-    return this._primaryScheme;
-  },
-
-  //
-  // see nsIHttpServerIdentity.primaryHost
-  //
-  get primaryHost()
-  {
-    if (this._primaryPort === -1)
-      throw Cr.NS_ERROR_NOT_INITIALIZED;
-    return this._primaryHost;
-  },
-
-  //
-  // see nsIHttpServerIdentity.primaryPort
-  //
-  get primaryPort()
-  {
-    if (this._primaryPort === -1)
-      throw Cr.NS_ERROR_NOT_INITIALIZED;
-    return this._primaryPort;
-  },
-
-  //
-  // see nsIHttpServerIdentity.add
-  //
-  add: function(scheme, host, port)
-  {
-    this._validate(scheme, host, port);
-
-    var entry = this._locations["x" + host];
-    if (!entry)
-      this._locations["x" + host] = entry = {};
-
-    entry[port] = scheme;
-  },
-
-  //
-  // see nsIHttpServerIdentity.remove
-  //
-  remove: function(scheme, host, port)
-  {
-    this._validate(scheme, host, port);
-
-    var entry = this._locations["x" + host];
-    if (!entry)
-      return false;
-
-    var present = port in entry;
-    delete entry[port];
-
-    if (this._primaryScheme == scheme &&
-        this._primaryHost == host &&
-        this._primaryPort == port &&
-        this._defaultPort !== -1)
-    {
-      // Always keep at least one identity in existence at any time, unless
-      // we're in the process of shutting down (the last condition above).
-      this._primaryPort = -1;
-      this._initialize(this._defaultPort, host, false);
-    }
-
-    return present;
-  },
-
-  //
-  // see nsIHttpServerIdentity.has
-  //
-  has: function(scheme, host, port)
-  {
-    this._validate(scheme, host, port);
-
-    return "x" + host in this._locations &&
-           scheme === this._locations["x" + host][port];
-  },
-
-  //
-  // see nsIHttpServerIdentity.has
-  //
-  getScheme: function(host, port)
-  {
-    this._validate("http", host, port);
-
-    var entry = this._locations["x" + host];
-    if (!entry)
-      return "";
-
-    return entry[port] || "";
-  },
-
-  //
-  // see nsIHttpServerIdentity.setPrimary
-  //
-  setPrimary: function(scheme, host, port)
-  {
-    this._validate(scheme, host, port);
-
-    this.add(scheme, host, port);
-
-    this._primaryScheme = scheme;
-    this._primaryHost = host;
-    this._primaryPort = port;
-  },
-
-
-  // NSISUPPORTS
-
-  //
-  // see nsISupports.QueryInterface
-  //
-  QueryInterface: function(iid)
-  {
-    if (iid.equals(Ci.nsIHttpServerIdentity) || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-
-  // PRIVATE IMPLEMENTATION
-
-  /**
-   * Initializes the primary name for the corresponding server, based on the
-   * provided port number.
-   */
-  _initialize: function(port, host, addSecondaryDefault)
-  {
-    this._host = host;
-    if (this._primaryPort !== -1)
-      this.add("http", host, port);
-    else
-      this.setPrimary("http", "localhost", port);
-    this._defaultPort = port;
-
-    // Only add this if we're being called at server startup
-    if (addSecondaryDefault && host != "127.0.0.1")
-      this.add("http", "127.0.0.1", port);
-  },
-
-  /**
-   * Called at server shutdown time, unsets the primary location only if it was
-   * the default-assigned location and removes the default location from the
-   * set of locations used.
-   */
-  _teardown: function()
-  {
-    if (this._host != "127.0.0.1") {
-      // Not the default primary location, nothing special to do here
-      this.remove("http", "127.0.0.1", this._defaultPort);
-    }
-    
-    // This is a *very* tricky bit of reasoning here; make absolutely sure the
-    // tests for this code pass before you commit changes to it.
-    if (this._primaryScheme == "http" &&
-        this._primaryHost == this._host &&
-        this._primaryPort == this._defaultPort)
-    {
-      // Make sure we don't trigger the readding logic in .remove(), then remove
-      // the default location.
-      var port = this._defaultPort;
-      this._defaultPort = -1;
-      this.remove("http", this._host, port);
-
-      // Ensure a server start triggers the setPrimary() path in ._initialize()
-      this._primaryPort = -1;
-    }
-    else
-    {
-      // No reason not to remove directly as it's not our primary location
-      this.remove("http", this._host, this._defaultPort);
-    }
-  },
-
-  /**
-   * Ensures scheme, host, and port are all valid with respect to RFC 2396.
-   *
-   * @throws NS_ERROR_ILLEGAL_VALUE
-   *   if any argument doesn't match the corresponding production
-   */
-  _validate: function(scheme, host, port)
-  {
-    if (scheme !== "http" && scheme !== "https")
-    {
-      dumpn("*** server only supports http/https schemes: '" + scheme + "'");
-      dumpStack();
-      throw Cr.NS_ERROR_ILLEGAL_VALUE;
-    }
-    if (!HOST_REGEX.test(host))
-    {
-      dumpn("*** unexpected host: '" + host + "'");
-      throw Cr.NS_ERROR_ILLEGAL_VALUE;
-    }
-    if (port < 0 || port > 65535)
-    {
-      dumpn("*** unexpected port: '" + port + "'");
-      throw Cr.NS_ERROR_ILLEGAL_VALUE;
-    }
-  }
-};
-
-
-/**
- * Represents a connection to the server (and possibly in the future the thread
- * on which the connection is processed).
- *
- * @param input : nsIInputStream
- *   stream from which incoming data on the connection is read
- * @param output : nsIOutputStream
- *   stream to write data out the connection
- * @param server : nsHttpServer
- *   the server handling the connection
- * @param port : int
- *   the port on which the server is running
- * @param outgoingPort : int
- *   the outgoing port used by this connection
- * @param number : uint
- *   a serial number used to uniquely identify this connection
- */
-function Connection(input, output, server, port, outgoingPort, number)
-{
-  dumpn("*** opening new connection " + number + " on port " + outgoingPort);
-
-  /** Stream of incoming data. */
-  this.input = input;
-
-  /** Stream for outgoing data. */
-  this.output = output;
-
-  /** The server associated with this request. */
-  this.server = server;
-
-  /** The port on which the server is running. */
-  this.port = port;
-
-  /** The outgoing poort used by this connection. */
-  this._outgoingPort = outgoingPort;
-
-  /** The serial number of this connection. */
-  this.number = number;
-
-  /**
-   * The request for which a response is being generated, null if the
-   * incoming request has not been fully received or if it had errors.
-   */
-  this.request = null;
-
-  /** State variables for debugging. */
-  this._closed = this._processed = false;
-}
-Connection.prototype =
-{
-  /** Closes this connection's input/output streams. */
-  close: function()
-  {
-    dumpn("*** closing connection " + this.number +
-          " on port " + this._outgoingPort);
-
-    this.input.close();
-    this.output.close();
-    this._closed = true;
-
-    var server = this.server;
-    server._connectionClosed(this);
-
-    // If an error triggered a server shutdown, act on it now
-    if (server._doQuit)
-      server.stop(function() { /* not like we can do anything better */ });
-  },
-
-  /**
-   * Initiates processing of this connection, using the data in the given
-   * request.
-   *
-   * @param request : Request
-   *   the request which should be processed
-   */
-  process: function(request)
-  {
-    NS_ASSERT(!this._closed && !this._processed);
-
-    this._processed = true;
-
-    this.request = request;
-    this.server._handler.handleResponse(this);
-  },
-
-  /**
-   * Initiates processing of this connection, generating a response with the
-   * given HTTP error code.
-   *
-   * @param code : uint
-   *   an HTTP code, so in the range [0, 1000)
-   * @param request : Request
-   *   incomplete data about the incoming request (since there were errors
-   *   during its processing
-   */
-  processError: function(code, request)
-  {
-    NS_ASSERT(!this._closed && !this._processed);
-
-    this._processed = true;
-    this.request = request;
-    this.server._handler.handleError(code, this);
-  },
-
-  /** Converts this to a string for debugging purposes. */
-  toString: function()
-  {
-    return "<Connection(" + this.number +
-           (this.request ? ", " + this.request.path : "") +"): " +
-           (this._closed ? "closed" : "open") + ">";
-  }
-};
-
-
-
-/** Returns an array of count bytes from the given input stream. */
-function readBytes(inputStream, count)
-{
-  return new BinaryInputStream(inputStream).readByteArray(count);
-}
-
-
-
-/** Request reader processing states; see RequestReader for details. */
-const READER_IN_REQUEST_LINE = 0;
-const READER_IN_HEADERS      = 1;
-const READER_IN_BODY         = 2;
-const READER_FINISHED        = 3;
-
-
-/**
- * Reads incoming request data asynchronously, does any necessary preprocessing,
- * and forwards it to the request handler.  Processing occurs in three states:
- *
- *   READER_IN_REQUEST_LINE     Reading the request's status line
- *   READER_IN_HEADERS          Reading headers in the request
- *   READER_IN_BODY             Reading the body of the request
- *   READER_FINISHED            Entire request has been read and processed
- *
- * During the first two stages, initial metadata about the request is gathered
- * into a Request object.  Once the status line and headers have been processed,
- * we start processing the body of the request into the Request.  Finally, when
- * the entire body has been read, we create a Response and hand it off to the
- * ServerHandler to be given to the appropriate request handler.
- *
- * @param connection : Connection
- *   the connection for the request being read
- */
-function RequestReader(connection)
-{
-  /** Connection metadata for this request. */
-  this._connection = connection;
-
-  /**
-   * A container providing line-by-line access to the raw bytes that make up the
-   * data which has been read from the connection but has not yet been acted
-   * upon (by passing it to the request handler or by extracting request
-   * metadata from it).
-   */
-  this._data = new LineData();
-
-  /**
-   * The amount of data remaining to be read from the body of this request.
-   * After all headers in the request have been read this is the value in the
-   * Content-Length header, but as the body is read its value decreases to zero.
-   */
-  this._contentLength = 0;
-
-  /** The current state of parsing the incoming request. */
-  this._state = READER_IN_REQUEST_LINE;
-
-  /** Metadata constructed from the incoming request for the request handler. */
-  this._metadata = new Request(connection.port);
-
-  /**
-   * Used to preserve state if we run out of line data midway through a
-   * multi-line header.  _lastHeaderName stores the name of the header, while
-   * _lastHeaderValue stores the value we've seen so far for the header.
-   *
-   * These fields are always either both undefined or both strings.
-   */
-  this._lastHeaderName = this._lastHeaderValue = undefined;
-}
-RequestReader.prototype =
-{
-  // NSIINPUTSTREAMCALLBACK
-
-  /**
-   * Called when more data from the incoming request is available.  This method
-   * then reads the available data from input and deals with that data as
-   * necessary, depending upon the syntax of already-downloaded data.
-   *
-   * @param input : nsIAsyncInputStream
-   *   the stream of incoming data from the connection
-   */
-  onInputStreamReady: function(input)
-  {
-    dumpn("*** onInputStreamReady(input=" + input + ") on thread " +
-          gThreadManager.currentThread + " (main is " +
-          gThreadManager.mainThread + ")");
-    dumpn("*** this._state == " + this._state);
-
-    // Handle cases where we get more data after a request error has been
-    // discovered but *before* we can close the connection.
-    var data = this._data;
-    if (!data)
-      return;
-
-    try
-    {
-      data.appendBytes(readBytes(input, input.available()));
-    }
-    catch (e)
-    {
-      if (streamClosed(e))
-      {
-        dumpn("*** WARNING: unexpected error when reading from socket; will " +
-              "be treated as if the input stream had been closed");
-        dumpn("*** WARNING: actual error was: " + e);
-      }
-
-      // We've lost a race -- input has been closed, but we're still expecting
-      // to read more data.  available() will throw in this case, and since
-      // we're dead in the water now, destroy the connection.
-      dumpn("*** onInputStreamReady called on a closed input, destroying " +
-            "connection");
-      this._connection.close();
-      return;
-    }
-
-    switch (this._state)
-    {
-      default:
-        NS_ASSERT(false, "invalid state: " + this._state);
-        break;
-
-      case READER_IN_REQUEST_LINE:
-        if (!this._processRequestLine())
-          break;
-        /* fall through */
-
-      case READER_IN_HEADERS:
-        if (!this._processHeaders())
-          break;
-        /* fall through */
-
-      case READER_IN_BODY:
-        this._processBody();
-    }
-
-    if (this._state != READER_FINISHED)
-      input.asyncWait(this, 0, 0, gThreadManager.currentThread);
-  },
-
-  //
-  // see nsISupports.QueryInterface
-  //
-  QueryInterface: function(aIID)
-  {
-    if (aIID.equals(Ci.nsIInputStreamCallback) ||
-        aIID.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-
-  // PRIVATE API
-
-  /**
-   * Processes unprocessed, downloaded data as a request line.
-   *
-   * @returns boolean
-   *   true iff the request line has been fully processed
-   */
-  _processRequestLine: function()
-  {
-    NS_ASSERT(this._state == READER_IN_REQUEST_LINE);
-
-    // Servers SHOULD ignore any empty line(s) received where a Request-Line
-    // is expected (section 4.1).
-    var data = this._data;
-    var line = {};
-    var readSuccess;
-    while ((readSuccess = data.readLine(line)) && line.value == "")
-      dumpn("*** ignoring beginning blank line...");
-
-    // if we don't have a full line, wait until we do
-    if (!readSuccess)
-      return false;
-
-    // we have the first non-blank line
-    try
-    {
-      this._parseRequestLine(line.value);
-      this._state = READER_IN_HEADERS;
-      return true;
-    }
-    catch (e)
-    {
-      this._handleError(e);
-      return false;
-    }
-  },
-
-  /**
-   * Processes stored data, assuming it is either at the beginning or in
-   * the middle of processing request headers.
-   *
-   * @returns boolean
-   *   true iff header data in the request has been fully processed
-   */
-  _processHeaders: function()
-  {
-    NS_ASSERT(this._state == READER_IN_HEADERS);
-
-    // XXX things to fix here:
-    //
-    // - need to support RFC 2047-encoded non-US-ASCII characters
-
-    try
-    {
-      var done = this._parseHeaders();
-      if (done)
-      {
-        var request = this._metadata;
-
-        // XXX this is wrong for requests with transfer-encodings applied to
-        //     them, particularly chunked (which by its nature can have no
-        //     meaningful Content-Length header)!
-        this._contentLength = request.hasHeader("Content-Length")
-                            ? parseInt(request.getHeader("Content-Length"), 10)
-                            : 0;
-        dumpn("_processHeaders, Content-length=" + this._contentLength);
-
-        this._state = READER_IN_BODY;
-      }
-      return done;
-    }
-    catch (e)
-    {
-      this._handleError(e);
-      return false;
-    }
-  },
-
-  /**
-   * Processes stored data, assuming it is either at the beginning or in
-   * the middle of processing the request body.
-   *
-   * @returns boolean
-   *   true iff the request body has been fully processed
-   */
-  _processBody: function()
-  {
-    NS_ASSERT(this._state == READER_IN_BODY);
-
-    // XXX handle chunked transfer-coding request bodies!
-
-    try
-    {
-      if (this._contentLength > 0)
-      {
-        var data = this._data.purge();
-        var count = Math.min(data.length, this._contentLength);
-        dumpn("*** loading data=" + data + " len=" + data.length +
-              " excess=" + (data.length - count));
-
-        var bos = new BinaryOutputStream(this._metadata._bodyOutputStream);
-        bos.writeByteArray(data, count);
-        this._contentLength -= count;
-      }
-
-      dumpn("*** remaining body data len=" + this._contentLength);
-      if (this._contentLength == 0)
-      {
-        this._validateRequest();
-        this._state = READER_FINISHED;
-        this._handleResponse();
-        return true;
-      }
-      
-      return false;
-    }
-    catch (e)
-    {
-      this._handleError(e);
-      return false;
-    }
-  },
-
-  /**
-   * Does various post-header checks on the data in this request.
-   *
-   * @throws : HttpError
-   *   if the request was malformed in some way
-   */
-  _validateRequest: function()
-  {
-    NS_ASSERT(this._state == READER_IN_BODY);
-
-    dumpn("*** _validateRequest");
-
-    var metadata = this._metadata;
-    var headers = metadata._headers;
-
-    // 19.6.1.1 -- servers MUST report 400 to HTTP/1.1 requests w/o Host header
-    var identity = this._connection.server.identity;
-    if (metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1))
-    {
-      if (!headers.hasHeader("Host"))
-      {
-        dumpn("*** malformed HTTP/1.1 or greater request with no Host header!");
-        throw HTTP_400;
-      }
-
-      // If the Request-URI wasn't absolute, then we need to determine our host.
-      // We have to determine what scheme was used to access us based on the
-      // server identity data at this point, because the request just doesn't
-      // contain enough data on its own to do this, sadly.
-      if (!metadata._host)
-      {
-        var host, port;
-        var hostPort = headers.getHeader("Host");
-        var colon = hostPort.indexOf(":");
-        if (colon < 0)
-        {
-          host = hostPort;
-          port = "";
-        }
-        else
-        {
-          host = hostPort.substring(0, colon);
-          port = hostPort.substring(colon + 1);
-        }
-
-        // NB: We allow an empty port here because, oddly, a colon may be
-        //     present even without a port number, e.g. "example.com:"; in this
-        //     case the default port applies.
-        if (!HOST_REGEX.test(host) || !/^\d*$/.test(port))
-        {
-          dumpn("*** malformed hostname (" + hostPort + ") in Host " +
-                "header, 400 time");
-          throw HTTP_400;
-        }
-
-        // If we're not given a port, we're stuck, because we don't know what
-        // scheme to use to look up the correct port here, in general.  Since
-        // the HTTPS case requires a tunnel/proxy and thus requires that the
-        // requested URI be absolute (and thus contain the necessary
-        // information), let's assume HTTP will prevail and use that.
-        port = +port || 80;
-
-        var scheme = identity.getScheme(host, port);
-        if (!scheme)
-        {
-          dumpn("*** unrecognized hostname (" + hostPort + ") in Host " +
-                "header, 400 time");
-          throw HTTP_400;
-        }
-
-        metadata._scheme = scheme;
-        metadata._host = host;
-        metadata._port = port;
-      }
-    }
-    else
-    {
-      NS_ASSERT(metadata._host === undefined,
-                "HTTP/1.0 doesn't allow absolute paths in the request line!");
-
-      metadata._scheme = identity.primaryScheme;
-      metadata._host = identity.primaryHost;
-      metadata._port = identity.primaryPort;
-    }
-
-    NS_ASSERT(identity.has(metadata._scheme, metadata._host, metadata._port),
-              "must have a location we recognize by now!");
-  },
-
-  /**
-   * Handles responses in case of error, either in the server or in the request.
-   *
-   * @param e
-   *   the specific error encountered, which is an HttpError in the case where
-   *   the request is in some way invalid or cannot be fulfilled; if this isn't
-   *   an HttpError we're going to be paranoid and shut down, because that
-   *   shouldn't happen, ever
-   */
-  _handleError: function(e)
-  {
-    // Don't fall back into normal processing!
-    this._state = READER_FINISHED;
-
-    var server = this._connection.server;
-    if (e instanceof HttpError)
-    {
-      var code = e.code;
-    }
-    else
-    {
-      dumpn("!!! UNEXPECTED ERROR: " + e +
-            (e.lineNumber ? ", line " + e.lineNumber : ""));
-
-      // no idea what happened -- be paranoid and shut down
-      code = 500;
-      server._requestQuit();
-    }
-
-    // make attempted reuse of data an error
-    this._data = null;
-
-    this._connection.processError(code, this._metadata);
-  },
-
-  /**
-   * Now that we've read the request line and headers, we can actually hand off
-   * the request to be handled.
-   *
-   * This method is called once per request, after the request line and all
-   * headers and the body, if any, have been received.
-   */
-  _handleResponse: function()
-  {
-    NS_ASSERT(this._state == READER_FINISHED);
-
-    // We don't need the line-based data any more, so make attempted reuse an
-    // error.
-    this._data = null;
-
-    this._connection.process(this._metadata);
-  },
-
-
-  // PARSING
-
-  /**
-   * Parses the request line for the HTTP request associated with this.
-   *
-   * @param line : string
-   *   the request line
-   */
-  _parseRequestLine: function(line)
-  {
-    NS_ASSERT(this._state == READER_IN_REQUEST_LINE);
-
-    dumpn("*** _parseRequestLine('" + line + "')");
-
-    var metadata = this._metadata;
-
-    // clients and servers SHOULD accept any amount of SP or HT characters
-    // between fields, even though only a single SP is required (section 19.3)
-    var request = line.split(/[ \t]+/);
-    if (!request || request.length != 3)
-      throw HTTP_400;
-
-    metadata._method = request[0];
-
-    // get the HTTP version
-    var ver = request[2];
-    var match = ver.match(/^HTTP\/(\d+\.\d+)$/);
-    if (!match)
-      throw HTTP_400;
-
-    // determine HTTP version
-    try
-    {
-      metadata._httpVersion = new nsHttpVersion(match[1]);
-      if (!metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_0))
-        throw "unsupported HTTP version";
-    }
-    catch (e)
-    {
-      // we support HTTP/1.0 and HTTP/1.1 only
-      throw HTTP_501;
-    }
-
-
-    var fullPath = request[1];
-    var serverIdentity = this._connection.server.identity;
-
-    var scheme, host, port;
-
-    if (fullPath.charAt(0) != "/")
-    {
-      // No absolute paths in the request line in HTTP prior to 1.1
-      if (!metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1))
-        throw HTTP_400;
-
-      try
-      {
-        var uri = Cc["@mozilla.org/network/io-service;1"]
-                    .getService(Ci.nsIIOService)
-                    .newURI(fullPath, null, null);
-        fullPath = uri.path;
-        scheme = uri.scheme;
-        host = metadata._host = uri.asciiHost;
-        port = uri.port;
-        if (port === -1)
-        {
-          if (scheme === "http")
-            port = 80;
-          else if (scheme === "https")
-            port = 443;
-          else
-            throw HTTP_400;
-        }
-      }
-      catch (e)
-      {
-        // If the host is not a valid host on the server, the response MUST be a
-        // 400 (Bad Request) error message (section 5.2).  Alternately, the URI
-        // is malformed.
-        throw HTTP_400;
-      }
-
-      if (!serverIdentity.has(scheme, host, port) || fullPath.charAt(0) != "/")
-        throw HTTP_400;
-    }
-
-    var splitter = fullPath.indexOf("?");
-    if (splitter < 0)
-    {
-      // _queryString already set in ctor
-      metadata._path = fullPath;
-    }
-    else
-    {
-      metadata._path = fullPath.substring(0, splitter);
-      metadata._queryString = fullPath.substring(splitter + 1);
-    }
-
-    metadata._scheme = scheme;
-    metadata._host = host;
-    metadata._port = port;
-  },
-
-  /**
-   * Parses all available HTTP headers in this until the header-ending CRLFCRLF,
-   * adding them to the store of headers in the request.
-   *
-   * @throws
-   *   HTTP_400 if the headers are malformed
-   * @returns boolean
-   *   true if all headers have now been processed, false otherwise
-   */
-  _parseHeaders: function()
-  {
-    NS_ASSERT(this._state == READER_IN_HEADERS);
-
-    dumpn("*** _parseHeaders");
-
-    var data = this._data;
-
-    var headers = this._metadata._headers;
-    var lastName = this._lastHeaderName;
-    var lastVal = this._lastHeaderValue;
-
-    var line = {};
-    while (true)
-    {
-      NS_ASSERT(!((lastVal === undefined) ^ (lastName === undefined)),
-                lastName === undefined ?
-                  "lastVal without lastName?  lastVal: '" + lastVal + "'" :
-                  "lastName without lastVal?  lastName: '" + lastName + "'");
-
-      if (!data.readLine(line))
-      {
-        // save any data we have from the header we might still be processing
-        this._lastHeaderName = lastName;
-        this._lastHeaderValue = lastVal;
-        return false;
-      }
-
-      var lineText = line.value;
-      var firstChar = lineText.charAt(0);
-
-      // blank line means end of headers
-      if (lineText == "")
-      {
-        // we're finished with the previous header
-        if (lastName)
-        {
-          try
-          {
-            headers.setHeader(lastName, lastVal, true);
-          }
-          catch (e)
-          {
-            dumpn("*** e == " + e);
-            throw HTTP_400;
-          }
-        }
-        else
-        {
-          // no headers in request -- valid for HTTP/1.0 requests
-        }
-
-        // either way, we're done processing headers
-        this._state = READER_IN_BODY;
-        return true;
-      }
-      else if (firstChar == " " || firstChar == "\t")
-      {
-        // multi-line header if we've already seen a header line
-        if (!lastName)
-        {
-          // we don't have a header to continue!
-          throw HTTP_400;
-        }
-
-        // append this line's text to the value; starts with SP/HT, so no need
-        // for separating whitespace
-        lastVal += lineText;
-      }
-      else
-      {
-        // we have a new header, so set the old one (if one existed)
-        if (lastName)
-        {
-          try
-          {
-            headers.setHeader(lastName, lastVal, true);
-          }
-          catch (e)
-          {
-            dumpn("*** e == " + e);
-            throw HTTP_400;
-          }
-        }
-
-        var colon = lineText.indexOf(":"); // first colon must be splitter
-        if (colon < 1)
-        {
-          // no colon or missing header field-name
-          throw HTTP_400;
-        }
-
-        // set header name, value (to be set in the next loop, usually)
-        lastName = lineText.substring(0, colon);
-        lastVal = lineText.substring(colon + 1);
-      } // empty, continuation, start of header
-    } // while (true)
-  }
-};
-
-
-/** The character codes for CR and LF. */
-const CR = 0x0D, LF = 0x0A;
-
-/**
- * Calculates the number of characters before the first CRLF pair in array, or
- * -1 if the array contains no CRLF pair.
- *
- * @param array : Array
- *   an array of numbers in the range [0, 256), each representing a single
- *   character; the first CRLF is the lowest index i where
- *   |array[i] == "\r".charCodeAt(0)| and |array[i+1] == "\n".charCodeAt(0)|,
- *   if such an |i| exists, and -1 otherwise
- * @param start : uint
- *   start index from which to begin searching in array
- * @returns int
- *   the index of the first CRLF if any were present, -1 otherwise
- */
-function findCRLF(array, start)
-{
-  for (var i = array.indexOf(CR, start); i >= 0; i = array.indexOf(CR, i + 1))
-  {
-    if (array[i + 1] == LF)
-      return i;
-  }
-  return -1;
-}
-
-
-/**
- * A container which provides line-by-line access to the arrays of bytes with
- * which it is seeded.
- */
-function LineData()
-{
-  /** An array of queued bytes from which to get line-based characters. */
-  this._data = [];
-
-  /** Start index from which to search for CRLF. */
-  this._start = 0;
-}
-LineData.prototype =
-{
-  /**
-   * Appends the bytes in the given array to the internal data cache maintained
-   * by this.
-   */
-  appendBytes: function(bytes)
-  {
-    var count = bytes.length;
-    var quantum = 262144; // just above half SpiderMonkey's argument-count limit
-    if (count < quantum)
-    {
-      Array.prototype.push.apply(this._data, bytes);
-      return;
-    }
-
-    // Large numbers of bytes may cause Array.prototype.push to be called with
-    // more arguments than the JavaScript engine supports.  In that case append
-    // bytes in fixed-size amounts until all bytes are appended.
-    for (var start = 0; start < count; start += quantum)
-    {
-      var slice = bytes.slice(start, Math.min(start + quantum, count));
-      Array.prototype.push.apply(this._data, slice);
-    }
-  },
-
-  /**
-   * Removes and returns a line of data, delimited by CRLF, from this.
-   *
-   * @param out
-   *   an object whose "value" property will be set to the first line of text
-   *   present in this, sans CRLF, if this contains a full CRLF-delimited line
-   *   of text; if this doesn't contain enough data, the value of the property
-   *   is undefined
-   * @returns boolean
-   *   true if a full line of data could be read from the data in this, false
-   *   otherwise
-   */
-  readLine: function(out)
-  {
-    var data = this._data;
-    var length = findCRLF(data, this._start);
-    if (length < 0)
-    {
-      this._start = data.length;
-      return false;
-    }
-
-    // Reset for future lines.
-    this._start = 0;
-
-    //
-    // We have the index of the CR, so remove all the characters, including
-    // CRLF, from the array with splice, and convert the removed array
-    // (excluding the trailing CRLF characters) into the corresponding string.
-    //
-    var leading = data.splice(0, length + 2);
-    var quantum = 262144;
-    var line = "";
-    for (var start = 0; start < length; start += quantum)
-    {
-      var slice = leading.slice(start, Math.min(start + quantum, length));
-      line += String.fromCharCode.apply(null, slice);
-    }
-
-    out.value = line;
-    return true;
-  },
-
-  /**
-   * Removes the bytes currently within this and returns them in an array.
-   *
-   * @returns Array
-   *   the bytes within this when this method is called
-   */
-  purge: function()
-  {
-    var data = this._data;
-    this._data = [];
-    return data;
-  }
-};
-
-
-
-/**
- * Creates a request-handling function for an nsIHttpRequestHandler object.
- */
-function createHandlerFunc(handler)
-{
-  return function(metadata, response) { handler.handle(metadata, response); };
-}
-
-
-/**
- * The default handler for directories; writes an HTML response containing a
- * slightly-formatted directory listing.
- */
-function defaultIndexHandler(metadata, response)
-{
-  response.setHeader("Content-Type", "text/html", false);
-
-  var path = htmlEscape(decodeURI(metadata.path));
-
-  //
-  // Just do a very basic bit of directory listings -- no need for too much
-  // fanciness, especially since we don't have a style sheet in which we can
-  // stick rules (don't want to pollute the default path-space).
-  //
-
-  var body = '<html>\
-                <head>\
-                  <title>' + path + '</title>\
-                </head>\
-                <body>\
-                  <h1>' + path + '</h1>\
-                  <ol style="list-style-type: none">';
-
-  var directory = metadata.getProperty("directory");
-  NS_ASSERT(directory && directory.isDirectory());
-
-  var fileList = [];
-  var files = directory.directoryEntries;
-  while (files.hasMoreElements())
-  {
-    var f = files.getNext().QueryInterface(Ci.nsIFile);
-    var name = f.leafName;
-    if (!f.isHidden() &&
-        (name.charAt(name.length - 1) != HIDDEN_CHAR ||
-         name.charAt(name.length - 2) == HIDDEN_CHAR))
-      fileList.push(f);
-  }
-
-  fileList.sort(fileSort);
-
-  for (var i = 0; i < fileList.length; i++)
-  {
-    var file = fileList[i];
-    try
-    {
-      var name = file.leafName;
-      if (name.charAt(name.length - 1) == HIDDEN_CHAR)
-        name = name.substring(0, name.length - 1);
-      var sep = file.isDirectory() ? "/" : "";
-
-      // Note: using " to delimit the attribute here because encodeURIComponent
-      //       passes through '.
-      var item = '<li><a href="' + encodeURIComponent(name) + sep + '">' +
-                   htmlEscape(name) + sep +
-                 '</a></li>';
-
-      body += item;
-    }
-    catch (e) { /* some file system error, ignore the file */ }
-  }
-
-  body    += '    </ol>\
-                </body>\
-              </html>';
-
-  response.bodyOutputStream.write(body, body.length);
-}
-
-/**
- * Sorts a and b (nsIFile objects) into an aesthetically pleasing order.
- */
-function fileSort(a, b)
-{
-  var dira = a.isDirectory(), dirb = b.isDirectory();
-
-  if (dira && !dirb)
-    return -1;
-  if (dirb && !dira)
-    return 1;
-
-  var namea = a.leafName.toLowerCase(), nameb = b.leafName.toLowerCase();
-  return nameb > namea ? -1 : 1;
-}
-
-
-/**
- * Converts an externally-provided path into an internal path for use in
- * determining file mappings.
- *
- * @param path
- *   the path to convert
- * @param encoded
- *   true if the given path should be passed through decodeURI prior to
- *   conversion
- * @throws URIError
- *   if path is incorrectly encoded
- */
-function toInternalPath(path, encoded)
-{
-  if (encoded)
-    path = decodeURI(path);
-
-  var comps = path.split("/");
-  for (var i = 0, sz = comps.length; i < sz; i++)
-  {
-    var comp = comps[i];
-    if (comp.charAt(comp.length - 1) == HIDDEN_CHAR)
-      comps[i] = comp + HIDDEN_CHAR;
-  }
-  return comps.join("/");
-}
-
-
-/**
- * Adds custom-specified headers for the given file to the given response, if
- * any such headers are specified.
- *
- * @param file
- *   the file on the disk which is to be written
- * @param metadata
- *   metadata about the incoming request
- * @param response
- *   the Response to which any specified headers/data should be written
- * @throws HTTP_500
- *   if an error occurred while processing custom-specified headers
- */
-function maybeAddHeaders(file, metadata, response)
-{
-  var name = file.leafName;
-  if (name.charAt(name.length - 1) == HIDDEN_CHAR)
-    name = name.substring(0, name.length - 1);
-
-  var headerFile = file.parent;
-  headerFile.append(name + HEADERS_SUFFIX);
-
-  if (!headerFile.exists())
-    return;
-
-  const PR_RDONLY = 0x01;
-  var fis = new FileInputStream(headerFile, PR_RDONLY, 0444,
-                                Ci.nsIFileInputStream.CLOSE_ON_EOF);
-
-  try
-  {
-    var lis = new ConverterInputStream(fis, "UTF-8", 1024, 0x0);
-    lis.QueryInterface(Ci.nsIUnicharLineInputStream);
-
-    var line = {value: ""};
-    var more = lis.readLine(line);
-
-    if (!more && line.value == "")
-      return;
-
-
-    // request line
-
-    var status = line.value;
-    if (status.indexOf("HTTP ") == 0)
-    {
-      status = status.substring(5);
-      var space = status.indexOf(" ");
-      var code, description;
-      if (space < 0)
-      {
-        code = status;
-        description = "";
-      }
-      else
-      {
-        code = status.substring(0, space);
-        description = status.substring(space + 1, status.length);
-      }
-    
-      response.setStatusLine(metadata.httpVersion, parseInt(code, 10), description);
-
-      line.value = "";
-      more = lis.readLine(line);
-    }
-
-    // headers
-    while (more || line.value != "")
-    {
-      var header = line.value;
-      var colon = header.indexOf(":");
-
-      response.setHeader(header.substring(0, colon),
-                         header.substring(colon + 1, header.length),
-                         false); // allow overriding server-set headers
-
-      line.value = "";
-      more = lis.readLine(line);
-    }
-  }
-  catch (e)
-  {
-    dumpn("WARNING: error in headers for " + metadata.path + ": " + e);
-    throw HTTP_500;
-  }
-  finally
-  {
-    fis.close();
-  }
-}
-
-
-/**
- * An object which handles requests for a server, executing default and
- * overridden behaviors as instructed by the code which uses and manipulates it.
- * Default behavior includes the paths / and /trace (diagnostics), with some
- * support for HTTP error pages for various codes and fallback to HTTP 500 if
- * those codes fail for any reason.
- *
- * @param server : nsHttpServer
- *   the server in which this handler is being used
- */
-function ServerHandler(server)
-{
-  // FIELDS
-
-  /**
-   * The nsHttpServer instance associated with this handler.
-   */
-  this._server = server;
-
-  /**
-   * A FileMap object containing the set of path->nsILocalFile mappings for
-   * all directory mappings set in the server (e.g., "/" for /var/www/html/,
-   * "/foo/bar/" for /local/path/, and "/foo/bar/baz/" for /local/path2).
-   *
-   * Note carefully: the leading and trailing "/" in each path (not file) are
-   * removed before insertion to simplify the code which uses this.  You have
-   * been warned!
-   */
-  this._pathDirectoryMap = new FileMap();
-
-  /**
-   * Custom request handlers for the server in which this resides.  Path-handler
-   * pairs are stored as property-value pairs in this property.
-   *
-   * @see ServerHandler.prototype._defaultPaths
-   */
-  this._overridePaths = {};
-  
-  /**
-   * Custom request handlers for the error handlers in the server in which this
-   * resides.  Path-handler pairs are stored as property-value pairs in this
-   * property.
-   *
-   * @see ServerHandler.prototype._defaultErrors
-   */
-  this._overrideErrors = {};
-
-  /**
-   * Maps file extensions to their MIME types in the server, overriding any
-   * mapping that might or might not exist in the MIME service.
-   */
-  this._mimeMappings = {};
-
-  /**
-   * The default handler for requests for directories, used to serve directories
-   * when no index file is present.
-   */
-  this._indexHandler = defaultIndexHandler;
-
-  /** Per-path state storage for the server. */
-  this._state = {};
-
-  /** Entire-server state storage. */
-  this._sharedState = {};
-
-  /** Entire-server state storage for nsISupports values. */
-  this._objectState = {};
-}
-ServerHandler.prototype =
-{
-  // PUBLIC API
-
-  /**
-   * Handles a request to this server, responding to the request appropriately
-   * and initiating server shutdown if necessary.
-   *
-   * This method never throws an exception.
-   *
-   * @param connection : Connection
-   *   the connection for this request
-   */
-  handleResponse: function(connection)
-  {
-    var request = connection.request;
-    var response = new Response(connection);
-
-    var path = request.path;
-    dumpn("*** path == " + path);
-
-    try
-    {
-      try
-      {
-        if (path in this._overridePaths)
-        {
-          // explicit paths first, then files based on existing directory mappings,
-          // then (if the file doesn't exist) built-in server default paths
-          dumpn("calling override for " + path);
-          this._overridePaths[path](request, response);
-        }
-        else
-        {
-          this._handleDefault(request, response);
-        }
-      }
-      catch (e)
-      {
-        if (response.partiallySent())
-        {
-          response.abort(e);
-          return;
-        }
-
-        if (!(e instanceof HttpError))
-        {
-          dumpn("*** unexpected error: e == " + e);
-          throw HTTP_500;
-        }
-        if (e.code !== 404)
-          throw e;
-
-        dumpn("*** default: " + (path in this._defaultPaths));
-
-        response = new Response(connection);
-        if (path in this._defaultPaths)
-          this._defaultPaths[path](request, response);
-        else
-          throw HTTP_404;
-      }
-    }
-    catch (e)
-    {
-      if (response.partiallySent())
-      {
-        response.abort(e);
-        return;
-      }
-
-      var errorCode = "internal";
-
-      try
-      {
-        if (!(e instanceof HttpError))
-          throw e;
-
-        errorCode = e.code;
-        dumpn("*** errorCode == " + errorCode);
-
-        response = new Response(connection);
-        if (e.customErrorHandling)
-          e.customErrorHandling(response);
-        this._handleError(errorCode, request, response);
-        return;
-      }
-      catch (e2)
-      {
-        dumpn("*** error handling " + errorCode + " error: " +
-              "e2 == " + e2 + ", shutting down server");
-
-        connection.server._requestQuit();
-        response.abort(e2);
-        return;
-      }
-    }
-
-    response.complete();
-  },
-
-  //
-  // see nsIHttpServer.registerFile
-  //
-  registerFile: function(path, file)
-  {
-    if (!file)
-    {
-      dumpn("*** unregistering '" + path + "' mapping");
-      delete this._overridePaths[path];
-      return;
-    }
-
-    dumpn("*** registering '" + path + "' as mapping to " + file.path);
-    file = file.clone();
-
-    var self = this;
-    this._overridePaths[path] =
-      function(request, response)
-      {
-        if (!file.exists())
-          throw HTTP_404;
-
-        response.setStatusLine(request.httpVersion, 200, "OK");
-        self._writeFileResponse(request, file, response, 0, file.fileSize);
-      };
-  },
-
-  //
-  // see nsIHttpServer.registerPathHandler
-  //
-  registerPathHandler: function(path, handler)
-  {
-    // XXX true path validation!
-    if (path.charAt(0) != "/")
-      throw Cr.NS_ERROR_INVALID_ARG;
-
-    this._handlerToField(handler, this._overridePaths, path);
-  },
-
-  //
-  // see nsIHttpServer.registerDirectory
-  //
-  registerDirectory: function(path, directory)
-  {
-    // strip off leading and trailing '/' so that we can use lastIndexOf when
-    // determining exactly how a path maps onto a mapped directory --
-    // conditional is required here to deal with "/".substring(1, 0) being
-    // converted to "/".substring(0, 1) per the JS specification
-    var key = path.length == 1 ? "" : path.substring(1, path.length - 1);
-
-    // the path-to-directory mapping code requires that the first character not
-    // be "/", or it will go into an infinite loop
-    if (key.charAt(0) == "/")
-      throw Cr.NS_ERROR_INVALID_ARG;
-
-    key = toInternalPath(key, false);
-
-    if (directory)
-    {
-      dumpn("*** mapping '" + path + "' to the location " + directory.path);
-      this._pathDirectoryMap.put(key, directory);
-    }
-    else
-    {
-      dumpn("*** removing mapping for '" + path + "'");
-      this._pathDirectoryMap.put(key, null);
-    }
-  },
-
-  //
-  // see nsIHttpServer.registerErrorHandler
-  //
-  registerErrorHandler: function(err, handler)
-  {
-    if (!(err in HTTP_ERROR_CODES))
-      dumpn("*** WARNING: registering non-HTTP/1.1 error code " +
-            "(" + err + ") handler -- was this intentional?");
-
-    this._handlerToField(handler, this._overrideErrors, err);
-  },
-
-  //
-  // see nsIHttpServer.setIndexHandler
-  //
-  setIndexHandler: function(handler)
-  {
-    if (!handler)
-      handler = defaultIndexHandler;
-    else if (typeof(handler) != "function")
-      handler = createHandlerFunc(handler);
-
-    this._indexHandler = handler;
-  },
-
-  //
-  // see nsIHttpServer.registerContentType
-  //
-  registerContentType: function(ext, type)
-  {
-    if (!type)
-      delete this._mimeMappings[ext];
-    else
-      this._mimeMappings[ext] = headerUtils.normalizeFieldValue(type);
-  },
-
-  // PRIVATE API
-
-  /**
-   * Sets or remove (if handler is null) a handler in an object with a key.
-   *
-   * @param handler
-   *   a handler, either function or an nsIHttpRequestHandler
-   * @param dict
-   *   The object to attach the handler to.
-   * @param key
-   *   The field name of the handler.
-   */
-  _handlerToField: function(handler, dict, key)
-  {
-    // for convenience, handler can be a function if this is run from xpcshell
-    if (typeof(handler) == "function")
-      dict[key] = handler;
-    else if (handler)
-      dict[key] = createHandlerFunc(handler);
-    else
-      delete dict[key];
-  },
-
-  /**
-   * Handles a request which maps to a file in the local filesystem (if a base
-   * path has already been set; otherwise the 404 error is thrown).
-   *
-   * @param metadata : Request
-   *   metadata for the incoming request
-   * @param response : Response
-   *   an uninitialized Response to the given request, to be initialized by a
-   *   request handler
-   * @throws HTTP_###
-   *   if an HTTP error occurred (usually HTTP_404); note that in this case the
-   *   calling code must handle post-processing of the response
-   */
-  _handleDefault: function(metadata, response)
-  {
-    dumpn("*** _handleDefault()");
-
-    response.setStatusLine(metadata.httpVersion, 200, "OK");
-
-    var path = metadata.path;
-    NS_ASSERT(path.charAt(0) == "/", "invalid path: <" + path + ">");
-
-    // determine the actual on-disk file; this requires finding the deepest
-    // path-to-directory mapping in the requested URL
-    var file = this._getFileForPath(path);
-
-    // the "file" might be a directory, in which case we either serve the
-    // contained index.html or make the index handler write the response
-    if (file.exists() && file.isDirectory())
-    {
-      file.append("index.html"); // make configurable?
-      if (!file.exists() || file.isDirectory())
-      {
-        metadata._ensurePropertyBag();
-        metadata._bag.setPropertyAsInterface("directory", file.parent);
-        this._indexHandler(metadata, response);
-        return;
-      }
-    }
-
-    // alternately, the file might not exist
-    if (!file.exists())
-      throw HTTP_404;
-
-    var start, end;
-    if (metadata._httpVersion.atLeast(nsHttpVersion.HTTP_1_1) &&
-        metadata.hasHeader("Range") &&
-        this._getTypeFromFile(file) !== SJS_TYPE)
-    {
-      var rangeMatch = metadata.getHeader("Range").match(/^bytes=(\d+)?-(\d+)?$/);
-      if (!rangeMatch)
-        throw HTTP_400;
-
-      if (rangeMatch[1] !== undefined)
-        start = parseInt(rangeMatch[1], 10);
-
-      if (rangeMatch[2] !== undefined)
-        end = parseInt(rangeMatch[2], 10);
-
-      if (start === undefined && end === undefined)
-        throw HTTP_400;
-
-      // No start given, so the end is really the count of bytes from the
-      // end of the file.
-      if (start === undefined)
-      {
-        start = Math.max(0, file.fileSize - end);
-        end   = file.fileSize - 1;
-      }
-
-      // start and end are inclusive
-      if (end === undefined || end >= file.fileSize)
-        end = file.fileSize - 1;
-
-      if (start !== undefined && start >= file.fileSize) {
-        var HTTP_416 = new HttpError(416, "Requested Range Not Satisfiable");
-        HTTP_416.customErrorHandling = function(errorResponse)
-        {
-          maybeAddHeaders(file, metadata, errorResponse);
-        };
-        throw HTTP_416;
-      }
-
-      if (end < start)
-      {
-        response.setStatusLine(metadata.httpVersion, 200, "OK");
-        start = 0;
-        end = file.fileSize - 1;
-      }
-      else
-      {
-        response.setStatusLine(metadata.httpVersion, 206, "Partial Content");
-        var contentRange = "bytes " + start + "-" + end + "/" + file.fileSize;
-        response.setHeader("Content-Range", contentRange);
-      }
-    }
-    else
-    {
-      start = 0;
-      end = file.fileSize - 1;
-    }
-
-    // finally...
-    dumpn("*** handling '" + path + "' as mapping to " + file.path + " from " +
-          start + " to " + end + " inclusive");
-    this._writeFileResponse(metadata, file, response, start, end - start + 1);
-  },
-
-  /**
-   * Writes an HTTP response for the given file, including setting headers for
-   * file metadata.
-   *
-   * @param metadata : Request
-   *   the Request for which a response is being generated
-   * @param file : nsILocalFile
-   *   the file which is to be sent in the response
-   * @param response : Response
-   *   the response to which the file should be written
-   * @param offset: uint
-   *   the byte offset to skip to when writing
-   * @param count: uint
-   *   the number of bytes to write
-   */
-  _writeFileResponse: function(metadata, file, response, offset, count)
-  {
-    const PR_RDONLY = 0x01;
-
-    var type = this._getTypeFromFile(file);
-    if (type === SJS_TYPE)
-    {
-      var fis = new FileInputStream(file, PR_RDONLY, 0444,
-                                    Ci.nsIFileInputStream.CLOSE_ON_EOF);
-
-      try
-      {
-        var sis = new ScriptableInputStream(fis);
-        var s = Cu.Sandbox(gGlobalObject);
-        s.importFunction(dump, "dump");
-
-        // Define a basic key-value state-preservation API across requests, with
-        // keys initially corresponding to the empty string.
-        var self = this;
-        var path = metadata.path;
-        s.importFunction(function getState(k)
-        {
-          return self._getState(path, k);
-        });
-        s.importFunction(function setState(k, v)
-        {
-          self._setState(path, k, v);
-        });
-        s.importFunction(function getSharedState(k)
-        {
-          return self._getSharedState(k);
-        });
-        s.importFunction(function setSharedState(k, v)
-        {
-          self._setSharedState(k, v);
-        });
-        s.importFunction(function getObjectState(k, callback)
-        {
-          callback(self._getObjectState(k));
-        });
-        s.importFunction(function setObjectState(k, v)
-        {
-          self._setObjectState(k, v);
-        });
-        s.importFunction(function registerPathHandler(p, h)
-        {
-          self.registerPathHandler(p, h);
-        });
-
-        // Make it possible for sjs files to access their location
-        this._setState(path, "__LOCATION__", file.path);
-
-        try
-        {
-          // Alas, the line number in errors dumped to console when calling the
-          // request handler is simply an offset from where we load the SJS file.
-          // Work around this in a reasonably non-fragile way by dynamically
-          // getting the line number where we evaluate the SJS file.  Don't
-          // separate these two lines!
-          var line = new Error().lineNumber;
-          Cu.evalInSandbox(sis.read(file.fileSize), s);
-        }
-        catch (e)
-        {
-          dumpn("*** syntax error in SJS at " + file.path + ": " + e);
-          throw HTTP_500;
-        }
-
-        try
-        {
-          s.handleRequest(metadata, response);
-        }
-        catch (e)
-        {
-          dump("*** error running SJS at " + file.path + ": " +
-               e + " on line " +
-               (e instanceof Error
-               ? e.lineNumber + " in httpd.js"
-               : (e.lineNumber - line)) + "\n");
-          throw HTTP_500;
-        }
-      }
-      finally
-      {
-        fis.close();
-      }
-    }
-    else
-    {
-      try
-      {
-        response.setHeader("Last-Modified",
-                           toDateString(file.lastModifiedTime),
-                           false);
-      }
-      catch (e) { /* lastModifiedTime threw, ignore */ }
-
-      response.setHeader("Content-Type", type, false);
-      maybeAddHeaders(file, metadata, response);
-      response.setHeader("Content-Length", "" + count, false);
-
-      var fis = new FileInputStream(file, PR_RDONLY, 0444,
-                                    Ci.nsIFileInputStream.CLOSE_ON_EOF);
-
-      offset = offset || 0;
-      count  = count || file.fileSize;
-      NS_ASSERT(offset === 0 || offset < file.fileSize, "bad offset");
-      NS_ASSERT(count >= 0, "bad count");
-      NS_ASSERT(offset + count <= file.fileSize, "bad total data size");
-
-      try
-      {
-        if (offset !== 0)
-        {
-          // Seek (or read, if seeking isn't supported) to the correct offset so
-          // the data sent to the client matches the requested range.
-          if (fis instanceof Ci.nsISeekableStream)
-            fis.seek(Ci.nsISeekableStream.NS_SEEK_SET, offset);
-          else
-            new ScriptableInputStream(fis).read(offset);
-        }
-      }
-      catch (e)
-      {
-        fis.close();
-        throw e;
-      }
-
-      function writeMore()
-      {
-        gThreadManager.currentThread
-                      .dispatch(writeData, Ci.nsIThread.DISPATCH_NORMAL);
-      }
-
-      var input = new BinaryInputStream(fis);
-      var output = new BinaryOutputStream(response.bodyOutputStream);
-      var writeData =
-        {
-          run: function()
-          {
-            var chunkSize = Math.min(65536, count);
-            count -= chunkSize;
-            NS_ASSERT(count >= 0, "underflow");
-
-            try
-            {
-              var data = input.readByteArray(chunkSize);
-              NS_ASSERT(data.length === chunkSize,
-                        "incorrect data returned?  got " + data.length +
-                        ", expected " + chunkSize);
-              output.writeByteArray(data, data.length);
-              if (count === 0)
-              {
-                fis.close();
-                response.finish();
-              }
-              else
-              {
-                writeMore();
-              }
-            }
-            catch (e)
-            {
-              try
-              {
-                fis.close();
-              }
-              finally
-              {
-                response.finish();
-              }
-              throw e;
-            }
-          }
-        };
-
-      writeMore();
-
-      // Now that we know copying will start, flag the response as async.
-      response.processAsync();
-    }
-  },
-
-  /**
-   * Get the value corresponding to a given key for the given path for SJS state
-   * preservation across requests.
-   *
-   * @param path : string
-   *   the path from which the given state is to be retrieved
-   * @param k : string
-   *   the key whose corresponding value is to be returned
-   * @returns string
-   *   the corresponding value, which is initially the empty string
-   */
-  _getState: function(path, k)
-  {
-    var state = this._state;
-    if (path in state && k in state[path])
-      return state[path][k];
-    return "";
-  },
-
-  /**
-   * Set the value corresponding to a given key for the given path for SJS state
-   * preservation across requests.
-   *
-   * @param path : string
-   *   the path from which the given state is to be retrieved
-   * @param k : string
-   *   the key whose corresponding value is to be set
-   * @param v : string
-   *   the value to be set
-   */
-  _setState: function(path, k, v)
-  {
-    if (typeof v !== "string")
-      throw new Error("non-string value passed");
-    var state = this._state;
-    if (!(path in state))
-      state[path] = {};
-    state[path][k] = v;
-  },
-
-  /**
-   * Get the value corresponding to a given key for SJS state preservation
-   * across requests.
-   *
-   * @param k : string
-   *   the key whose corresponding value is to be returned
-   * @returns string
-   *   the corresponding value, which is initially the empty string
-   */
-  _getSharedState: function(k)
-  {
-    var state = this._sharedState;
-    if (k in state)
-      return state[k];
-    return "";
-  },
-
-  /**
-   * Set the value corresponding to a given key for SJS state preservation
-   * across requests.
-   *
-   * @param k : string
-   *   the key whose corresponding value is to be set
-   * @param v : string
-   *   the value to be set
-   */
-  _setSharedState: function(k, v)
-  {
-    if (typeof v !== "string")
-      throw new Error("non-string value passed");
-    this._sharedState[k] = v;
-  },
-
-  /**
-   * Returns the object associated with the given key in the server for SJS
-   * state preservation across requests.
-   *
-   * @param k : string
-   *  the key whose corresponding object is to be returned
-   * @returns nsISupports
-   *  the corresponding object, or null if none was present
-   */
-  _getObjectState: function(k)
-  {
-    if (typeof k !== "string")
-      throw new Error("non-string key passed");
-    return this._objectState[k] || null;
-  },
-
-  /**
-   * Sets the object associated with the given key in the server for SJS
-   * state preservation across requests.
-   *
-   * @param k : string
-   *  the key whose corresponding object is to be set
-   * @param v : nsISupports
-   *  the object to be associated with the given key; may be null
-   */
-  _setObjectState: function(k, v)
-  {
-    if (typeof k !== "string")
-      throw new Error("non-string key passed");
-    if (typeof v !== "object")
-      throw new Error("non-object value passed");
-    if (v && !("QueryInterface" in v))
-    {
-      throw new Error("must pass an nsISupports; use wrappedJSObject to ease " +
-                      "pain when using the server from JS");
-    }
-
-    this._objectState[k] = v;
-  },
-
-  /**
-   * Gets a content-type for the given file, first by checking for any custom
-   * MIME-types registered with this handler for the file's extension, second by
-   * asking the global MIME service for a content-type, and finally by failing
-   * over to application/octet-stream.
-   *
-   * @param file : nsIFile
-   *   the nsIFile for which to get a file type
-   * @returns string
-   *   the best content-type which can be determined for the file
-   */
-  _getTypeFromFile: function(file)
-  {
-    try
-    {
-      var name = file.leafName;
-      var dot = name.lastIndexOf(".");
-      if (dot > 0)
-      {
-        var ext = name.slice(dot + 1);
-        if (ext in this._mimeMappings)
-          return this._mimeMappings[ext];
-      }
-      return Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
-               .getService(Ci.nsIMIMEService)
-               .getTypeFromFile(file);
-    }
-    catch (e)
-    {
-      return "application/octet-stream";
-    }
-  },
-
-  /**
-   * Returns the nsILocalFile which corresponds to the path, as determined using
-   * all registered path->directory mappings and any paths which are explicitly
-   * overridden.
-   *
-   * @param path : string
-   *   the server path for which a file should be retrieved, e.g. "/foo/bar"
-   * @throws HttpError
-   *   when the correct action is the corresponding HTTP error (i.e., because no
-   *   mapping was found for a directory in path, the referenced file doesn't
-   *   exist, etc.)
-   * @returns nsILocalFile
-   *   the file to be sent as the response to a request for the path
-   */
-  _getFileForPath: function(path)
-  {
-    // decode and add underscores as necessary
-    try
-    {
-      path = toInternalPath(path, true);
-    }
-    catch (e)
-    {
-      throw HTTP_400; // malformed path
-    }
-
-    // next, get the directory which contains this path
-    var pathMap = this._pathDirectoryMap;
-
-    // An example progression of tmp for a path "/foo/bar/baz/" might be:
-    // "foo/bar/baz/", "foo/bar/baz", "foo/bar", "foo", ""
-    var tmp = path.substring(1);
-    while (true)
-    {
-      // do we have a match for current head of the path?
-      var file = pathMap.get(tmp);
-      if (file)
-      {
-        // XXX hack; basically disable showing mapping for /foo/bar/ when the
-        //     requested path was /foo/bar, because relative links on the page
-        //     will all be incorrect -- we really need the ability to easily
-        //     redirect here instead
-        if (tmp == path.substring(1) &&
-            tmp.length != 0 &&
-            tmp.charAt(tmp.length - 1) != "/")
-          file = null;
-        else
-          break;
-      }
-
-      // if we've finished trying all prefixes, exit
-      if (tmp == "")
-        break;
-
-      tmp = tmp.substring(0, tmp.lastIndexOf("/"));
-    }
-
-    // no mapping applies, so 404
-    if (!file)
-      throw HTTP_404;
-
-
-    // last, get the file for the path within the determined directory
-    var parentFolder = file.parent;
-    var dirIsRoot = (parentFolder == null);
-
-    // Strategy here is to append components individually, making sure we
-    // never move above the given directory; this allows paths such as
-    // "<file>/foo/../bar" but prevents paths such as "<file>/../base-sibling";
-    // this component-wise approach also means the code works even on platforms
-    // which don't use "/" as the directory separator, such as Windows
-    var leafPath = path.substring(tmp.length + 1);
-    var comps = leafPath.split("/");
-    for (var i = 0, sz = comps.length; i < sz; i++)
-    {
-      var comp = comps[i];
-
-      if (comp == "..")
-        file = file.parent;
-      else if (comp == "." || comp == "")
-        continue;
-      else
-        file.append(comp);
-
-      if (!dirIsRoot && file.equals(parentFolder))
-        throw HTTP_403;
-    }
-
-    return file;
-  },
-
-  /**
-   * Writes the error page for the given HTTP error code over the given
-   * connection.
-   *
-   * @param errorCode : uint
-   *   the HTTP error code to be used
-   * @param connection : Connection
-   *   the connection on which the error occurred
-   */
-  handleError: function(errorCode, connection)
-  {
-    var response = new Response(connection);
-
-    dumpn("*** error in request: " + errorCode);
-
-    this._handleError(errorCode, new Request(connection.port), response);
-  }, 
-
-  /**
-   * Handles a request which generates the given error code, using the
-   * user-defined error handler if one has been set, gracefully falling back to
-   * the x00 status code if the code has no handler, and failing to status code
-   * 500 if all else fails.
-   *
-   * @param errorCode : uint
-   *   the HTTP error which is to be returned
-   * @param metadata : Request
-   *   metadata for the request, which will often be incomplete since this is an
-   *   error
-   * @param response : Response
-   *   an uninitialized Response should be initialized when this method
-   *   completes with information which represents the desired error code in the
-   *   ideal case or a fallback code in abnormal circumstances (i.e., 500 is a
-   *   fallback for 505, per HTTP specs)
-   */
-  _handleError: function(errorCode, metadata, response)
-  {
-    if (!metadata)
-      throw Cr.NS_ERROR_NULL_POINTER;
-
-    var errorX00 = errorCode - (errorCode % 100);
-
-    try
-    {
-      if (!(errorCode in HTTP_ERROR_CODES))
-        dumpn("*** WARNING: requested invalid error: " + errorCode);
-
-      // RFC 2616 says that we should try to handle an error by its class if we
-      // can't otherwise handle it -- if that fails, we revert to handling it as
-      // a 500 internal server error, and if that fails we throw and shut down
-      // the server
-
-      // actually handle the error
-      try
-      {
-        if (errorCode in this._overrideErrors)
-          this._overrideErrors[errorCode](metadata, response);
-        else
-          this._defaultErrors[errorCode](metadata, response);
-      }
-      catch (e)
-      {
-        if (response.partiallySent())
-        {
-          response.abort(e);
-          return;
-        }
-
-        // don't retry the handler that threw
-        if (errorX00 == errorCode)
-          throw HTTP_500;
-
-        dumpn("*** error in handling for error code " + errorCode + ", " +
-              "falling back to " + errorX00 + "...");
-        response = new Response(response._connection);
-        if (errorX00 in this._overrideErrors)
-          this._overrideErrors[errorX00](metadata, response);
-        else if (errorX00 in this._defaultErrors)
-          this._defaultErrors[errorX00](metadata, response);
-        else
-          throw HTTP_500;
-      }
-    }
-    catch (e)
-    {
-      if (response.partiallySent())
-      {
-        response.abort();
-        return;
-      }
-
-      // we've tried everything possible for a meaningful error -- now try 500
-      dumpn("*** error in handling for error code " + errorX00 + ", falling " +
-            "back to 500...");
-
-      try
-      {
-        response = new Response(response._connection);
-        if (500 in this._overrideErrors)
-          this._overrideErrors[500](metadata, response);
-        else
-          this._defaultErrors[500](metadata, response);
-      }
-      catch (e2)
-      {
-        dumpn("*** multiple errors in default error handlers!");
-        dumpn("*** e == " + e + ", e2 == " + e2);
-        response.abort(e2);
-        return;
-      }
-    }
-
-    response.complete();
-  },
-
-  // FIELDS
-
-  /**
-   * This object contains the default handlers for the various HTTP error codes.
-   */
-  _defaultErrors:
-  {
-    400: function(metadata, response)
-    {
-      // none of the data in metadata is reliable, so hard-code everything here
-      response.setStatusLine("1.1", 400, "Bad Request");
-      response.setHeader("Content-Type", "text/plain", false);
-
-      var body = "Bad request\n";
-      response.bodyOutputStream.write(body, body.length);
-    },
-    403: function(metadata, response)
-    {
-      response.setStatusLine(metadata.httpVersion, 403, "Forbidden");
-      response.setHeader("Content-Type", "text/html", false);
-
-      var body = "<html>\
-                    <head><title>403 Forbidden</title></head>\
-                    <body>\
-                      <h1>403 Forbidden</h1>\
-                    </body>\
-                  </html>";
-      response.bodyOutputStream.write(body, body.length);
-    },
-    404: function(metadata, response)
-    {
-      response.setStatusLine(metadata.httpVersion, 404, "Not Found");
-      response.setHeader("Content-Type", "text/html", false);
-
-      var body = "<html>\
-                    <head><title>404 Not Found</title></head>\
-                    <body>\
-                      <h1>404 Not Found</h1>\
-                      <p>\
-                        <span style='font-family: monospace;'>" +
-                          htmlEscape(metadata.path) +
-                       "</span> was not found.\
-                      </p>\
-                    </body>\
-                  </html>";
-      response.bodyOutputStream.write(body, body.length);
-    },
-    416: function(metadata, response)
-    {
-      response.setStatusLine(metadata.httpVersion,
-                            416,
-                            "Requested Range Not Satisfiable");
-      response.setHeader("Content-Type", "text/html", false);
-
-      var body = "<html>\
-                   <head>\
-                    <title>416 Requested Range Not Satisfiable</title></head>\
-                    <body>\
-                     <h1>416 Requested Range Not Satisfiable</h1>\
-                     <p>The byte range was not valid for the\
-                        requested resource.\
-                     </p>\
-                    </body>\
-                  </html>";
-      response.bodyOutputStream.write(body, body.length);
-    },
-    500: function(metadata, response)
-    {
-      response.setStatusLine(metadata.httpVersion,
-                             500,
-                             "Internal Server Error");
-      response.setHeader("Content-Type", "text/html", false);
-
-      var body = "<html>\
-                    <head><title>500 Internal Server Error</title></head>\
-                    <body>\
-                      <h1>500 Internal Server Error</h1>\
-                      <p>Something's broken in this server and\
-                        needs to be fixed.</p>\
-                    </body>\
-                  </html>";
-      response.bodyOutputStream.write(body, body.length);
-    },
-    501: function(metadata, response)
-    {
-      response.setStatusLine(metadata.httpVersion, 501, "Not Implemented");
-      response.setHeader("Content-Type", "text/html", false);
-
-      var body = "<html>\
-                    <head><title>501 Not Implemented</title></head>\
-                    <body>\
-                      <h1>501 Not Implemented</h1>\
-                      <p>This server is not (yet) Apache.</p>\
-                    </body>\
-                  </html>";
-      response.bodyOutputStream.write(body, body.length);
-    },
-    505: function(metadata, response)
-    {
-      response.setStatusLine("1.1", 505, "HTTP Version Not Supported");
-      response.setHeader("Content-Type", "text/html", false);
-
-      var body = "<html>\
-                    <head><title>505 HTTP Version Not Supported</title></head>\
-                    <body>\
-                      <h1>505 HTTP Version Not Supported</h1>\
-                      <p>This server only supports HTTP/1.0 and HTTP/1.1\
-                        connections.</p>\
-                    </body>\
-                  </html>";
-      response.bodyOutputStream.write(body, body.length);
-    }
-  },
-
-  /**
-   * Contains handlers for the default set of URIs contained in this server.
-   */
-  _defaultPaths:
-  {
-    "/": function(metadata, response)
-    {
-      response.setStatusLine(metadata.httpVersion, 200, "OK");
-      response.setHeader("Content-Type", "text/html", false);
-
-      var body = "<html>\
-                    <head><title>httpd.js</title></head>\
-                    <body>\
-                      <h1>httpd.js</h1>\
-                      <p>If you're seeing this page, httpd.js is up and\
-                        serving requests!  Now set a base path and serve some\
-                        files!</p>\
-                    </body>\
-                  </html>";
-
-      response.bodyOutputStream.write(body, body.length);
-    },
-
-    "/trace": function(metadata, response)
-    {
-      response.setStatusLine(metadata.httpVersion, 200, "OK");
-      response.setHeader("Content-Type", "text/plain", false);
-
-      var body = "Request-URI: " +
-                 metadata.scheme + "://" + metadata.host + ":" + metadata.port +
-                 metadata.path + "\n\n";
-      body += "Request (semantically equivalent, slightly reformatted):\n\n";
-      body += metadata.method + " " + metadata.path;
-
-      if (metadata.queryString)
-        body +=  "?" + metadata.queryString;
-        
-      body += " HTTP/" + metadata.httpVersion + "\r\n";
-
-      var headEnum = metadata.headers;
-      while (headEnum.hasMoreElements())
-      {
-        var fieldName = headEnum.getNext()
-                                .QueryInterface(Ci.nsISupportsString)
-                                .data;
-        body += fieldName + ": " + metadata.getHeader(fieldName) + "\r\n";
-      }
-
-      response.bodyOutputStream.write(body, body.length);
-    }
-  }
-};
-
-
-/**
- * Maps absolute paths to files on the local file system (as nsILocalFiles).
- */
-function FileMap()
-{
-  /** Hash which will map paths to nsILocalFiles. */
-  this._map = {};
-}
-FileMap.prototype =
-{
-  // PUBLIC API
-
-  /**
-   * Maps key to a clone of the nsILocalFile value if value is non-null;
-   * otherwise, removes any extant mapping for key.
-   *
-   * @param key : string
-   *   string to which a clone of value is mapped
-   * @param value : nsILocalFile
-   *   the file to map to key, or null to remove a mapping
-   */
-  put: function(key, value)
-  {
-    if (value)
-      this._map[key] = value.clone();
-    else
-      delete this._map[key];
-  },
-
-  /**
-   * Returns a clone of the nsILocalFile mapped to key, or null if no such
-   * mapping exists.
-   *
-   * @param key : string
-   *   key to which the returned file maps
-   * @returns nsILocalFile
-   *   a clone of the mapped file, or null if no mapping exists
-   */
-  get: function(key)
-  {
-    var val = this._map[key];
-    return val ? val.clone() : null;
-  }
-};
-
-
-// Response CONSTANTS
-
-// token       = *<any CHAR except CTLs or separators>
-// CHAR        = <any US-ASCII character (0-127)>
-// CTL         = <any US-ASCII control character (0-31) and DEL (127)>
-// separators  = "(" | ")" | "<" | ">" | "@"
-//             | "," | ";" | ":" | "\" | <">
-//             | "/" | "[" | "]" | "?" | "="
-//             | "{" | "}" | SP  | HT
-const IS_TOKEN_ARRAY =
-  [0, 0, 0, 0, 0, 0, 0, 0, //   0
-   0, 0, 0, 0, 0, 0, 0, 0, //   8
-   0, 0, 0, 0, 0, 0, 0, 0, //  16
-   0, 0, 0, 0, 0, 0, 0, 0, //  24
-
-   0, 1, 0, 1, 1, 1, 1, 1, //  32
-   0, 0, 1, 1, 0, 1, 1, 0, //  40
-   1, 1, 1, 1, 1, 1, 1, 1, //  48
-   1, 1, 0, 0, 0, 0, 0, 0, //  56
-
-   0, 1, 1, 1, 1, 1, 1, 1, //  64
-   1, 1, 1, 1, 1, 1, 1, 1, //  72
-   1, 1, 1, 1, 1, 1, 1, 1, //  80
-   1, 1, 1, 0, 0, 0, 1, 1, //  88
-
-   1, 1, 1, 1, 1, 1, 1, 1, //  96
-   1, 1, 1, 1, 1, 1, 1, 1, // 104
-   1, 1, 1, 1, 1, 1, 1, 1, // 112
-   1, 1, 1, 0, 1, 0, 1];   // 120
-
-
-/**
- * Determines whether the given character code is a CTL.
- *
- * @param code : uint
- *   the character code
- * @returns boolean
- *   true if code is a CTL, false otherwise
- */
-function isCTL(code)
-{
-  return (code >= 0 && code <= 31) || (code == 127);
-}
-
-/**
- * Represents a response to an HTTP request, encapsulating all details of that
- * response.  This includes all headers, the HTTP version, status code and
- * explanation, and the entity itself.
- *
- * @param connection : Connection
- *   the connection over which this response is to be written
- */
-function Response(connection)
-{
-  /** The connection over which this response will be written. */
-  this._connection = connection;
-
-  /**
-   * The HTTP version of this response; defaults to 1.1 if not set by the
-   * handler.
-   */
-  this._httpVersion = nsHttpVersion.HTTP_1_1;
-
-  /**
-   * The HTTP code of this response; defaults to 200.
-   */
-  this._httpCode = 200;
-
-  /**
-   * The description of the HTTP code in this response; defaults to "OK".
-   */
-  this._httpDescription = "OK";
-
-  /**
-   * An nsIHttpHeaders object in which the headers in this response should be
-   * stored.  This property is null after the status line and headers have been
-   * written to the network, and it may be modified up until it is cleared,
-   * except if this._finished is set first (in which case headers are written
-   * asynchronously in response to a finish() call not preceded by
-   * flushHeaders()).
-   */
-  this._headers = new nsHttpHeaders();
-
-  /**
-   * Set to true when this response is ended (completely constructed if possible
-   * and the connection closed); further actions on this will then fail.
-   */
-  this._ended = false;
-
-  /**
-   * A stream used to hold data written to the body of this response.
-   */
-  this._bodyOutputStream = null;
-
-  /**
-   * A stream containing all data that has been written to the body of this
-   * response so far.  (Async handlers make the data contained in this
-   * unreliable as a way of determining content length in general, but auxiliary
-   * saved information can sometimes be used to guarantee reliability.)
-   */
-  this._bodyInputStream = null;
-
-  /**
-   * A stream copier which copies data to the network.  It is initially null
-   * until replaced with a copier for response headers; when headers have been
-   * fully sent it is replaced with a copier for the response body, remaining
-   * so for the duration of response processing.
-   */
-  this._asyncCopier = null;
-
-  /**
-   * True if this response has been designated as being processed
-   * asynchronously rather than for the duration of a single call to
-   * nsIHttpRequestHandler.handle.
-   */
-  this._processAsync = false;
-
-  /**
-   * True iff finish() has been called on this, signaling that no more changes
-   * to this may be made.
-   */
-  this._finished = false;
-
-  /**
-   * True iff powerSeized() has been called on this, signaling that this
-   * response is to be handled manually by the response handler (which may then
-   * send arbitrary data in response, even non-HTTP responses).
-   */
-  this._powerSeized = false;
-}
-Response.prototype =
-{
-  // PUBLIC CONSTRUCTION API
-
-  //
-  // see nsIHttpResponse.bodyOutputStream
-  //
-  get bodyOutputStream()
-  {
-    if (this._finished)
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-
-    if (!this._bodyOutputStream)
-    {
-      var pipe = new Pipe(true, false, Response.SEGMENT_SIZE, PR_UINT32_MAX,
-                          null);
-      this._bodyOutputStream = pipe.outputStream;
-      this._bodyInputStream = pipe.inputStream;
-      if (this._processAsync || this._powerSeized)
-        this._startAsyncProcessor();
-    }
-
-    return this._bodyOutputStream;
-  },
-
-  //
-  // see nsIHttpResponse.write
-  //
-  write: function(data)
-  {
-    if (this._finished)
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-
-    var dataAsString = String(data);
-    this.bodyOutputStream.write(dataAsString, dataAsString.length);
-  },
-
-  //
-  // see nsIHttpResponse.setStatusLine
-  //
-  setStatusLine: function(httpVersion, code, description)
-  {
-    if (!this._headers || this._finished || this._powerSeized)
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-    this._ensureAlive();
-
-    if (!(code >= 0 && code < 1000))
-      throw Cr.NS_ERROR_INVALID_ARG;
-
-    try
-    {
-      var httpVer;
-      // avoid version construction for the most common cases
-      if (!httpVersion || httpVersion == "1.1")
-        httpVer = nsHttpVersion.HTTP_1_1;
-      else if (httpVersion == "1.0")
-        httpVer = nsHttpVersion.HTTP_1_0;
-      else
-        httpVer = new nsHttpVersion(httpVersion);
-    }
-    catch (e)
-    {
-      throw Cr.NS_ERROR_INVALID_ARG;
-    }
-
-    // Reason-Phrase = *<TEXT, excluding CR, LF>
-    // TEXT          = <any OCTET except CTLs, but including LWS>
-    //
-    // XXX this ends up disallowing octets which aren't Unicode, I think -- not
-    //     much to do if description is IDL'd as string
-    if (!description)
-      description = "";
-    for (var i = 0; i < description.length; i++)
-      if (isCTL(description.charCodeAt(i)) && description.charAt(i) != "\t")
-        throw Cr.NS_ERROR_INVALID_ARG;
-
-    // set the values only after validation to preserve atomicity
-    this._httpDescription = description;
-    this._httpCode = code;
-    this._httpVersion = httpVer;
-  },
-
-  //
-  // see nsIHttpResponse.setHeader
-  //
-  setHeader: function(name, value, merge)
-  {
-    if (!this._headers || this._finished || this._powerSeized)
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-    this._ensureAlive();
-
-    this._headers.setHeader(name, value, merge);
-  },
-
-  //
-  // see nsIHttpResponse.processAsync
-  //
-  processAsync: function()
-  {
-    if (this._finished)
-      throw Cr.NS_ERROR_UNEXPECTED;
-    if (this._powerSeized)
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-    if (this._processAsync)
-      return;
-    this._ensureAlive();
-
-    dumpn("*** processing connection " + this._connection.number + " async");
-    this._processAsync = true;
-
-    /*
-     * Either the bodyOutputStream getter or this method is responsible for
-     * starting the asynchronous processor and catching writes of data to the
-     * response body of async responses as they happen, for the purpose of
-     * forwarding those writes to the actual connection's output stream.
-     * If bodyOutputStream is accessed first, calling this method will create
-     * the processor (when it first is clear that body data is to be written
-     * immediately, not buffered).  If this method is called first, accessing
-     * bodyOutputStream will create the processor.  If only this method is
-     * called, we'll write nothing, neither headers nor the nonexistent body,
-     * until finish() is called.  Since that delay is easily avoided by simply
-     * getting bodyOutputStream or calling write(""), we don't worry about it.
-     */
-    if (this._bodyOutputStream && !this._asyncCopier)
-      this._startAsyncProcessor();
-  },
-
-  //
-  // see nsIHttpResponse.seizePower
-  //
-  seizePower: function()
-  {
-    if (this._processAsync)
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-    if (this._finished)
-      throw Cr.NS_ERROR_UNEXPECTED;
-    if (this._powerSeized)
-      return;
-    this._ensureAlive();
-
-    dumpn("*** forcefully seizing power over connection " +
-          this._connection.number + "...");
-
-    // Purge any already-written data without sending it.  We could as easily
-    // swap out the streams entirely, but that makes it possible to acquire and
-    // unknowingly use a stale reference, so we require there only be one of
-    // each stream ever for any response to avoid this complication.
-    if (this._asyncCopier)
-      this._asyncCopier.cancel(Cr.NS_BINDING_ABORTED);
-    this._asyncCopier = null;
-    if (this._bodyOutputStream)
-    {
-      var input = new BinaryInputStream(this._bodyInputStream);
-      var avail;
-      while ((avail = input.available()) > 0)
-        input.readByteArray(avail);
-    }
-
-    this._powerSeized = true;
-    if (this._bodyOutputStream)
-      this._startAsyncProcessor();
-  },
-
-  //
-  // see nsIHttpResponse.finish
-  //
-  finish: function()
-  {
-    if (!this._processAsync && !this._powerSeized)
-      throw Cr.NS_ERROR_UNEXPECTED;
-    if (this._finished)
-      return;
-
-    dumpn("*** finishing connection " + this._connection.number);
-    this._startAsyncProcessor(); // in case bodyOutputStream was never accessed
-    if (this._bodyOutputStream)
-      this._bodyOutputStream.close();
-    this._finished = true;
-  },
-
-
-  // NSISUPPORTS
-
-  //
-  // see nsISupports.QueryInterface
-  //
-  QueryInterface: function(iid)
-  {
-    if (iid.equals(Ci.nsIHttpResponse) || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-
-  // POST-CONSTRUCTION API (not exposed externally)
-
-  /**
-   * The HTTP version number of this, as a string (e.g. "1.1").
-   */
-  get httpVersion()
-  {
-    this._ensureAlive();
-    return this._httpVersion.toString();
-  },
-
-  /**
-   * The HTTP status code of this response, as a string of three characters per
-   * RFC 2616.
-   */
-  get httpCode()
-  {
-    this._ensureAlive();
-
-    var codeString = (this._httpCode < 10 ? "0" : "") +
-                     (this._httpCode < 100 ? "0" : "") +
-                     this._httpCode;
-    return codeString;
-  },
-
-  /**
-   * The description of the HTTP status code of this response, or "" if none is
-   * set.
-   */
-  get httpDescription()
-  {
-    this._ensureAlive();
-
-    return this._httpDescription;
-  },
-
-  /**
-   * The headers in this response, as an nsHttpHeaders object.
-   */
-  get headers()
-  {
-    this._ensureAlive();
-
-    return this._headers;
-  },
-
-  //
-  // see nsHttpHeaders.getHeader
-  //
-  getHeader: function(name)
-  {
-    this._ensureAlive();
-
-    return this._headers.getHeader(name);
-  },
-
-  /**
-   * Determines whether this response may be abandoned in favor of a newly
-   * constructed response.  A response may be abandoned only if it is not being
-   * sent asynchronously and if raw control over it has not been taken from the
-   * server.
-   *
-   * @returns boolean
-   *   true iff no data has been written to the network
-   */
-  partiallySent: function()
-  {
-    dumpn("*** partiallySent()");
-    return this._processAsync || this._powerSeized;
-  },
-
-  /**
-   * If necessary, kicks off the remaining request processing needed to be done
-   * after a request handler performs its initial work upon this response.
-   */
-  complete: function()
-  {
-    dumpn("*** complete()");
-    if (this._processAsync || this._powerSeized)
-    {
-      NS_ASSERT(this._processAsync ^ this._powerSeized,
-                "can't both send async and relinquish power");
-      return;
-    }
-
-    NS_ASSERT(!this.partiallySent(), "completing a partially-sent response?");
-
-    this._startAsyncProcessor();
-
-    // Now make sure we finish processing this request!
-    if (this._bodyOutputStream)
-      this._bodyOutputStream.close();
-  },
-
-  /**
-   * Abruptly ends processing of this response, usually due to an error in an
-   * incoming request but potentially due to a bad error handler.  Since we
-   * cannot handle the error in the usual way (giving an HTTP error page in
-   * response) because data may already have been sent (or because the response
-   * might be expected to have been generated asynchronously or completely from
-   * scratch by the handler), we stop processing this response and abruptly
-   * close the connection.
-   *
-   * @param e : Error
-   *   the exception which precipitated this abort, or null if no such exception
-   *   was generated
-   */
-  abort: function(e)
-  {
-    dumpn("*** abort(<" + e + ">)");
-
-    // This response will be ended by the processor if one was created.
-    var copier = this._asyncCopier;
-    if (copier)
-    {
-      // We dispatch asynchronously here so that any pending writes of data to
-      // the connection will be deterministically written.  This makes it easier
-      // to specify exact behavior, and it makes observable behavior more
-      // predictable for clients.  Note that the correctness of this depends on
-      // callbacks in response to _waitToReadData in WriteThroughCopier
-      // happening asynchronously with respect to the actual writing of data to
-      // bodyOutputStream, as they currently do; if they happened synchronously,
-      // an event which ran before this one could write more data to the
-      // response body before we get around to canceling the copier.  We have
-      // tests for this in test_seizepower.js, however, and I can't think of a
-      // way to handle both cases without removing bodyOutputStream access and
-      // moving its effective write(data, length) method onto Response, which
-      // would be slower and require more code than this anyway.
-      gThreadManager.currentThread.dispatch({
-        run: function()
-        {
-          dumpn("*** canceling copy asynchronously...");
-          copier.cancel(Cr.NS_ERROR_UNEXPECTED);
-        }
-      }, Ci.nsIThread.DISPATCH_NORMAL);
-    }
-    else
-    {
-      this.end();
-    }
-  },
-
-  /**
-   * Closes this response's network connection, marks the response as finished,
-   * and notifies the server handler that the request is done being processed.
-   */
-  end: function()
-  {
-    NS_ASSERT(!this._ended, "ending this response twice?!?!");
-
-    this._connection.close();
-    if (this._bodyOutputStream)
-      this._bodyOutputStream.close();
-
-    this._finished = true;
-    this._ended = true;
-  },
-
-  // PRIVATE IMPLEMENTATION
-
-  /**
-   * Sends the status line and headers of this response if they haven't been
-   * sent and initiates the process of copying data written to this response's
-   * body to the network.
-   */
-  _startAsyncProcessor: function()
-  {
-    dumpn("*** _startAsyncProcessor()");
-
-    // Handle cases where we're being called a second time.  The former case
-    // happens when this is triggered both by complete() and by processAsync(),
-    // while the latter happens when processAsync() in conjunction with sent
-    // data causes abort() to be called.
-    if (this._asyncCopier || this._ended)
-    {
-      dumpn("*** ignoring second call to _startAsyncProcessor");
-      return;
-    }
-
-    // Send headers if they haven't been sent already and should be sent, then
-    // asynchronously continue to send the body.
-    if (this._headers && !this._powerSeized)
-    {
-      this._sendHeaders();
-      return;
-    }
-
-    this._headers = null;
-    this._sendBody();
-  },
-
-  /**
-   * Signals that all modifications to the response status line and headers are
-   * complete and then sends that data over the network to the client.  Once
-   * this method completes, a different response to the request that resulted
-   * in this response cannot be sent -- the only possible action in case of
-   * error is to abort the response and close the connection.
-   */
-  _sendHeaders: function()
-  {
-    dumpn("*** _sendHeaders()");
-
-    NS_ASSERT(this._headers);
-    NS_ASSERT(!this._powerSeized);
-
-    // request-line
-    var statusLine = "HTTP/" + this.httpVersion + " " +
-                     this.httpCode + " " +
-                     this.httpDescription + "\r\n";
-
-    // header post-processing
-
-    var headers = this._headers;
-    headers.setHeader("Connection", "close", false);
-    headers.setHeader("Server", "httpd.js", false);
-    if (!headers.hasHeader("Date"))
-      headers.setHeader("Date", toDateString(Date.now()), false);
-
-    // Any response not being processed asynchronously must have an associated
-    // Content-Length header for reasons of backwards compatibility with the
-    // initial server, which fully buffered every response before sending it.
-    // Beyond that, however, it's good to do this anyway because otherwise it's
-    // impossible to test behaviors that depend on the presence or absence of a
-    // Content-Length header.
-    if (!this._processAsync)
-    {
-      dumpn("*** non-async response, set Content-Length");
-
-      var bodyStream = this._bodyInputStream;
-      var avail = bodyStream ? bodyStream.available() : 0;
-
-      // XXX assumes stream will always report the full amount of data available
-      headers.setHeader("Content-Length", "" + avail, false);
-    }
-
-
-    // construct and send response
-    dumpn("*** header post-processing completed, sending response head...");
-
-    // request-line
-    var preambleData = [statusLine];
-
-    // headers
-    var headEnum = headers.enumerator;
-    while (headEnum.hasMoreElements())
-    {
-      var fieldName = headEnum.getNext()
-                              .QueryInterface(Ci.nsISupportsString)
-                              .data;
-      var values = headers.getHeaderValues(fieldName);
-      for (var i = 0, sz = values.length; i < sz; i++)
-        preambleData.push(fieldName + ": " + values[i] + "\r\n");
-    }
-
-    // end request-line/headers
-    preambleData.push("\r\n");
-
-    var preamble = preambleData.join("");
-
-    var responseHeadPipe = new Pipe(true, false, 0, PR_UINT32_MAX, null);
-    responseHeadPipe.outputStream.write(preamble, preamble.length);
-
-    var response = this;
-    var copyObserver =
-      {
-        onStartRequest: function(request, cx)
-        {
-          dumpn("*** preamble copying started");
-        },
-
-        onStopRequest: function(request, cx, statusCode)
-        {
-          dumpn("*** preamble copying complete " +
-                "[status=0x" + statusCode.toString(16) + "]");
-
-          if (!Components.isSuccessCode(statusCode))
-          {
-            dumpn("!!! header copying problems: non-success statusCode, " +
-                  "ending response");
-
-            response.end();
-          }
-          else
-          {
-            response._sendBody();
-          }
-        },
-
-        QueryInterface: function(aIID)
-        {
-          if (aIID.equals(Ci.nsIRequestObserver) || aIID.equals(Ci.nsISupports))
-            return this;
-
-          throw Cr.NS_ERROR_NO_INTERFACE;
-        }
-      };
-
-    var headerCopier = this._asyncCopier =
-      new WriteThroughCopier(responseHeadPipe.inputStream,
-                             this._connection.output,
-                             copyObserver, null);
-
-    responseHeadPipe.outputStream.close();
-
-    // Forbid setting any more headers or modifying the request line.
-    this._headers = null;
-  },
-
-  /**
-   * Asynchronously writes the body of the response (or the entire response, if
-   * seizePower() has been called) to the network.
-   */
-  _sendBody: function()
-  {
-    dumpn("*** _sendBody");
-
-    NS_ASSERT(!this._headers, "still have headers around but sending body?");
-
-    // If no body data was written, we're done
-    if (!this._bodyInputStream)
-    {
-      dumpn("*** empty body, response finished");
-      this.end();
-      return;
-    }
-
-    var response = this;
-    var copyObserver =
-      {
-        onStartRequest: function(request, context)
-        {
-          dumpn("*** onStartRequest");
-        },
-
-        onStopRequest: function(request, cx, statusCode)
-        {
-          dumpn("*** onStopRequest [status=0x" + statusCode.toString(16) + "]");
-
-          if (statusCode === Cr.NS_BINDING_ABORTED)
-          {
-            dumpn("*** terminating copy observer without ending the response");
-          }
-          else
-          {
-            if (!Components.isSuccessCode(statusCode))
-              dumpn("*** WARNING: non-success statusCode in onStopRequest");
-
-            response.end();
-          }
-        },
-
-        QueryInterface: function(aIID)
-        {
-          if (aIID.equals(Ci.nsIRequestObserver) || aIID.equals(Ci.nsISupports))
-            return this;
-
-          throw Cr.NS_ERROR_NO_INTERFACE;
-        }
-      };
-
-    dumpn("*** starting async copier of body data...");
-    this._asyncCopier =
-      new WriteThroughCopier(this._bodyInputStream, this._connection.output,
-                            copyObserver, null);
-  },
-
-  /** Ensures that this hasn't been ended. */
-  _ensureAlive: function()
-  {
-    NS_ASSERT(!this._ended, "not handling response lifetime correctly");
-  }
-};
-
-/**
- * Size of the segments in the buffer used in storing response data and writing
- * it to the socket.
- */
-Response.SEGMENT_SIZE = 8192;
-
-/** Serves double duty in WriteThroughCopier implementation. */
-function notImplemented()
-{
-  throw Cr.NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/** Returns true iff the given exception represents stream closure. */
-function streamClosed(e)
-{
-  return e === Cr.NS_BASE_STREAM_CLOSED ||
-         (typeof e === "object" && e.result === Cr.NS_BASE_STREAM_CLOSED);
-}
-
-/** Returns true iff the given exception represents a blocked stream. */
-function wouldBlock(e)
-{
-  return e === Cr.NS_BASE_STREAM_WOULD_BLOCK ||
-         (typeof e === "object" && e.result === Cr.NS_BASE_STREAM_WOULD_BLOCK);
-}
-
-/**
- * Copies data from source to sink as it becomes available, when that data can
- * be written to sink without blocking.
- *
- * @param source : nsIAsyncInputStream
- *   the stream from which data is to be read
- * @param sink : nsIAsyncOutputStream
- *   the stream to which data is to be copied
- * @param observer : nsIRequestObserver
- *   an observer which will be notified when the copy starts and finishes
- * @param context : nsISupports
- *   context passed to observer when notified of start/stop
- * @throws NS_ERROR_NULL_POINTER
- *   if source, sink, or observer are null
- */
-function WriteThroughCopier(source, sink, observer, context)
-{
-  if (!source || !sink || !observer)
-    throw Cr.NS_ERROR_NULL_POINTER;
-
-  /** Stream from which data is being read. */
-  this._source = source;
-
-  /** Stream to which data is being written. */
-  this._sink = sink;
-
-  /** Observer watching this copy. */
-  this._observer = observer;
-
-  /** Context for the observer watching this. */
-  this._context = context;
-
-  /**
-   * True iff this is currently being canceled (cancel has been called, the
-   * callback may not yet have been made).
-   */
-  this._canceled = false;
-
-  /**
-   * False until all data has been read from input and written to output, at
-   * which point this copy is completed and cancel() is asynchronously called.
-   */
-  this._completed = false;
-
-  /** Required by nsIRequest, meaningless. */
-  this.loadFlags = 0;
-  /** Required by nsIRequest, meaningless. */
-  this.loadGroup = null;
-  /** Required by nsIRequest, meaningless. */
-  this.name = "response-body-copy";
-
-  /** Status of this request. */
-  this.status = Cr.NS_OK;
-
-  /** Arrays of byte strings waiting to be written to output. */
-  this._pendingData = [];
-
-  // start copying
-  try
-  {
-    observer.onStartRequest(this, context);
-    this._waitToReadData();
-    this._waitForSinkClosure();
-  }
-  catch (e)
-  {
-    dumpn("!!! error starting copy: " + e +
-          ("lineNumber" in e ? ", line " + e.lineNumber : ""));
-    dumpn(e.stack);
-    this.cancel(Cr.NS_ERROR_UNEXPECTED);
-  }
-}
-WriteThroughCopier.prototype =
-{
-  /* nsISupports implementation */
-
-  QueryInterface: function(iid)
-  {
-    if (iid.equals(Ci.nsIInputStreamCallback) ||
-        iid.equals(Ci.nsIOutputStreamCallback) ||
-        iid.equals(Ci.nsIRequest) ||
-        iid.equals(Ci.nsISupports))
-    {
-      return this;
-    }
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-
-  // NSIINPUTSTREAMCALLBACK
-
-  /**
-   * Receives a more-data-in-input notification and writes the corresponding
-   * data to the output.
-   *
-   * @param input : nsIAsyncInputStream
-   *   the input stream on whose data we have been waiting
-   */
-  onInputStreamReady: function(input)
-  {
-    if (this._source === null)
-      return;
-
-    dumpn("*** onInputStreamReady");
-
-    //
-    // Ordinarily we'll read a non-zero amount of data from input, queue it up
-    // to be written and then wait for further callbacks.  The complications in
-    // this method are the cases where we deviate from that behavior when errors
-    // occur or when copying is drawing to a finish.
-    //
-    // The edge cases when reading data are:
-    //
-    //   Zero data is read
-    //     If zero data was read, we're at the end of available data, so we can
-    //     should stop reading and move on to writing out what we have (or, if
-    //     we've already done that, onto notifying of completion).
-    //   A stream-closed exception is thrown
-    //     This is effectively a less kind version of zero data being read; the
-    //     only difference is that we notify of completion with that result
-    //     rather than with NS_OK.
-    //   Some other exception is thrown
-    //     This is the least kind result.  We don't know what happened, so we
-    //     act as though the stream closed except that we notify of completion
-    //     with the result NS_ERROR_UNEXPECTED.
-    //
-
-    var bytesWanted = 0, bytesConsumed = -1;
-    try
-    {
-      input = new BinaryInputStream(input);
-
-      bytesWanted = Math.min(input.available(), Response.SEGMENT_SIZE);
-      dumpn("*** input wanted: " + bytesWanted);
-
-      if (bytesWanted > 0)
-      {
-        var data = input.readByteArray(bytesWanted);
-        bytesConsumed = data.length;
-        this._pendingData.push(String.fromCharCode.apply(String, data));
-      }
-
-      dumpn("*** " + bytesConsumed + " bytes read");
-
-      // Handle the zero-data edge case in the same place as all other edge
-      // cases are handled.
-      if (bytesWanted === 0)
-        throw Cr.NS_BASE_STREAM_CLOSED;
-    }
-    catch (e)
-    {
-      if (streamClosed(e))
-      {
-        dumpn("*** input stream closed");
-        e = bytesWanted === 0 ? Cr.NS_OK : Cr.NS_ERROR_UNEXPECTED;
-      }
-      else
-      {
-        dumpn("!!! unexpected error reading from input, canceling: " + e);
-        e = Cr.NS_ERROR_UNEXPECTED;
-      }
-
-      this._doneReadingSource(e);
-      return;
-    }
-
-    var pendingData = this._pendingData;
-
-    NS_ASSERT(bytesConsumed > 0);
-    NS_ASSERT(pendingData.length > 0, "no pending data somehow?");
-    NS_ASSERT(pendingData[pendingData.length - 1].length > 0,
-              "buffered zero bytes of data?");
-
-    NS_ASSERT(this._source !== null);
-
-    // Reading has gone great, and we've gotten data to write now.  What if we
-    // don't have a place to write that data, because output went away just
-    // before this read?  Drop everything on the floor, including new data, and
-    // cancel at this point.
-    if (this._sink === null)
-    {
-      pendingData.length = 0;
-      this._doneReadingSource(Cr.NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    // Okay, we've read the data, and we know we have a place to write it.  We
-    // need to queue up the data to be written, but *only* if none is queued
-    // already -- if data's already queued, the code that actually writes the
-    // data will make sure to wait on unconsumed pending data.
-    try
-    {
-      if (pendingData.length === 1)
-        this._waitToWriteData();
-    }
-    catch (e)
-    {
-      dumpn("!!! error waiting to write data just read, swallowing and " +
-            "writing only what we already have: " + e);
-      this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    // Whee!  We successfully read some data, and it's successfully queued up to
-    // be written.  All that remains now is to wait for more data to read.
-    try
-    {
-      this._waitToReadData();
-    }
-    catch (e)
-    {
-      dumpn("!!! error waiting to read more data: " + e);
-      this._doneReadingSource(Cr.NS_ERROR_UNEXPECTED);
-    }
-  },
-
-
-  // NSIOUTPUTSTREAMCALLBACK
-
-  /**
-   * Callback when data may be written to the output stream without blocking, or
-   * when the output stream has been closed.
-   *
-   * @param output : nsIAsyncOutputStream
-   *   the output stream on whose writability we've been waiting, also known as
-   *   this._sink
-   */
-  onOutputStreamReady: function(output)
-  {
-    if (this._sink === null)
-      return;
-
-    dumpn("*** onOutputStreamReady");
-
-    var pendingData = this._pendingData;
-    if (pendingData.length === 0)
-    {
-      // There's no pending data to write.  The only way this can happen is if
-      // we're waiting on the output stream's closure, so we can respond to a
-      // copying failure as quickly as possible (rather than waiting for data to
-      // be available to read and then fail to be copied).  Therefore, we must
-      // be done now -- don't bother to attempt to write anything and wrap
-      // things up.
-      dumpn("!!! output stream closed prematurely, ending copy");
-
-      this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-
-    NS_ASSERT(pendingData[0].length > 0, "queued up an empty quantum?");
-
-    //
-    // Write out the first pending quantum of data.  The possible errors here
-    // are:
-    //
-    //   The write might fail because we can't write that much data
-    //     Okay, we've written what we can now, so re-queue what's left and
-    //     finish writing it out later.
-    //   The write failed because the stream was closed
-    //     Discard pending data that we can no longer write, stop reading, and
-    //     signal that copying finished.
-    //   Some other error occurred.
-    //     Same as if the stream were closed, but notify with the status
-    //     NS_ERROR_UNEXPECTED so the observer knows something was wonky.
-    //
-
-    try
-    {
-      var quantum = pendingData[0];
-
-      // XXX |quantum| isn't guaranteed to be ASCII, so we're relying on
-      //     undefined behavior!  We're only using this because writeByteArray
-      //     is unusably broken for asynchronous output streams; see bug 532834
-      //     for details.
-      var bytesWritten = output.write(quantum, quantum.length);
-      if (bytesWritten === quantum.length)
-        pendingData.shift();
-      else
-        pendingData[0] = quantum.substring(bytesWritten);
-
-      dumpn("*** wrote " + bytesWritten + " bytes of data");
-    }
-    catch (e)
-    {
-      if (wouldBlock(e))
-      {
-        NS_ASSERT(pendingData.length > 0,
-                  "stream-blocking exception with no data to write?");
-        NS_ASSERT(pendingData[0].length > 0,
-                  "stream-blocking exception with empty quantum?");
-        this._waitToWriteData();
-        return;
-      }
-
-      if (streamClosed(e))
-        dumpn("!!! output stream prematurely closed, signaling error...");
-      else
-        dumpn("!!! unknown error: " + e + ", quantum=" + quantum);
-
-      this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    // The day is ours!  Quantum written, now let's see if we have more data
-    // still to write.
-    try
-    {
-      if (pendingData.length > 0)
-      {
-        this._waitToWriteData();
-        return;
-      }
-    }
-    catch (e)
-    {
-      dumpn("!!! unexpected error waiting to write pending data: " + e);
-      this._doneWritingToSink(Cr.NS_ERROR_UNEXPECTED);
-      return;
-    }
-
-    // Okay, we have no more pending data to write -- but might we get more in
-    // the future?
-    if (this._source !== null)
-    {
-      /*
-       * If we might, then wait for the output stream to be closed.  (We wait
-       * only for closure because we have no data to write -- and if we waited
-       * for a specific amount of data, we would get repeatedly notified for no
-       * reason if over time the output stream permitted more and more data to
-       * be written to it without blocking.)
-       */
-       this._waitForSinkClosure();
-    }
-    else
-    {
-      /*
-       * On the other hand, if we can't have more data because the input
-       * stream's gone away, then it's time to notify of copy completion.
-       * Victory!
-       */
-      this._sink = null;
-      this._cancelOrDispatchCancelCallback(Cr.NS_OK);
-    }
-  },
-
-
-  // NSIREQUEST
-
-  /** Returns true if the cancel observer hasn't been notified yet. */
-  isPending: function()
-  {
-    return !this._completed;
-  },
-
-  /** Not implemented, don't use! */
-  suspend: notImplemented,
-  /** Not implemented, don't use! */
-  resume: notImplemented,
-
-  /**
-   * Cancels data reading from input, asynchronously writes out any pending
-   * data, and causes the observer to be notified with the given error code when
-   * all writing has finished.
-   *
-   * @param status : nsresult
-   *   the status to pass to the observer when data copying has been canceled
-   */
-  cancel: function(status)
-  {
-    dumpn("*** cancel(" + status.toString(16) + ")");
-
-    if (this._canceled)
-    {
-      dumpn("*** suppressing a late cancel");
-      return;
-    }
-
-    this._canceled = true;
-    this.status = status;
-
-    // We could be in the middle of absolutely anything at this point.  Both
-    // input and output might still be around, we might have pending data to
-    // write, and in general we know nothing about the state of the world.  We
-    // therefore must assume everything's in progress and take everything to its
-    // final steady state (or so far as it can go before we need to finish
-    // writing out remaining data).
-
-    this._doneReadingSource(status);
-  },
-
-
-  // PRIVATE IMPLEMENTATION
-
-  /**
-   * Stop reading input if we haven't already done so, passing e as the status
-   * when closing the stream, and kick off a copy-completion notice if no more
-   * data remains to be written.
-   *
-   * @param e : nsresult
-   *   the status to be used when closing the input stream
-   */
-  _doneReadingSource: function(e)
-  {
-    dumpn("*** _doneReadingSource(0x" + e.toString(16) + ")");
-
-    this._finishSource(e);
-    if (this._pendingData.length === 0)
-      this._sink = null;
-    else
-      NS_ASSERT(this._sink !== null, "null output?");
-
-    // If we've written out all data read up to this point, then it's time to
-    // signal completion.
-    if (this._sink === null)
-    {
-      NS_ASSERT(this._pendingData.length === 0, "pending data still?");
-      this._cancelOrDispatchCancelCallback(e);
-    }
-  },
-
-  /**
-   * Stop writing output if we haven't already done so, discard any data that
-   * remained to be sent, close off input if it wasn't already closed, and kick
-   * off a copy-completion notice.
-   *
-   * @param e : nsresult
-   *   the status to be used when closing input if it wasn't already closed
-   */
-  _doneWritingToSink: function(e)
-  {
-    dumpn("*** _doneWritingToSink(0x" + e.toString(16) + ")");
-
-    this._pendingData.length = 0;
-    this._sink = null;
-    this._doneReadingSource(e);
-  },
-
-  /**
-   * Completes processing of this copy: either by canceling the copy if it
-   * hasn't already been canceled using the provided status, or by dispatching
-   * the cancel callback event (with the originally provided status, of course)
-   * if it already has been canceled.
-   *
-   * @param status : nsresult
-   *   the status code to use to cancel this, if this hasn't already been
-   *   canceled
-   */
-  _cancelOrDispatchCancelCallback: function(status)
-  {
-    dumpn("*** _cancelOrDispatchCancelCallback(" + status + ")");
-
-    NS_ASSERT(this._source === null, "should have finished input");
-    NS_ASSERT(this._sink === null, "should have finished output");
-    NS_ASSERT(this._pendingData.length === 0, "should have no pending data");
-
-    if (!this._canceled)
-    {
-      this.cancel(status);
-      return;
-    }
-
-    var self = this;
-    var event =
-      {
-        run: function()
-        {
-          dumpn("*** onStopRequest async callback");
-
-          self._completed = true;
-          try
-          {
-            self._observer.onStopRequest(self, self._context, self.status);
-          }
-          catch (e)
-          {
-            NS_ASSERT(false,
-                      "how are we throwing an exception here?  we control " +
-                      "all the callers!  " + e);
-          }
-        }
-      };
-
-    gThreadManager.currentThread.dispatch(event, Ci.nsIThread.DISPATCH_NORMAL);
-  },
-
-  /**
-   * Kicks off another wait for more data to be available from the input stream.
-   */
-  _waitToReadData: function()
-  {
-    dumpn("*** _waitToReadData");
-    this._source.asyncWait(this, 0, Response.SEGMENT_SIZE,
-                           gThreadManager.mainThread);
-  },
-
-  /**
-   * Kicks off another wait until data can be written to the output stream.
-   */
-  _waitToWriteData: function()
-  {
-    dumpn("*** _waitToWriteData");
-
-    var pendingData = this._pendingData;
-    NS_ASSERT(pendingData.length > 0, "no pending data to write?");
-    NS_ASSERT(pendingData[0].length > 0, "buffered an empty write?");
-
-    this._sink.asyncWait(this, 0, pendingData[0].length,
-                         gThreadManager.mainThread);
-  },
-
-  /**
-   * Kicks off a wait for the sink to which data is being copied to be closed.
-   * We wait for stream closure when we don't have any data to be copied, rather
-   * than waiting to write a specific amount of data.  We can't wait to write
-   * data because the sink might be infinitely writable, and if no data appears
-   * in the source for a long time we might have to spin quite a bit waiting to
-   * write, waiting to write again, &c.  Waiting on stream closure instead means
-   * we'll get just one notification if the sink dies.  Note that when data
-   * starts arriving from the sink we'll resume waiting for data to be written,
-   * dropping this closure-only callback entirely.
-   */
-  _waitForSinkClosure: function()
-  {
-    dumpn("*** _waitForSinkClosure");
-
-    this._sink.asyncWait(this, Ci.nsIAsyncOutputStream.WAIT_CLOSURE_ONLY, 0,
-                         gThreadManager.mainThread);
-  },
-
-  /**
-   * Closes input with the given status, if it hasn't already been closed;
-   * otherwise a no-op.
-   *
-   * @param status : nsresult
-   *   status code use to close the source stream if necessary
-   */
-  _finishSource: function(status)
-  {
-    dumpn("*** _finishSource(" + status.toString(16) + ")");
-
-    if (this._source !== null)
-    {
-      this._source.closeWithStatus(status);
-      this._source = null;
-    }
-  }
-};
-
-
-/**
- * A container for utility functions used with HTTP headers.
- */
-const headerUtils =
-{
-  /**
-   * Normalizes fieldName (by converting it to lowercase) and ensures it is a
-   * valid header field name (although not necessarily one specified in RFC
-   * 2616).
-   *
-   * @throws NS_ERROR_INVALID_ARG
-   *   if fieldName does not match the field-name production in RFC 2616
-   * @returns string
-   *   fieldName converted to lowercase if it is a valid header, for characters
-   *   where case conversion is possible
-   */
-  normalizeFieldName: function(fieldName)
-  {
-    if (fieldName == "")
-      throw Cr.NS_ERROR_INVALID_ARG;
-
-    for (var i = 0, sz = fieldName.length; i < sz; i++)
-    {
-      if (!IS_TOKEN_ARRAY[fieldName.charCodeAt(i)])
-      {
-        dumpn(fieldName + " is not a valid header field name!");
-        throw Cr.NS_ERROR_INVALID_ARG;
-      }
-    }
-
-    return fieldName.toLowerCase();
-  },
-
-  /**
-   * Ensures that fieldValue is a valid header field value (although not
-   * necessarily as specified in RFC 2616 if the corresponding field name is
-   * part of the HTTP protocol), normalizes the value if it is, and
-   * returns the normalized value.
-   *
-   * @param fieldValue : string
-   *   a value to be normalized as an HTTP header field value
-   * @throws NS_ERROR_INVALID_ARG
-   *   if fieldValue does not match the field-value production in RFC 2616
-   * @returns string
-   *   fieldValue as a normalized HTTP header field value
-   */
-  normalizeFieldValue: function(fieldValue)
-  {
-    // field-value    = *( field-content | LWS )
-    // field-content  = <the OCTETs making up the field-value
-    //                  and consisting of either *TEXT or combinations
-    //                  of token, separators, and quoted-string>
-    // TEXT           = <any OCTET except CTLs,
-    //                  but including LWS>
-    // LWS            = [CRLF] 1*( SP | HT )
-    //
-    // quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
-    // qdtext         = <any TEXT except <">>
-    // quoted-pair    = "\" CHAR
-    // CHAR           = <any US-ASCII character (octets 0 - 127)>
-
-    // Any LWS that occurs between field-content MAY be replaced with a single
-    // SP before interpreting the field value or forwarding the message
-    // downstream (section 4.2); we replace 1*LWS with a single SP
-    var val = fieldValue.replace(/(?:(?:\r\n)?[ \t]+)+/g, " ");
-
-    // remove leading/trailing LWS (which has been converted to SP)
-    val = val.replace(/^ +/, "").replace(/ +$/, "");
-
-    // that should have taken care of all CTLs, so val should contain no CTLs
-    for (var i = 0, len = val.length; i < len; i++)
-      if (isCTL(val.charCodeAt(i)))
-        throw Cr.NS_ERROR_INVALID_ARG;
-
-    // XXX disallows quoted-pair where CHAR is a CTL -- will not invalidly
-    //     normalize, however, so this can be construed as a tightening of the
-    //     spec and not entirely as a bug
-    return val;
-  }
-};
-
-
-
-/**
- * Converts the given string into a string which is safe for use in an HTML
- * context.
- *
- * @param str : string
- *   the string to make HTML-safe
- * @returns string
- *   an HTML-safe version of str
- */
-function htmlEscape(str)
-{
-  // this is naive, but it'll work
-  var s = "";
-  for (var i = 0; i < str.length; i++)
-    s += "&#" + str.charCodeAt(i) + ";";
-  return s;
-}
-
-
-/**
- * Constructs an object representing an HTTP version (see section 3.1).
- *
- * @param versionString
- *   a string of the form "#.#", where # is an non-negative decimal integer with
- *   or without leading zeros
- * @throws
- *   if versionString does not specify a valid HTTP version number
- */
-function nsHttpVersion(versionString)
-{
-  var matches = /^(\d+)\.(\d+)$/.exec(versionString);
-  if (!matches)
-    throw "Not a valid HTTP version!";
-
-  /** The major version number of this, as a number. */
-  this.major = parseInt(matches[1], 10);
-
-  /** The minor version number of this, as a number. */
-  this.minor = parseInt(matches[2], 10);
-
-  if (isNaN(this.major) || isNaN(this.minor) ||
-      this.major < 0    || this.minor < 0)
-    throw "Not a valid HTTP version!";
-}
-nsHttpVersion.prototype =
-{
-  /**
-   * Returns the standard string representation of the HTTP version represented
-   * by this (e.g., "1.1").
-   */
-  toString: function ()
-  {
-    return this.major + "." + this.minor;
-  },
-
-  /**
-   * Returns true if this represents the same HTTP version as otherVersion,
-   * false otherwise.
-   *
-   * @param otherVersion : nsHttpVersion
-   *   the version to compare against this
-   */
-  equals: function (otherVersion)
-  {
-    return this.major == otherVersion.major &&
-           this.minor == otherVersion.minor;
-  },
-
-  /** True if this >= otherVersion, false otherwise. */
-  atLeast: function(otherVersion)
-  {
-    return this.major > otherVersion.major ||
-           (this.major == otherVersion.major &&
-            this.minor >= otherVersion.minor);
-  }
-};
-
-nsHttpVersion.HTTP_1_0 = new nsHttpVersion("1.0");
-nsHttpVersion.HTTP_1_1 = new nsHttpVersion("1.1");
-
-
-/**
- * An object which stores HTTP headers for a request or response.
- *
- * Note that since headers are case-insensitive, this object converts headers to
- * lowercase before storing them.  This allows the getHeader and hasHeader
- * methods to work correctly for any case of a header, but it means that the
- * values returned by .enumerator may not be equal case-sensitively to the
- * values passed to setHeader when adding headers to this.
- */
-function nsHttpHeaders()
-{
-  /**
-   * A hash of headers, with header field names as the keys and header field
-   * values as the values.  Header field names are case-insensitive, but upon
-   * insertion here they are converted to lowercase.  Header field values are
-   * normalized upon insertion to contain no leading or trailing whitespace.
-   *
-   * Note also that per RFC 2616, section 4.2, two headers with the same name in
-   * a message may be treated as one header with the same field name and a field
-   * value consisting of the separate field values joined together with a "," in
-   * their original order.  This hash stores multiple headers with the same name
-   * in this manner.
-   */
-  this._headers = {};
-}
-nsHttpHeaders.prototype =
-{
-  /**
-   * Sets the header represented by name and value in this.
-   *
-   * @param name : string
-   *   the header name
-   * @param value : string
-   *   the header value
-   * @throws NS_ERROR_INVALID_ARG
-   *   if name or value is not a valid header component
-   */
-  setHeader: function(fieldName, fieldValue, merge)
-  {
-    var name = headerUtils.normalizeFieldName(fieldName);
-    var value = headerUtils.normalizeFieldValue(fieldValue);
-
-    // The following three headers are stored as arrays because their real-world
-    // syntax prevents joining individual headers into a single header using 
-    // ",".  See also <http://hg.mozilla.org/mozilla-central/diff/9b2a99adc05e/netwerk/protocol/http/src/nsHttpHeaderArray.cpp#l77>
-    if (merge && name in this._headers)
-    {
-      if (name === "www-authenticate" ||
-          name === "proxy-authenticate" ||
-          name === "set-cookie") 
-      {
-        this._headers[name].push(value);
-      }
-      else 
-      {
-        this._headers[name][0] += "," + value;
-        NS_ASSERT(this._headers[name].length === 1,
-            "how'd a non-special header have multiple values?")
-      }
-    }
-    else
-    {
-      this._headers[name] = [value];
-    }
-  },
-
-  /**
-   * Returns the value for the header specified by this.
-   *
-   * @throws NS_ERROR_INVALID_ARG
-   *   if fieldName does not constitute a valid header field name
-   * @throws NS_ERROR_NOT_AVAILABLE
-   *   if the given header does not exist in this
-   * @returns string
-   *   the field value for the given header, possibly with non-semantic changes
-   *   (i.e., leading/trailing whitespace stripped, whitespace runs replaced
-   *   with spaces, etc.) at the option of the implementation; multiple 
-   *   instances of the header will be combined with a comma, except for 
-   *   the three headers noted in the description of getHeaderValues
-   */
-  getHeader: function(fieldName)
-  {
-    return this.getHeaderValues(fieldName).join("\n");
-  },
-
-  /**
-   * Returns the value for the header specified by fieldName as an array.
-   *
-   * @throws NS_ERROR_INVALID_ARG
-   *   if fieldName does not constitute a valid header field name
-   * @throws NS_ERROR_NOT_AVAILABLE
-   *   if the given header does not exist in this
-   * @returns [string]
-   *   an array of all the header values in this for the given
-   *   header name.  Header values will generally be collapsed
-   *   into a single header by joining all header values together
-   *   with commas, but certain headers (Proxy-Authenticate,
-   *   WWW-Authenticate, and Set-Cookie) violate the HTTP spec
-   *   and cannot be collapsed in this manner.  For these headers
-   *   only, the returned array may contain multiple elements if
-   *   that header has been added more than once.
-   */
-  getHeaderValues: function(fieldName)
-  {
-    var name = headerUtils.normalizeFieldName(fieldName);
-
-    if (name in this._headers)
-      return this._headers[name];
-    else
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-  },
-
-  /**
-   * Returns true if a header with the given field name exists in this, false
-   * otherwise.
-   *
-   * @param fieldName : string
-   *   the field name whose existence is to be determined in this
-   * @throws NS_ERROR_INVALID_ARG
-   *   if fieldName does not constitute a valid header field name
-   * @returns boolean
-   *   true if the header's present, false otherwise
-   */
-  hasHeader: function(fieldName)
-  {
-    var name = headerUtils.normalizeFieldName(fieldName);
-    return (name in this._headers);
-  },
-
-  /**
-   * Returns a new enumerator over the field names of the headers in this, as
-   * nsISupportsStrings.  The names returned will be in lowercase, regardless of
-   * how they were input using setHeader (header names are case-insensitive per
-   * RFC 2616).
-   */
-  get enumerator()
-  {
-    var headers = [];
-    for (var i in this._headers)
-    {
-      var supports = new SupportsString();
-      supports.data = i;
-      headers.push(supports);
-    }
-
-    return new nsSimpleEnumerator(headers);
-  }
-};
-
-
-/**
- * Constructs an nsISimpleEnumerator for the given array of items.
- *
- * @param items : Array
- *   the items, which must all implement nsISupports
- */
-function nsSimpleEnumerator(items)
-{
-  this._items = items;
-  this._nextIndex = 0;
-}
-nsSimpleEnumerator.prototype =
-{
-  hasMoreElements: function()
-  {
-    return this._nextIndex < this._items.length;
-  },
-  getNext: function()
-  {
-    if (!this.hasMoreElements())
-      throw Cr.NS_ERROR_NOT_AVAILABLE;
-
-    return this._items[this._nextIndex++];
-  },
-  QueryInterface: function(aIID)
-  {
-    if (Ci.nsISimpleEnumerator.equals(aIID) ||
-        Ci.nsISupports.equals(aIID))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  }
-};
-
-
-/**
- * A representation of the data in an HTTP request.
- *
- * @param port : uint
- *   the port on which the server receiving this request runs
- */
-function Request(port)
-{
-  /** Method of this request, e.g. GET or POST. */
-  this._method = "";
-
-  /** Path of the requested resource; empty paths are converted to '/'. */
-  this._path = "";
-
-  /** Query string, if any, associated with this request (not including '?'). */
-  this._queryString = "";
-
-  /** Scheme of requested resource, usually http, always lowercase. */
-  this._scheme = "http";
-
-  /** Hostname on which the requested resource resides. */
-  this._host = undefined;
-
-  /** Port number over which the request was received. */
-  this._port = port;
-
-  var bodyPipe = new Pipe(false, false, 0, PR_UINT32_MAX, null);
-
-  /** Stream from which data in this request's body may be read. */
-  this._bodyInputStream = bodyPipe.inputStream;
-
-  /** Stream to which data in this request's body is written. */
-  this._bodyOutputStream = bodyPipe.outputStream;
-
-  /**
-   * The headers in this request.
-   */
-  this._headers = new nsHttpHeaders();
-
-  /**
-   * For the addition of ad-hoc properties and new functionality without having
-   * to change nsIHttpRequest every time; currently lazily created, as its only
-   * use is in directory listings.
-   */
-  this._bag = null;
-}
-Request.prototype =
-{
-  // SERVER METADATA
-
-  //
-  // see nsIHttpRequest.scheme
-  //
-  get scheme()
-  {
-    return this._scheme;
-  },
-
-  //
-  // see nsIHttpRequest.host
-  //
-  get host()
-  {
-    return this._host;
-  },
-
-  //
-  // see nsIHttpRequest.port
-  //
-  get port()
-  {
-    return this._port;
-  },
-
-  // REQUEST LINE
-
-  //
-  // see nsIHttpRequest.method
-  //
-  get method()
-  {
-    return this._method;
-  },
-
-  //
-  // see nsIHttpRequest.httpVersion
-  //
-  get httpVersion()
-  {
-    return this._httpVersion.toString();
-  },
-
-  //
-  // see nsIHttpRequest.path
-  //
-  get path()
-  {
-    return this._path;
-  },
-
-  //
-  // see nsIHttpRequest.queryString
-  //
-  get queryString()
-  {
-    return this._queryString;
-  },
-
-  // HEADERS
-
-  //
-  // see nsIHttpRequest.getHeader
-  //
-  getHeader: function(name)
-  {
-    return this._headers.getHeader(name);
-  },
-
-  //
-  // see nsIHttpRequest.hasHeader
-  //
-  hasHeader: function(name)
-  {
-    return this._headers.hasHeader(name);
-  },
-
-  //
-  // see nsIHttpRequest.headers
-  //
-  get headers()
-  {
-    return this._headers.enumerator;
-  },
-
-  //
-  // see nsIPropertyBag.enumerator
-  //
-  get enumerator()
-  {
-    this._ensurePropertyBag();
-    return this._bag.enumerator;
-  },
-
-  //
-  // see nsIHttpRequest.headers
-  //
-  get bodyInputStream()
-  {
-    return this._bodyInputStream;
-  },
-
-  //
-  // see nsIPropertyBag.getProperty
-  //
-  getProperty: function(name) 
-  {
-    this._ensurePropertyBag();
-    return this._bag.getProperty(name);
-  },
-
-
-  // NSISUPPORTS
-
-  //
-  // see nsISupports.QueryInterface
-  //
-  QueryInterface: function(iid)
-  {
-    if (iid.equals(Ci.nsIHttpRequest) || iid.equals(Ci.nsISupports))
-      return this;
-
-    throw Cr.NS_ERROR_NO_INTERFACE;
-  },
-
-
-  // PRIVATE IMPLEMENTATION
-  
-  /** Ensures a property bag has been created for ad-hoc behaviors. */
-  _ensurePropertyBag: function()
-  {
-    if (!this._bag)
-      this._bag = new WritablePropertyBag();
-  }
-};
-
-
-// XPCOM trappings
-
-var NSGetFactory = XPCOMUtils.generateNSGetFactory([nsHttpServer]);
-
-/**
- * Creates a new HTTP server listening for loopback traffic on the given port,
- * starts it, and runs the server until the server processes a shutdown request,
- * spinning an event loop so that events posted by the server's socket are
- * processed.
- *
- * This method is primarily intended for use in running this script from within
- * xpcshell and running a functional HTTP server without having to deal with
- * non-essential details.
- *
- * Note that running multiple servers using variants of this method probably
- * doesn't work, simply due to how the internal event loop is spun and stopped.
- *
- * @note
- *   This method only works with Mozilla 1.9 (i.e., Firefox 3 or trunk code);
- *   you should use this server as a component in Mozilla 1.8.
- * @param port
- *   the port on which the server will run, or -1 if there exists no preference
- *   for a specific port; note that attempting to use some values for this
- *   parameter (particularly those below 1024) may cause this method to throw or
- *   may result in the server being prematurely shut down
- * @param basePath
- *   a local directory from which requests will be served (i.e., if this is
- *   "/home/jwalden/" then a request to /index.html will load
- *   /home/jwalden/index.html); if this is omitted, only the default URLs in
- *   this server implementation will be functional
- */
-function server(port, basePath)
-{
-  if (basePath)
-  {
-    var lp = Cc["@mozilla.org/file/local;1"]
-               .createInstance(Ci.nsILocalFile);
-    lp.initWithPath(basePath);
-  }
-
-  // if you're running this, you probably want to see debugging info
-  DEBUG = true;
-
-  var srv = new nsHttpServer();
-  if (lp)
-    srv.registerDirectory("/", lp);
-  srv.registerContentType("sjs", SJS_TYPE);
-  srv.identity.setPrimary("http", "localhost", port);
-  srv.start(port);
-
-  var thread = gThreadManager.currentThread;
-  while (!srv.isStopped())
-    thread.processNextEvent(true);
-
-  // get rid of any pending requests
-  while (thread.hasPendingEvents())
-    thread.processNextEvent(true);
-
-  DEBUG = false;
-}
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -2,23 +2,18 @@
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
-const CC = Components.Constructor;
 const Cr = Components.results;
 
-const LocalFile = CC('@mozilla.org/file/local;1',
-                     'nsILocalFile',
-                     'initWithPath');
-
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/ContactService.jsm');
 Cu.import('resource://gre/modules/Webapps.jsm');
 
 XPCOMUtils.defineLazyGetter(Services, 'env', function() {
   return Cc['@mozilla.org/process/environment;1']
            .getService(Ci.nsIEnvironment);
@@ -39,43 +34,29 @@ XPCOMUtils.defineLazyGetter(Services, 'a
            .getService(Ci.nsIAudioManager);
 });
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'fm', function() {
   return Cc['@mozilla.org/focus-manager;1']
            .getService(Ci.nsFocusManager);
 });
 
-#ifndef MOZ_WIDGET_GONK
-// In order to use http:// scheme instead of file:// scheme
-// (that is much more restricted) the following code kick-off
-// a local http server listening on http://127.0.0.1:7777 and
-// http://localhost:7777.
-function startupHttpd(baseDir, port) {
-  const httpdURL = 'chrome://browser/content/httpd.js';
-  let httpd = {};
-  Services.scriptloader.loadSubScript(httpdURL, httpd);
-  let server = new httpd.nsHttpServer();
-  server.registerDirectory('/', new LocalFile(baseDir));
-  server.registerContentType('appcache', 'text/cache-manifest');
-  server.start(port);
-}
-#endif
-
 // FIXME Bug 707625
 // until we have a proper security model, add some rights to
 // the pre-installed web applications
 // XXX never grant 'content-camera' to non-gaia apps
 function addPermissions(urls) {
   let permissions = [
     'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app',
     'content-camera', 'webcontacts-manage', 'wifi-manage', 'desktop-notification',
     'geolocation'
   ];
   urls.forEach(function(url) {
+    url = url.trim();
+    dump("XxXxX adding permissions for " + url);
     let uri = Services.io.newURI(url, null, null);
     let allow = Ci.nsIPermissionManager.ALLOW_ACTION;
 
     permissions.forEach(function(permission) {
       Services.perms.add(uri, permission, allow);
     });
   });
 }
@@ -90,27 +71,17 @@ var shell = {
 
   get homeURL() {
     try {
       let homeSrc = Services.env.get('B2G_HOMESCREEN');
       if (homeSrc)
         return homeSrc;
     } catch (e) {}
 
-    let urls = Services.prefs.getCharPref('browser.homescreenURL').split(',');
-    for (let i = 0; i < urls.length; i++) {
-      let url = urls[i];
-      if (url.substring(0, 7) != 'file://')
-        return url;
-
-      let file = new LocalFile(url.substring(7, url.length));
-      if (file.exists())
-        return url;
-    }
-    return null;
+    return Services.prefs.getCharPref('browser.homescreenURL');
   },
 
   start: function shell_init() {
     let homeURL = this.homeURL;
     if (!homeURL) {
       let msg = 'Fatal error during startup: No homescreen found: try setting B2G_HOMESCREEN';
       return alert(msg);
     }
@@ -127,42 +98,22 @@ var shell = {
     // Until the volume can be set from the content side, set it to a
     // a specific value when the device starts. This way the front-end
     // can display a notification when the volume change and show a volume
     // level modified from this point.
     try {
       Services.audioManager.masterVolume = 0.5;
     } catch(e) {}
 
+    let domains = "";
     try {
-      Services.io.offline = false;
-
-      let fileScheme = 'file://';
-      if (homeURL.substring(0, fileScheme.length) == fileScheme) {
-#ifndef MOZ_WIDGET_GONK
-        homeURL = homeURL.replace(fileScheme, '');
-
-        let baseDir = homeURL.split('/');
-        baseDir.pop();
-        baseDir = baseDir.join('/');
+      domains = Services.prefs.getCharPref('b2g.privileged.domains');
+    } catch(e) {}
 
-        const SERVER_PORT = 7777;
-        startupHttpd(baseDir, SERVER_PORT);
-
-        let baseHost = 'http://localhost';
-        homeURL = homeURL.replace(baseDir, baseHost + ':' + SERVER_PORT);
-#else
-        homeURL = 'http://localhost:7777' + homeURL.replace(fileScheme, '');
-#endif
-      }
-      addPermissions([homeURL]);
-    } catch (e) {
-      let msg = 'Fatal error during startup: [' + e + '[' + homeURL + ']';
-      return alert(msg);
-    }
+    addPermissions(domains.split(","));
 
     // Load webapi.js as a frame script
     let frameScriptUrl = 'chrome://browser/content/webapi.js';
     try {
       messageManager.loadFrameScript(frameScriptUrl, true);
     } catch (e) {
       dump('Error loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n');
     }
--- a/b2g/chrome/jar.mn
+++ b/b2g/chrome/jar.mn
@@ -4,19 +4,16 @@ chrome.jar:
 % content branding %content/branding/
 % content browser %content/
 
 * content/shell.xul                     (content/shell.xul)
 * content/shell.js                      (content/shell.js)
 #ifndef MOZ_TOUCH
   content/touch.js                      (content/touch.js)
 #endif
-#ifndef MOZ_WIDGET_GONK
-  content/httpd.js                      (content/httpd.js)
-#endif
   content/webapi.js                     (content/webapi.js)
   content/content.css                   (content/content.css)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
 % override chrome://global/skin/netError.css chrome://browser/content/netError.css
   content/netError.xhtml                (content/netError.xhtml)
   content/netError.css                  (content/netError.css)
   content/images/errorpage-larry-black.png (content/images/errorpage-larry-black.png)
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -19,8 +19,13 @@ contract @mozilla.org/content-permission
 component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
 contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
 #endif
 
 # MozKeyboard.js
 component {397a7fdf-2254-47be-b74e-76625a1a66d5} MozKeyboard.js
 contract @mozilla.org/b2g-keyboard;1 {397a7fdf-2254-47be-b74e-76625a1a66d5}
 category JavaScript-navigator-property mozKeyboard @mozilla.org/b2g-keyboard;1
+
+# DirectoryProvider.js
+component {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5} DirectoryProvider.js
+contract @mozilla.org/browser/directory-provider;1 {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}
+category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
new file mode 100644
--- /dev/null
+++ b/b2g/components/DirectoryProvider.js
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const OFFLINE_PROFILE_DIR = "/data/local"
+
+function DirectoryProvider() {
+}
+
+DirectoryProvider.prototype = {
+  classID: Components.ID("{9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}"),
+  
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
+
+  getFile: function dp_getFile(prop, persistent) {
+#ifdef MOZ_WIDGET_GONK
+    if (prop == "cachePDir" || prop == "webappsDir") {
+      prop.persistent = true;
+      let file = Cc["@mozilla.org/file/local;1"]
+                   .createInstance(Ci.nsILocalFile)
+      file.initWithPath(OFFLINE_PROFILE_DIR);
+      return file;
+    }
+#endif
+
+    return null;
+  }
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]);
+
--- a/b2g/components/Makefile.in
+++ b/b2g/components/Makefile.in
@@ -49,15 +49,16 @@ XPIDLSRCS = \
         $(NULL)
 
 EXTRA_PP_COMPONENTS = \
         B2GComponents.manifest \
         CameraContent.js \
         AlertsService.js \
         ContentPermissionPrompt.js \
         MozKeyboard.js \
+        DirectoryProvider.js \
         $(NULL)
 
 ifdef MOZ_UPDATER
 EXTRA_PP_COMPONENTS += UpdatePrompt.js
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -623,8 +623,9 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/MarionetteComponents.manifest
 @BINPATH@/components/marionettecomponent.js
 @BINPATH@/components/AlertsService.js
 @BINPATH@/components/ContentPermissionPrompt.js
 #ifdef MOZ_UPDATER
 @BINPATH@/components/UpdatePrompt.js
 #endif
 @BINPATH@/components/MozKeyboard.js
+@BINPATH@/components/DirectoryProvider.js
--- a/browser/Makefile.in
+++ b/browser/Makefile.in
@@ -52,27 +52,22 @@ PARALLEL_DIRS = \
   $(NULL)
 
 DIRS = \
   devtools \
   app \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
-ifdef MOZ_INSTALLER
 DIRS += installer/windows
 endif
-endif
 
 include $(topsrcdir)/config/rules.mk
 
 ifeq ($(OS_ARCH),WINNT)
-ifdef MOZ_INSTALLER
-
 # For Windows build the uninstaller during the application build since the
 # uninstaller is included with the application for mar file generation.
 libs::
 	$(MAKE) -C installer/windows uninstaller
 ifdef MOZ_MAINTENANCE_SERVICE
 	$(MAKE) -C installer/windows maintenanceservice_installer
 endif
 endif
-endif
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1331848989000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1332452348000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
@@ -64,32 +64,40 @@
       <emItem  blockID="i64" id="royal@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i72" os="WINNT" id="{4ED1F68A-5463-4931-9384-8FFF5ED91D92}">
                         <versionRange  minVersion="3.4.1" maxVersion="3.4.1.194" severity="1">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i77" id="{fa277cfc-1d75-4949-a1f9-4ac8e41b2dfd}">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i53" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
                         <versionRange  minVersion="2.0.3" maxVersion="2.0.3">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i59" id="ghostviewer@youtube2.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i70" id="psid-vhvxQHMZBOzUZA@jetpack">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                   </emItem>
+      <emItem  blockID="i68" id="flashupdate@adobe.com">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                  </emItem>
       <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
                         </emItem>
       <emItem  blockID="i67" id="youtube2@youtube2.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i60" id="youtb3@youtb3.com">
                         <versionRange  minVersion="0" maxVersion="*">
@@ -126,17 +134,17 @@
                     </versionRange>
                   </emItem>
       <emItem  blockID="i48" id="admin@youtubespeedup.com">
                         </emItem>
       <emItem  blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
                         <versionRange  minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
                     </versionRange>
                   </emItem>
-      <emItem  blockID="i68" id="flashupdate@adobe.com">
+      <emItem  blockID="i76" id="crossriderapp3924@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                   </emItem>
       <emItem  blockID="i47" id="youtube@youtube2.com">
                         </emItem>
       <emItem  blockID="i62" id="jid0-EcdqvFOgWLKHNJPuqAnawlykCGZ@jetpack">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
--- a/browser/base/content/abouthome/aboutHome.css
+++ b/browser/base/content/abouthome/aboutHome.css
@@ -250,16 +250,17 @@ body[narrow] #launcher[session] > .launc
 .launchButton:hover:active {
   background-image: -moz-linear-gradient(hsla(211,79%,6%,.02), hsla(211,79%,6%,.05));
   border-color: hsla(210,54%,20%,.2) hsla(210,54%,20%,.23) hsla(210,54%,20%,.25);
   box-shadow: 0 1px 1px hsla(211,79%,6%,.05) inset,
               0 0 1px hsla(211,79%,6%,.1) inset;
   -moz-transition-duration: 0ms;
 }
 
+.launchButton[hidden],
 #launcher:not([session]) > #restorePreviousSessionSeparator,
 #launcher:not([session]) > #restorePreviousSession {
   display: none;
 }
 
 #restorePreviousSessionSeparator {
   width: 3px;
   height: 116px;
@@ -293,38 +294,42 @@ body[narrow] #restorePreviousSession {
 
 .launchButton::before {
   display: block;
   height: 32px;
   margin-bottom: 6px;
   line-height: 0; /* remove extra vertical space due to non-zero font-size */
 }
 
+#downloads::before {
+  content: url("chrome://browser/content/abouthome/downloads.png");
+}
+
 #bookmarks::before {
   content: url("chrome://browser/content/abouthome/bookmarks.png");
 }
 
 #history::before {
   content: url("chrome://browser/content/abouthome/history.png");
 }
 
-#settings::before {
-  content: url("chrome://browser/content/abouthome/settings.png");
+#apps::before {
+  content: url("chrome://browser/content/abouthome/apps.png");
 }
 
 #addons::before {
   content: url("chrome://browser/content/abouthome/addons.png");
 }
 
-#downloads::before {
-  content: url("chrome://browser/content/abouthome/downloads.png");
+#sync::before {
+  content: url("chrome://browser/content/abouthome/sync.png");
 }
 
-#sync::before {
-  content: url("chrome://browser/content/abouthome/sync.png");
+#settings::before {
+  content: url("chrome://browser/content/abouthome/settings.png");
 }
 
 #restorePreviousSession::before {
   content: url("chrome://browser/content/abouthome/restore-large.png");
   height: 48px;
   width: 48px;
   display: inline-block; /* display on same line as text label */
   vertical-align: middle;
--- a/browser/base/content/abouthome/aboutHome.xhtml
+++ b/browser/base/content/abouthome/aboutHome.xhtml
@@ -87,21 +87,22 @@
           <span id="defaultSnippet2">&abouthome.defaultSnippet2.v1;</span>
         </div>
         <div id="snippets"/>
       </div>
     </div>
     <div class="spacer"/>
 
     <div id="launcher" session="true">
+      <button class="launchButton" id="downloads">&abouthome.downloadsButton.label;</button>
       <button class="launchButton" id="bookmarks">&abouthome.bookmarksButton.label;</button>
       <button class="launchButton" id="history">&abouthome.historyButton.label;</button>
-      <button class="launchButton" id="settings">&abouthome.settingsButton.label;</button>
+      <button class="launchButton" id="apps" hidden="true">&abouthome.appsButton.label;</button>
       <button class="launchButton" id="addons">&abouthome.addonsButton.label;</button>
-      <button class="launchButton" id="downloads">&abouthome.downloadsButton.label;</button>
       <button class="launchButton" id="sync">&syncBrand.shortName.label;</button>
+      <button class="launchButton" id="settings">&abouthome.settingsButton.label;</button>
       <div id="restorePreviousSessionSeparator"/>
       <button class="launchButton" id="restorePreviousSession">&historyRestoreLastSession.label;</button>
     </div>
 
-    <a id="aboutMozilla" href="http://www.mozilla.com/about/"/>
+    <a id="aboutMozilla" href="http://www.mozilla.org/about/"/>
   </body>
 </html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c6907b2006fa30d7aec833e3cff99706014babac
GIT binary patch
literal 438
zc$@*Y0ZIOeP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004fNkl<ZSi`kf
z%Wi})44j~@T4^bdo*{()|H&RYM%jqP4sjz{LMXAvPVBLPzZC#{0T?Ks{O7eg>L1Va
z2LMk13xJ&xx!D6mpstwdUuhE{vfQ{x0x&g#2-J~I^sfLOAiDjHBmgr*cqi=h8{;B9
zcqjXXY&SrN@lDa)gAZa+q7VW67(sO08x`-5D+ojq4Is2gi07K|O}x(^%b<iTDc6yQ
zT{AwMJ?=bRk^-d6j;q&RM)5Me1;lDlCW_>YwtuzOmX6+7BQY91pK3BG2eCIwwej*g
zwg%&yM@b)UMk4a83z>XZf`~lh#;8Hzj8Z1}()p3+RsiFFc%Lc&Brgz_3RhDx-TB-C
z;#NOl<#TH-m}{$`h0i6=wTfBQPgwa}^4wY{O#MU)pEvgt9(-QgPjtEGgA<?E_7lfk
zV7WglrCR%mXqgM{^0~F2Q0IacKDYD}mvey^pTFxTUgm;wI9-U&YdGx-{V&6*tPgJN
g`R%=O{SW#13ymv4XFC>~ZvX%Q07*qoM6N<$g36e}C;$Ke
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2702,16 +2702,20 @@ function PageProxyClickHandler(aEvent)
 }
 
 /**
  *  Handle load of some pages (about:*) so that we can make modifications
  *  to the DOM for unprivileged pages.
  */
 function BrowserOnAboutPageLoad(document) {
   if (/^about:home$/i.test(document.documentURI)) {
+    // XXX bug 738646 - when Marketplace is launched, remove this statement and
+    // the hidden attribute set on the apps button in aboutHome.xhtml
+    if (getBoolPref("browser.aboutHome.apps", false))
+      document.getElementById("apps").removeAttribute("hidden");
     let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
              getService(Components.interfaces.nsISessionStore);
     if (!ss.canRestoreLastSession)
       document.getElementById("launcher").removeAttribute("session");
   }
 }
 
 /**
@@ -2844,34 +2848,37 @@ function BrowserOnClick(event) {
     else if (/^about:home$/i.test(ownerDoc.documentURI)) {
       if (ot == ownerDoc.getElementById("restorePreviousSession")) {
         let ss = Cc["@mozilla.org/browser/sessionstore;1"].
                  getService(Ci.nsISessionStore);
         if (ss.canRestoreLastSession)
           ss.restoreLastSession();
         ownerDoc.getElementById("launcher").removeAttribute("session");
       }
+      else if (ot == ownerDoc.getElementById("downloads")) {
+        BrowserDownloadsUI();
+      }
       else if (ot == ownerDoc.getElementById("bookmarks")) {
         PlacesCommandHook.showPlacesOrganizer("AllBookmarks");
       }
       else if (ot == ownerDoc.getElementById("history")) {
         PlacesCommandHook.showPlacesOrganizer("History");
       }
-      else if (ot == ownerDoc.getElementById("settings")) {
-        openPreferences();
+      else if (ot == ownerDoc.getElementById("apps")) {
+        openUILinkIn("https://marketplace.mozilla.org/", "tab");
       }
       else if (ot == ownerDoc.getElementById("addons")) {
         BrowserOpenAddonsMgr();
       }
-      else if (ot == ownerDoc.getElementById("downloads")) {
-        BrowserDownloadsUI();
-      }
       else if (ot == ownerDoc.getElementById("sync")) {
         openPreferences("paneSync");
       }
+      else if (ot == ownerDoc.getElementById("settings")) {
+        openPreferences();
+      }
     }
 }
 
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -16,22 +16,23 @@ browser.jar:
 *       content/browser/aboutDialog.js                (content/aboutDialog.js)
 *       content/browser/aboutDialog.css               (content/aboutDialog.css)
 *       content/browser/aboutRobots.xhtml             (content/aboutRobots.xhtml)
 *       content/browser/abouthome/aboutHome.xhtml     (content/abouthome/aboutHome.xhtml)
 *       content/browser/abouthome/aboutHome.js        (content/abouthome/aboutHome.js)
 *       content/browser/abouthome/aboutHome.css       (content/abouthome/aboutHome.css)
         content/browser/abouthome/snippet1.png        (content/abouthome/snippet1.png)
         content/browser/abouthome/snippet2.png        (content/abouthome/snippet2.png)
+        content/browser/abouthome/downloads.png       (content/abouthome/downloads.png)
         content/browser/abouthome/bookmarks.png       (content/abouthome/bookmarks.png)
         content/browser/abouthome/history.png         (content/abouthome/history.png)
-        content/browser/abouthome/settings.png        (content/abouthome/settings.png)
+        content/browser/abouthome/apps.png            (content/abouthome/apps.png)
         content/browser/abouthome/addons.png          (content/abouthome/addons.png)
-        content/browser/abouthome/downloads.png       (content/abouthome/downloads.png)
         content/browser/abouthome/sync.png            (content/abouthome/sync.png)
+        content/browser/abouthome/settings.png        (content/abouthome/settings.png)
         content/browser/abouthome/restore.png         (content/abouthome/restore.png)
         content/browser/abouthome/restore-large.png   (content/abouthome/restore-large.png)
         content/browser/abouthome/mozilla.png         (content/abouthome/mozilla.png)
         content/browser/abouthome/noise.png           (content/abouthome/noise.png)
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
--- a/browser/components/thumbnails/PageThumbs.jsm
+++ b/browser/components/thumbnails/PageThumbs.jsm
@@ -105,23 +105,40 @@ let PageThumbs = {
 
   /**
    * Captures a thumbnail for the given browser and stores it to the cache.
    * @param aBrowser The browser to capture a thumbnail for.
    * @param aCallback The function to be called when finished (optional).
    */
   captureAndStore: function PageThumbs_captureAndStore(aBrowser, aCallback) {
     let url = aBrowser.currentURI.spec;
+    let channel = aBrowser.docShell.currentDocumentChannel;
+    let originalURL = channel.originalURI.spec;
+
     this.capture(aBrowser.contentWindow, function (aInputStream) {
       let telemetryStoreTime = new Date();
 
       function finish(aSuccessful) {
         if (aSuccessful) {
           Services.telemetry.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS")
             .add(new Date() - telemetryStoreTime);
+
+          // We've been redirected. Create a copy of the current thumbnail for
+          // the redirect source. We need to do this because:
+          //
+          // 1) Users can drag any kind of links onto the newtab page. If those
+          //    links redirect to a different URL then we want to be able to
+          //    provide thumbnails for both of them.
+          //
+          // 2) The newtab page should actually display redirect targets, only.
+          //    Because of bug 559175 this information can get lost when using
+          //    Sync and therefore also redirect sources appear on the newtab
+          //    page. We also want thumbnails for those.
+          if (url != originalURL)
+            PageThumbsCache._copy(url, originalURL);
         }
 
         if (aCallback)
           aCallback(aSuccessful);
       }
 
       // Get a writeable cache entry.
       PageThumbsCache.getWriteEntry(url, function (aEntry) {
@@ -202,16 +219,64 @@ let PageThumbsCache = {
    * @param aCallback The callback that is called when the cache entry is ready.
    */
   getWriteEntry: function Cache_getWriteEntry(aKey, aCallback) {
     // Try to open the desired cache entry.
     this._openCacheEntry(aKey, Ci.nsICache.ACCESS_WRITE, aCallback);
   },
 
   /**
+   * Copies an existing cache entry's data to a new cache entry.
+   * @param aSourceKey The key that contains the data to copy.
+   * @param aTargetKey The key that will be the copy of aSourceKey's data.
+   */
+  _copy: function Cache_copy(aSourceKey, aTargetKey) {
+    let sourceEntry, targetEntry, waitingCount = 2;
+
+    function finish() {
+      if (sourceEntry)
+        sourceEntry.close();
+
+      if (targetEntry)
+        targetEntry.close();
+    }
+
+    function copyDataWhenReady() {
+      if (--waitingCount > 0)
+        return;
+
+      if (!sourceEntry || !targetEntry) {
+        finish();
+        return;
+      }
+
+      let inputStream = sourceEntry.openInputStream(0);
+      let outputStream = targetEntry.openOutputStream(0);
+
+      // Copy the image data to a new entry.
+      NetUtil.asyncCopy(inputStream, outputStream, function (aResult) {
+        if (Components.isSuccessCode(aResult))
+          targetEntry.markValid();
+
+        finish();
+      });
+    }
+
+    this.getReadEntry(aSourceKey, function (aSourceEntry) {
+      sourceEntry = aSourceEntry;
+      copyDataWhenReady();
+    });
+
+    this.getWriteEntry(aTargetKey, function (aTargetEntry) {
+      targetEntry = aTargetEntry;
+      copyDataWhenReady();
+    });
+  },
+
+  /**
    * Opens the cache entry identified by the given key.
    * @param aKey The key identifying the desired cache entry.
    * @param aAccess The desired access mode (see nsICache.ACCESS_* constants).
    * @param aCallback The function to be called when the cache entry was opened.
    */
   _openCacheEntry: function Cache_openCacheEntry(aKey, aAccess, aCallback) {
     function onCacheEntryAvailable(aEntry, aAccessGranted, aStatus) {
       let validAccess = aAccess == aAccessGranted;
--- a/browser/components/thumbnails/test/Makefile.in
+++ b/browser/components/thumbnails/test/Makefile.in
@@ -8,14 +8,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = browser/components/thumbnails/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
 	browser_thumbnails_capture.js \
+	browser_thumbnails_redirect.js \
 	browser_thumbnails_bug726727.js \
 	head.js \
+	background_red.html \
+	background_red_redirect.sjs \
 	$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/background_red.html
@@ -0,0 +1,3 @@
+<html>
+  <body bgcolor=ff0000></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/background_red_redirect.sjs
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(aRequest, aResponse) {
+ // Set HTTP Status.
+ aResponse.setStatusLine(aRequest.httpVersion, 301, "Moved Permanently");
+
+ // Set redirect URI.
+ aResponse.setHeader("Location", "http://mochi.test:8888/browser/browser/components/thumbnails/test/background_red.html");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/thumbnails/test/browser_thumbnails_redirect.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const URL = "http://mochi.test:8888/browser/browser/components/thumbnails/" +
+            "test/background_red_redirect.sjs";
+
+let cacheService = Cc["@mozilla.org/network/cache-service;1"]
+                   .getService(Ci.nsICacheService);
+
+/**
+ * These tests ensure that we save and provide thumbnails for redirecting sites.
+ */
+function runTests() {
+  // Kick off history by loading a tab first or the test fails in single mode.
+  yield addTab(URL);
+  gBrowser.removeTab(gBrowser.selectedTab);
+
+  // Create a tab, redirecting to a page with a red background.
+  yield addTab(URL);
+  yield captureAndCheckColor(255, 0, 0, "we have a red thumbnail");
+
+  // Wait until the referrer's thumbnail's cache entry has been written.
+  yield whenCacheEntryExists(URL);
+  yield checkThumbnailColor(URL, 255, 0, 0, "referrer has a red thumbnail");
+}
+
+function whenCacheEntryExists(aKey) {
+  let callback = next;
+
+  checkCacheEntryExists(aKey, function (aExists) {
+    if (!aExists)
+      callback = function () whenCacheEntryExists(aKey);
+
+    executeSoon(callback);
+  });
+}
+
+function checkCacheEntryExists(aKey, aCallback) {
+  PageThumbsCache.getReadEntry(aKey, function (aEntry) {
+    let inputStream = aEntry && aEntry.openInputStream(0);
+    let exists = inputStream && inputStream.available();
+
+    if (inputStream)
+      inputStream.close();
+
+    if (aEntry)
+      aEntry.close();
+
+    aCallback(exists);
+  });
+}
--- a/browser/components/thumbnails/test/head.js
+++ b/browser/components/thumbnails/test/head.js
@@ -93,36 +93,48 @@ function whenLoaded(aElement, aCallback)
  * @param aBlue The blue component's intensity.
  * @param aMessage The info message to print when comparing the pixel color.
  */
 function captureAndCheckColor(aRed, aGreen, aBlue, aMessage) {
   let browser = gBrowser.selectedBrowser;
 
   // Capture the screenshot.
   PageThumbs.captureAndStore(browser, function () {
-    let width = 100, height = 100;
-    let thumb = PageThumbs.getThumbnailURL(browser.currentURI.spec, width, height);
+    checkThumbnailColor(browser.currentURI.spec, aRed, aGreen, aBlue, aMessage);
+  });
+}
 
-    getXULDocument(function (aDocument) {
-      let htmlns = "http://www.w3.org/1999/xhtml";
-      let img = aDocument.createElementNS(htmlns, "img");
-      img.setAttribute("src", thumb);
+/**
+ * Retrieve a thumbnail from the cache and compare its pixel color values.
+ * @param aURL The URL of the thumbnail's page.
+ * @param aRed The red component's intensity.
+ * @param aGreen The green component's intensity.
+ * @param aBlue The blue component's intensity.
+ * @param aMessage The info message to print when comparing the pixel color.
+ */
+function checkThumbnailColor(aURL, aRed, aGreen, aBlue, aMessage) {
+  let width = 100, height = 100;
+  let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
 
-      whenLoaded(img, function () {
-        let canvas = aDocument.createElementNS(htmlns, "canvas");
-        canvas.setAttribute("width", width);
-        canvas.setAttribute("height", height);
+  getXULDocument(function (aDocument) {
+    let htmlns = "http://www.w3.org/1999/xhtml";
+    let img = aDocument.createElementNS(htmlns, "img");
+    img.setAttribute("src", thumb);
 
-        // Draw the image to a canvas and compare the pixel color values.
-        let ctx = canvas.getContext("2d");
-        ctx.drawImage(img, 0, 0, width, height);
-        checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
+    whenLoaded(img, function () {
+      let canvas = aDocument.createElementNS(htmlns, "canvas");
+      canvas.setAttribute("width", width);
+      canvas.setAttribute("height", height);
 
-        next();
-      });
+      // Draw the image to a canvas and compare the pixel color values.
+      let ctx = canvas.getContext("2d");
+      ctx.drawImage(img, 0, 0, width, height);
+      checkCanvasColor(ctx, aRed, aGreen, aBlue, aMessage);
+
+      next();
     });
   });
 }
 
 /**
  * Passes a XUL document (created if necessary) to the given callback.
  * @param aCallback The function to be called when the XUL document has been
  *                  created. The first argument will be the document.
--- a/browser/makefiles.sh
+++ b/browser/makefiles.sh
@@ -98,21 +98,19 @@ security/manager/locales/Makefile
 
 if [ "$MOZ_SAFE_BROWSING" ]; then
   add_makefiles "
     browser/components/safebrowsing/Makefile
   "
 fi
 
 if [ "$MOZ_WIDGET_TOOLKIT" = "windows" ]; then
-  if [ "$MOZ_INSTALLER" ]; then
-    add_makefiles "
-      browser/installer/windows/Makefile
-    "
-  fi
+  add_makefiles "
+    browser/installer/windows/Makefile
+  "
 fi
 
 if [ "$MOZ_WIDGET_TOOLKIT" = "gtk2" -o "$MOZ_WIDGET_TOOLKIT" = "qt" ]; then
   add_makefiles "
     browser/themes/gnomestripe/Makefile
     browser/themes/gnomestripe/communicator/Makefile
   "
 elif [ "$MOZ_WIDGET_TOOLKIT" = "cocoa" ]; then
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -136,17 +136,16 @@ INCREMENTAL_LINKER = @INCREMENTAL_LINKER
 MACOSX_DEPLOYMENT_TARGET = @MACOSX_DEPLOYMENT_TARGET@
 ENABLE_TESTS	= @ENABLE_TESTS@
 ENABLE_MARIONETTE = @ENABLE_MARIONETTE@
 IBMBIDI = @IBMBIDI@
 MOZ_UNIVERSALCHARDET = @MOZ_UNIVERSALCHARDET@
 ACCESSIBILITY = @ACCESSIBILITY@
 MOZ_BRANDING_DIRECTORY = @MOZ_BRANDING_DIRECTORY@
 XPCOM_USE_LEA = @XPCOM_USE_LEA@
-MOZ_INSTALLER	= @MOZ_INSTALLER@
 MOZ_MAINTENANCE_SERVICE	= @MOZ_MAINTENANCE_SERVICE@
 MOZ_VERIFY_MAR_SIGNATURE	= @MOZ_VERIFY_MAR_SIGNATURE@
 MOZ_ENABLE_SIGNMAR	= @MOZ_ENABLE_SIGNMAR@
 MOZ_UPDATER	= @MOZ_UPDATER@
 MOZ_UPDATE_CHANNEL	= @MOZ_UPDATE_CHANNEL@
 MOZ_UPDATE_PACKAGING	= @MOZ_UPDATE_PACKAGING@
 MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@
 NS_ENABLE_TSF = @NS_ENABLE_TSF@
--- a/configure.in
+++ b/configure.in
@@ -4525,17 +4525,16 @@ dnl ====================================
 MOZ_ARG_HEADER(Application)
 
 ENABLE_TESTS=1
 ENABLE_SYSTEM_EXTENSION_DIRS=1
 MOZ_BRANDING_DIRECTORY=
 MOZ_OFFICIAL_BRANDING=
 MOZ_FEEDS=1
 MOZ_FLEXBOX=
-MOZ_INSTALLER=1
 MOZ_JSDEBUGGER=1
 MOZ_AUTH_EXTENSION=1
 MOZ_OGG=1
 MOZ_RAW=
 MOZ_SYDNEYAUDIO=
 MOZ_CUBEB=
 MOZ_VORBIS=
 MOZ_TREMOR=
@@ -4608,17 +4607,16 @@ esac
 
 case "${target}" in
     *-android*|*-linuxandroid*)
         if test "$CPU_ARCH" = "arm" ; then
           USE_ARM_KUSER=1
         fi
 
         NSS_DISABLE_DBM=1
-        MOZ_INSTALLER=
         NECKO_WIFI=
         MOZ_THEME_FASTSTRIPE=1
         MOZ_TREE_FREETYPE=1
         MOZ_MEMORY=1
         MOZ_RAW=1
         ;;
 esac
 
@@ -6289,17 +6287,17 @@ if test -n "$MOZ_TREE_FREETYPE"; then
    AC_SUBST(CAIRO_FT_CFLAGS)
 fi
 
 dnl ========================================================
 dnl Installer
 dnl ========================================================
 dnl Abort Windows build if the required major version and
 dnl minimum minor version of Unicode NSIS isn't in the path.
-if test -n "$MOZ_INSTALLER" -a "$OS_ARCH" = "WINNT"; then
+if test "$OS_ARCH" = "WINNT"; then
     REQ_NSIS_MAJOR_VER=2
     MIN_NSIS_MINOR_VER=33
     MOZ_PATH_PROGS(MAKENSISU, $MAKENSISU makensisu-2.46 makensisu makensis)
     if test -z "$MAKENSISU" -o "$MAKENSISU" = ":"; then
         AC_MSG_ERROR([To build the installer you must have the latest MozillaBuild or Unicode NSIS with a major version of $REQ_NSIS_MAJOR_VER and a minimum minor version of $MIN_NSIS_MINOR_VER in your path.])
     fi
     changequote(,)
     MAKENSISU_VER=`"$MAKENSISU" -version 2>/dev/null | sed -e '/-Unicode/!s/.*//g' -e 's/^v\([0-9]\+\.[0-9]\+\)\-Unicode$/\1/g'`
@@ -8367,17 +8365,16 @@ AC_SUBST(MOZ_PSM)
 AC_SUBST(MOZ_DEBUG)
 AC_SUBST(MOZ_DEBUG_SYMBOLS)
 AC_SUBST(MOZ_DEBUG_ENABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_DISABLE_DEFS)
 AC_SUBST(MOZ_DEBUG_FLAGS)
 AC_SUBST(MOZ_DEBUG_LDFLAGS)
 AC_SUBST(WARNINGS_AS_ERRORS)
 AC_SUBST(MOZ_EXTENSIONS)
-AC_SUBST(MOZ_INSTALLER)
 AC_SUBST(MOZ_JSDEBUGGER)
 AC_SUBST(MOZ_LOG_REFCNT)
 AC_SUBST(MOZ_LEAKY)
 AC_SUBST(MOZ_ENABLE_PROFILER_SPS)
 AC_SUBST(MOZ_JPROF)
 AC_SUBST(MOZ_SHARK)
 AC_SUBST(MOZ_CALLGRIND)
 AC_SUBST(MOZ_VTUNE)
--- a/content/base/public/nsDOMFile.h
+++ b/content/base/public/nsDOMFile.h
@@ -332,17 +332,17 @@ protected:
 };
 
 class nsDOMFileList MOZ_FINAL : public nsIDOMFileList,
                                 public nsWrapperCache
 {
 public:
   nsDOMFileList(nsISupports *aParent) : mParent(aParent)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMFileList)
 
   NS_DECL_NSIDOMFILELIST
 
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
--- a/content/base/src/nsContentList.h
+++ b/content/base/src/nsContentList.h
@@ -81,18 +81,17 @@ class Element;
 }
 
 
 class nsBaseContentList : public nsINodeList
 {
 public:
   nsBaseContentList()
   {
-    // Mark ourselves as a proxy
-    SetIsProxy();
+    SetIsDOMBinding();
   }
   virtual ~nsBaseContentList();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   // nsIDOMNodeList
   NS_DECL_NSIDOMNODELIST
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6611,17 +6611,17 @@ nsContentUtils::GetRootDocument(nsIDocum
 
 // static
 void
 nsContentUtils::ReleaseWrapper(nsISupports* aScriptObjectHolder,
                                nsWrapperCache* aCache)
 {
   if (aCache->PreservingWrapper()) {
     JSObject* obj = aCache->GetWrapperPreserveColor();
-    if (aCache->IsProxy()) {
+    if (aCache->IsDOMBinding()) {
       JSCompartment *compartment = js::GetObjectCompartment(obj);
       xpc::CompartmentPrivate *priv =
         static_cast<xpc::CompartmentPrivate *>(JS_GetCompartmentPrivate(compartment));
       priv->RemoveDOMExpandoObject(obj);
     }
     else {
       DropJSObjects(aScriptObjectHolder);
     }
--- a/content/base/src/nsDOMTokenList.cpp
+++ b/content/base/src/nsDOMTokenList.cpp
@@ -48,17 +48,17 @@
 
 
 nsDOMTokenList::nsDOMTokenList(nsGenericElement *aElement, nsIAtom* aAttrAtom)
   : mElement(aElement),
     mAttrAtom(aAttrAtom)
 {
   // We don't add a reference to our element. If it goes away,
   // we'll be told to drop our reference
-  SetIsProxy();
+  SetIsDOMBinding();
 }
 
 nsDOMTokenList::~nsDOMTokenList() { }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMTokenList)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMTokenList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
--- a/content/base/src/nsGenericElement.h
+++ b/content/base/src/nsGenericElement.h
@@ -95,17 +95,17 @@ typedef PRUptrdiff PtrBits;
  * @see nsIDOMNodeList
  */
 class nsChildContentList : public nsINodeList
 {
 public:
   nsChildContentList(nsINode* aNode)
     : mNode(aNode)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsChildContentList)
 
   // nsWrapperCache
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap);
--- a/content/events/src/nsDOMTouchEvent.h
+++ b/content/events/src/nsDOMTouchEvent.h
@@ -134,24 +134,24 @@ class nsDOMTouchList MOZ_FINAL : public 
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMTouchList)
   NS_DECL_NSIDOMTOUCHLIST
 
   nsDOMTouchList(nsISupports *aParent) : mParent(aParent)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
   }
   nsDOMTouchList(nsISupports *aParent,
                  nsTArray<nsCOMPtr<nsIDOMTouch> > &aTouches)
    : mPoints(aTouches),
      mParent(aParent)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
   }
 
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap)
   {
     return mozilla::dom::binding::TouchList::create(cx, scope, this,
                                                     triedToWrap);
   }
--- a/content/events/src/nsPaintRequest.h
+++ b/content/events/src/nsPaintRequest.h
@@ -63,17 +63,17 @@ private:
 };
 
 class nsPaintRequestList MOZ_FINAL : public nsIDOMPaintRequestList,
                                      public nsWrapperCache
 {
 public:
   nsPaintRequestList(nsIDOMEvent *aParent) : mParent(aParent)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsPaintRequestList)
   NS_DECL_NSIDOMPAINTREQUESTLIST
   
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
                                bool *triedToWrap)
--- a/content/html/content/src/nsClientRect.h
+++ b/content/html/content/src/nsClientRect.h
@@ -67,17 +67,17 @@ protected:
 };
 
 class nsClientRectList MOZ_FINAL : public nsIDOMClientRectList,
                                    public nsWrapperCache
 {
 public:
   nsClientRectList(nsISupports *aParent) : mParent(aParent)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
   }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsClientRectList)
 
   NS_DECL_NSIDOMCLIENTRECTLIST
   
   virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -2159,18 +2159,17 @@ nsHTMLFormElement::IntrinsicState() cons
 // a lightweight collection implementation somewhere
 
 nsFormControlList::nsFormControlList(nsHTMLFormElement* aForm) :
   mForm(aForm),
   // Initialize the elements list to have an initial capacity
   // of 8 to reduce allocations on small forms.
   mElements(8)
 {
-  // Mark ourselves as a proxy
-  SetIsProxy();
+  SetIsDOMBinding();
 }
 
 nsFormControlList::~nsFormControlList()
 {
   mForm = nsnull;
   Clear();
 }
 
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -1961,17 +1961,17 @@ nsHTMLSelectElement::VerifyOptionsArray(
 
 //----------------------------------------------------------------------
 //
 // nsHTMLOptionCollection implementation
 //
 
 nsHTMLOptionCollection::nsHTMLOptionCollection(nsHTMLSelectElement* aSelect)
 {
-  SetIsProxy();
+  SetIsDOMBinding();
 
   // Do not maintain a reference counted reference. When
   // the select goes away, it will let us know.
   mSelect = aSelect;
 }
 
 nsHTMLOptionCollection::~nsHTMLOptionCollection()
 {
--- a/content/html/content/src/nsHTMLTableElement.cpp
+++ b/content/html/content/src/nsHTMLTableElement.cpp
@@ -102,18 +102,17 @@ protected:
 TableRowsCollection::TableRowsCollection(nsHTMLTableElement *aParent)
   : mParent(aParent)
   , mOrphanRows(new nsContentList(mParent,
                                   kNameSpaceID_XHTML,
                                   nsGkAtoms::tr,
                                   nsGkAtoms::tr,
                                   false))
 {
-  // Mark ourselves as a proxy
-  SetIsProxy();
+  SetIsDOMBinding();
 }
 
 TableRowsCollection::~TableRowsCollection()
 {
   // we do NOT have a ref-counted reference to mParent, so do NOT
   // release it!  this is to avoid circular references.  The
   // instantiator who provided mParent is responsible for managing our
   // reference for us.
--- a/content/svg/content/src/DOMSVGLengthList.h
+++ b/content/svg/content/src/DOMSVGLengthList.h
@@ -78,17 +78,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGLengthList)
   NS_DECL_NSIDOMSVGLENGTHLIST
 
   DOMSVGLengthList(DOMSVGAnimatedLengthList *aAList,
                    const SVGLengthList &aInternalList)
     : mAList(aAList)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
 
     // aInternalList must be passed in explicitly because we can't use
     // InternalList() here. (Because it depends on IsAnimValList, which depends
     // on this object having been assigned to aAList's mBaseVal or mAnimVal,
     // which hasn't happend yet.)
     
     InternalListLengthWillChange(aInternalList.Length()); // Sync mItems
   }
--- a/content/svg/content/src/DOMSVGNumberList.h
+++ b/content/svg/content/src/DOMSVGNumberList.h
@@ -77,17 +77,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGNumberList)
   NS_DECL_NSIDOMSVGNUMBERLIST
 
   DOMSVGNumberList(DOMSVGAnimatedNumberList *aAList,
                    const SVGNumberList &aInternalList)
     : mAList(aAList)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
 
     // aInternalList must be passed in explicitly because we can't use
     // InternalList() here. (Because it depends on IsAnimValList, which depends
     // on this object having been assigned to aAList's mBaseVal or mAnimVal,
     // which hasn't happend yet.)
 
     InternalListLengthWillChange(aInternalList.Length()); // Sync mItems
   }
--- a/content/svg/content/src/DOMSVGPathSegList.h
+++ b/content/svg/content/src/DOMSVGPathSegList.h
@@ -166,17 +166,17 @@ private:
   /**
    * Only our static GetDOMWrapper() factory method may create objects of our
    * type.
    */
   DOMSVGPathSegList(nsSVGElement *aElement, bool aIsAnimValList)
     : mElement(aElement)
     , mIsAnimValList(aIsAnimValList)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
 
     InternalListWillChangeTo(InternalList()); // Sync mItems
   }
 
   ~DOMSVGPathSegList();
 
   nsSVGElement* Element() {
     return mElement.get();
--- a/content/svg/content/src/DOMSVGPointList.h
+++ b/content/svg/content/src/DOMSVGPointList.h
@@ -166,17 +166,17 @@ private:
   /**
    * Only our static GetDOMWrapper() factory method may create objects of our
    * type.
    */
   DOMSVGPointList(nsSVGElement *aElement, bool aIsAnimValList)
     : mElement(aElement)
     , mIsAnimValList(aIsAnimValList)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
 
     InternalListWillChangeTo(InternalList()); // Sync mItems
   }
 
   ~DOMSVGPointList();
 
   nsSVGElement* Element() {
     return mElement.get();
--- a/content/svg/content/src/DOMSVGTransformList.h
+++ b/content/svg/content/src/DOMSVGTransformList.h
@@ -73,17 +73,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGTransformList)
   NS_DECL_NSIDOMSVGTRANSFORMLIST
 
   DOMSVGTransformList(DOMSVGAnimatedTransformList *aAList,
                       const SVGTransformList &aInternalList)
     : mAList(aAList)
   {
-    SetIsProxy();
+    SetIsDOMBinding();
 
     // aInternalList must be passed in explicitly because we can't use
     // InternalList() here. (Because it depends on IsAnimValList, which depends
     // on this object having been assigned to aAList's mBaseVal or mAnimVal,
     // which hasn't happend yet.)
 
     InternalListLengthWillChange(aInternalList.Length()); // Sync mItems
   }
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -131,17 +131,17 @@ nsAnonymousContentList::nsAnonymousConte
                                                nsInsertionPointList* aElements)
   : mContent(aContent),
     mElements(aElements)
 {
   MOZ_COUNT_CTOR(nsAnonymousContentList);
 
   // We don't reference count our Anonymous reference (to avoid circular
   // references). We'll be told when the Anonymous goes away.
-  SetIsProxy();
+  SetIsDOMBinding();
 }
 
 nsAnonymousContentList::~nsAnonymousContentList()
 {
   MOZ_COUNT_DTOR(nsAnonymousContentList);
   delete mElements;
 }
 
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -59,17 +59,17 @@ EXTRA_PP_COMPONENTS = \
 EXTRA_JS_MODULES = ConsoleAPIStorage.jsm \
 		$(NULL)
 
 EXTRA_COMPONENTS = \
 	        Webapps.js \
 	        Webapps.manifest \
 		$(NULL)
 
-EXTRA_JS_MODULES += Webapps.jsm \
+EXTRA_PP_JS_MODULES += Webapps.jsm \
 		$(NULL)
 
 EXTRA_JS_MODULES += DOMRequestHelper.jsm \
 		$(NULL)
 
 XPIDLSRCS = \
   nsIDOMDOMError.idl \
   nsIDOMDOMRequest.idl \
--- a/dom/base/Webapps.jsm
+++ b/dom/base/Webapps.jsm
@@ -16,33 +16,39 @@ XPCOMUtils.defineLazyGetter(this, "NetUt
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
   return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
 });
 
+#ifdef MOZ_WIDGET_GONK
+  const DIRECTORY_NAME = "webappsDir";
+#else
+  const DIRECTORY_NAME = "ProfD";
+#endif
+
 let DOMApplicationRegistry = {
   appsFile: null,
   webapps: { },
 
   init: function() {
     this.messages = ["Webapps:Install", "Webapps:Uninstall",
                     "Webapps:GetSelf", "Webapps:GetInstalled",
                     "Webapps:Launch", "Webapps:GetAll"];
 
     this.messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
 
-    let appsDir = FileUtils.getDir("ProfD", ["webapps"], true, true);
-    this.appsFile = FileUtils.getFile("ProfD", ["webapps", "webapps.json"], true);
+    let appsDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps"], true, true);
+    this.appsFile = FileUtils.getFile(DIRECTORY_NAME, ["webapps", "webapps.json"], true);
 
     if (!this.appsFile.exists())
       return;
 
     this._loadJSONAsync(this.appsFile, (function(aData) { this.webapps = aData; }).bind(this));
 
     try {
       let hosts = Services.prefs.getCharPref("dom.mozApps.whitelist")
@@ -157,28 +163,28 @@ let DOMApplicationRegistry = {
   },
 
   confirmInstall: function(aData, aFromSync) {
     let app = aData.app;
     let id = app.syncId || this._appId(app.origin);
 
     // install an application again is considered as an update
     if (id) {
-      let dir = FileUtils.getDir("ProfD", ["webapps", id], true, true);
+      let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
       try {
         dir.remove(true);
       } catch(e) {
       }
     }
     else {
       let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
       id = uuidGenerator.generateUUID().toString();
     }
 
-    let dir = FileUtils.getDir("ProfD", ["webapps", id], true, true);
+    let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
 
     let manFile = dir.clone();
     manFile.append("manifest.json");
     this._writeFile(manFile, JSON.stringify(app.manifest));
 
     this.webapps[id] = this._cloneAppObject(app);
     delete this.webapps[id].manifest;
     this.webapps[id].installTime = (new Date()).getTime()
@@ -212,34 +218,34 @@ let DOMApplicationRegistry = {
   _readManifests: function(aData, aFinalCallback, aIndex) {
     if (!aData.length) {
       aFinalCallback(aData);
       return;
     }
 
     let index = aIndex || 0;
     let id = aData[index].id;
-    let file = FileUtils.getFile("ProfD", ["webapps", id, "manifest.json"], true);
+    let file = FileUtils.getFile(DIRECTORY_NAME, ["webapps", id, "manifest.json"], true);
     this._loadJSONAsync(file, (function(aJSON) {
       aData[index].manifest = aJSON;
       if (index == aData.length - 1)
         aFinalCallback(aData);
       else
         this._readManifests(aData, aFinalCallback, index + 1);
     }).bind(this)); 
   },
 
   uninstall: function(aData) {
     let found = false;
     for (let id in this.webapps) {
       let app = this.webapps[id];
       if (app.origin == aData.origin) {
         found = true;
         delete this.webapps[id];
-        let dir = FileUtils.getDir("ProfD", ["webapps", id], true, true);
+        let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
         try {
           dir.remove(true);
         } catch (e) {
         }
         this._saveApps((function() {
           ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
           Services.obs.notifyObservers(this, "webapps-sync-uninstall", id);
         }).bind(this));
@@ -335,17 +341,17 @@ let DOMApplicationRegistry = {
   updateApps: function(aRecords, aCallback) {
     for (let i = 0; i < aRecords.length; i++) {
       let record = aRecords[i];
       if (record.deleted) {
         if (!this.webapps[record.id])
           continue;
         let origin = this.webapps[record.id].origin;
         delete this.webapps[record.id];
-        let dir = FileUtils.getDir("ProfD", ["webapps", record.id], true, true);
+        let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", record.id], true, true);
         try {
           dir.remove(true);
         } catch (e) {
         }
         ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", { origin: origin });
       } else {
         if (!!this.webapps[record.id]) {
           this.webapps[record.id] = record.value;
@@ -373,17 +379,17 @@ let DOMApplicationRegistry = {
     }
     return apps;
   },
 
   wipe: function(aCallback) {
     let ids = this.getAllIDs();
     for (let id in ids) {
       delete this.webapps[id];
-      let dir = FileUtils.getDir("ProfD", ["webapps", id], true, true);
+      let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
       try {
         dir.remove(true);
       } catch (e) {
       }
     }
     this._saveApps(aCallback);
    }
 };
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -72,16 +72,17 @@
 
 // Helper Classes
 #include "nsXPIDLString.h"
 #include "nsJSUtils.h"
 #include "prmem.h"
 #include "jsapi.h"              // for JSAutoRequest
 #include "jsdbgapi.h"           // for JS_ClearWatchPointsForObject
 #include "jsfriendapi.h"        // for JS_GetGlobalForFrame
+#include "jswrapper.h"
 #include "nsReadableUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsJSEnvironment.h"
 #include "nsCharSeparatedTokenizer.h" // for Accept-Language parsing
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
 
 // Other Classes
@@ -794,16 +795,31 @@ nsPIDOMWindow::nsPIDOMWindow(nsPIDOMWind
  {}
 
 nsPIDOMWindow::~nsPIDOMWindow() {}
 
 //*****************************************************************************
 // nsOuterWindowProxy: Outer Window Proxy
 //*****************************************************************************
 
+class nsOuterWindowProxy : public js::Wrapper
+{
+public:
+  nsOuterWindowProxy() : js::Wrapper(0) {}
+
+  virtual bool isOuterWindow() {
+    return true;
+  }
+  JSString *obj_toString(JSContext *cx, JSObject *wrapper);
+  void finalize(JSContext *cx, JSObject *proxy);
+
+  static nsOuterWindowProxy singleton;
+};
+
+
 JSString *
 nsOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy)
 {
     JS_ASSERT(js::IsProxy(proxy));
 
     return JS_NewStringCopyZ(cx, "[object Window]");
 }
 
@@ -817,18 +833,18 @@ nsOuterWindowProxy::finalize(JSContext *
     CallQueryInterface(global, &cache);
     cache->ClearWrapper();
   }
 }
 
 nsOuterWindowProxy
 nsOuterWindowProxy::singleton;
 
-JSObject *
-NS_NewOuterWindowProxy(JSContext *cx, JSObject *parent)
+static JSObject*
+NewOuterWindowProxy(JSContext *cx, JSObject *parent)
 {
   JSAutoEnterCompartment ac;
   if (!ac.enter(cx, parent)) {
     return nsnull;
   }
 
   JSObject *obj = js::Wrapper::New(cx, parent, js::GetObjectProto(parent), parent,
                                    &nsOuterWindowProxy::singleton);
@@ -915,17 +931,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     }
   } else {
     // |this| is an outer window. Outer windows start out frozen and
     // remain frozen until they get an inner window, so freeze this
     // outer window here.
     Freeze();
 
     mObserver = nsnull;
-    SetIsProxy();
+    SetIsDOMBinding();
   }
 
   // We could have failed the first time through trying
   // to create the entropy collector, so we should
   // try to get one until we succeed.
 
   gRefCnt++;
 
@@ -1872,16 +1888,56 @@ ReparentWaiverWrappers(JSDHashTable *tab
         JS_SetParent(closure->mCx, value, closure->mNewInner);
     } else {
       JS_ClearPendingException(closure->mCx);
     }
     return JS_DHASH_NEXT;
 }
 
 nsresult
+nsGlobalWindow::CreateOuterObject(nsGlobalWindow* aNewInner)
+{
+  mContext->SetGlobalObject(this);
+
+  JSContext* cx = mContext->GetNativeContext();
+
+  if (IsChromeWindow()) {
+    // Always enable E4X for XUL and other chrome content -- there is no
+    // need to preserve the <!-- script hiding hack from JS-in-HTML daze
+    // (introduced in 1995 for graceful script degradation in Netscape 1,
+    // Mosaic, and other pre-JS browsers).
+    JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_XML);
+  }
+
+  JSObject* outer = NewOuterWindowProxy(cx, aNewInner->FastGetGlobalJSObject());
+  if (!outer) {
+    return NS_ERROR_FAILURE;
+  }
+
+  js::SetProxyExtra(outer, 0,
+    js::PrivateValue(static_cast<nsIScriptGlobalObject*>(this)));
+
+  return SetOuterObject(cx, outer);
+}
+
+nsresult
+nsGlobalWindow::SetOuterObject(JSContext* aCx, JSObject* aOuterObject)
+{
+  // Force our context's global object to be the outer.
+  // NB: JS_SetGlobalObject sets aCx->compartment.
+  JS_SetGlobalObject(aCx, aOuterObject);
+
+  // Set up the prototype for the outer object.
+  JSObject* inner = JS_GetParent(aOuterObject);
+  JS_SetPrototype(aCx, aOuterObject, JS_GetPrototype(inner));
+
+  return NS_OK;
+}
+
+nsresult
 nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
                                nsISupports* aState,
                                bool aForceReuseInnerWindow)
 {
   NS_TIME_FUNCTION;
 
   NS_PRECONDITION(mDocumentPrincipal == nsnull,
                   "mDocumentPrincipal prematurely set!");
@@ -2086,24 +2142,23 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       if (!currentInner->IsFrozen()) {
         currentInner->FreeInnerObjects();
       }
     }
 
     mInnerWindow = newInnerWindow;
 
     if (!mJSObject) {
-      mContext->CreateOuterObject(this, newInnerWindow);
+      CreateOuterObject(newInnerWindow);
       mContext->DidInitializeContext();
 
       mJSObject = mContext->GetNativeGlobal();
       SetWrapper(mJSObject);
     } else {
-      JSObject *outerObject =
-        NS_NewOuterWindowProxy(cx, newInnerWindow->mJSObject);
+      JSObject *outerObject = NewOuterWindowProxy(cx, newInnerWindow->mJSObject);
       if (!outerObject) {
         NS_ERROR("out of memory");
         return NS_ERROR_FAILURE;
       }
 
       js::SetProxyExtra(mJSObject, 0, js::PrivateValue(NULL));
 
       outerObject = JS_TransplantObject(cx, mJSObject, outerObject);
@@ -2122,17 +2177,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, mJSObject)) {
           NS_ERROR("unable to enter a compartment");
           return NS_ERROR_FAILURE;
         }
 
         JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject);
 
-        mContext->SetOuterObject(mJSObject);
+        SetOuterObject(cx, mJSObject);
 
         JSCompartment *compartment = js::GetObjectCompartment(mJSObject);
         xpc::CompartmentPrivate *priv =
           static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment));
         if (priv && priv->waiverWrapperMap) {
           NS_ASSERTION(!JS_IsExceptionPending(cx),
                        "We might overwrite a pending exception!");
           ReparentWaiverClosure closure = {
@@ -2195,20 +2250,31 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   }
 
   if (!aState && !reUseInnerWindow) {
     // Loading a new page and creating a new inner window, *not*
     // restoring from session history.
 
     // Now that both the the inner and outer windows are initialized
     // let the script context do its magic to hook them together.
-    mContext->ConnectToInner(newInnerWindow, mJSObject);
+#ifdef DEBUG
+    JSObject* newInnerJSObject = newInnerWindow->FastGetGlobalJSObject();
+#endif
+
+    // Now that we're connecting the outer global to the inner one,
+    // we must have transplanted it. The JS engine tries to maintain
+    // the global object's compartment as its default compartment,
+    // so update that now since it might have changed.
+    JS_SetGlobalObject(cx, mJSObject);
+    NS_ASSERTION(JS_GetPrototype(mJSObject) ==
+                 JS_GetPrototype(newInnerJSObject),
+                 "outer and inner globals should have the same prototype");
 
     nsCOMPtr<nsIContent> frame = do_QueryInterface(GetFrameElementInternal());
-    if (frame && frame->OwnerDoc()) {
+    if (frame) {
       nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow();
       if (parentWindow && parentWindow->TimeoutSuspendCount()) {
         SuspendTimeouts(parentWindow->TimeoutSuspendCount());
       }
     }
   }
 
   // Add an extra ref in case we release mContext during GC.
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -97,17 +97,16 @@
 #include "nsIIDBFactory.h"
 #include "nsFrameMessageManager.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIDOMTouchEvent.h"
 #include "nsIInlineEventHandlers.h"
 
 // JS includes
 #include "jsapi.h"
-#include "jswrapper.h"
 
 #define DEFAULT_HOME_PAGE "www.mozilla.org"
 #define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
 
 // Amount of time allowed between alert/prompt/confirm before enabling
 // the stop dialog checkbox.
 #define SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec
 
@@ -227,36 +226,16 @@ struct nsTimeout : PRCList
   nsCOMPtr<nsIScriptTimeoutHandler> mScriptHandler;
 
 private:
   // reference count for shared usage
   nsAutoRefCnt mRefCnt;
 };
 
 //*****************************************************************************
-// nsOuterWindow: Outer Window Proxy
-//*****************************************************************************
-
-class nsOuterWindowProxy : public js::Wrapper
-{
-public:
-  nsOuterWindowProxy() : js::Wrapper((unsigned)0) {}
-
-  virtual bool isOuterWindow() {
-    return true;
-  }
-  JSString *obj_toString(JSContext *cx, JSObject *wrapper);
-  void finalize(JSContext *cx, JSObject *proxy);
-
-  static nsOuterWindowProxy singleton;
-};
-
-JSObject *NS_NewOuterWindowProxy(JSContext *cx, JSObject *parent);
-
-//*****************************************************************************
 // nsGlobalWindow: Global Object for Scripting
 //*****************************************************************************
 // Beware that all scriptable interfaces implemented by
 // nsGlobalWindow will be reachable from JS, if you make this class
 // implement new interfaces you better know what you're
 // doing. Security wise this is very sensitive code. --
 // jst@netscape.com
 
@@ -838,16 +817,19 @@ protected:
   void ClearStatus();
 
   virtual void UpdateParentTarget();
 
   bool GetIsTabModalPromptAllowed();
 
   inline PRInt32 DOMMinTimeoutValue() const;
 
+  nsresult CreateOuterObject(nsGlobalWindow* aNewInner);
+  nsresult SetOuterObject(JSContext* aCx, JSObject* aOuterObject);
+
   // When adding new member variables, be careful not to create cycles
   // through JavaScript.  If there is any chance that a member variable
   // could own objects that are implemented in JavaScript, then those
   // objects will keep the global object (this object) alive.  To prevent
   // these cycles, ownership of such members must be released in
   // |CleanUp| and |SetDocShell|.
 
   // This member is also used on both inner and outer windows, but
--- a/dom/base/nsIScriptContext.h
+++ b/dom/base/nsIScriptContext.h
@@ -90,16 +90,18 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptC
 class nsIScriptContext : public nsIScriptContextPrincipal
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTCONTEXT_IID)
 
   /* Get the ID of this language. */
   virtual PRUint32 GetScriptTypeID() = 0;
 
+  virtual void SetGlobalObject(nsIScriptGlobalObject* aGlobalObject) = 0;
+
   /**
    * Compile and execute a script.
    *
    * @param aScript a string representing the script to be executed
    * @param aScopeObject a script object for the scope to execute in, or
    *                     nsnull to use a default scope
    * @param aPrincipal the principal the script should be evaluated with
    * @param aOriginPrincipal the principal the script originates from.  If null,
@@ -299,43 +301,21 @@ public:
   virtual nsresult CreateNativeGlobalForInner(
                                       nsIScriptGlobalObject *aNewInner,
                                       bool aIsChrome,
                                       nsIPrincipal *aPrincipal,
                                       JSObject** aNativeGlobal,
                                       nsISupports **aHolder) = 0;
 
   /**
-   * Connect this context to a new inner window, to allow "prototype"
-   * chaining from the inner to the outer.
-   * Called after both the the inner and outer windows are initialized
-   **/
-  virtual nsresult ConnectToInner(nsIScriptGlobalObject *aNewInner,
-                                  JSObject *aOuterGlobal) = 0;
-
-
-  /**
    * Initialize the context generally. Does not create a global object.
    **/
   virtual nsresult InitContext() = 0;
 
   /**
-   * Creates the outer window for this context.
-   *
-   * @param aGlobalObject The script global object to use as our global.
-   */
-  virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
-                                     nsIScriptGlobalObject *aCurrentInner) = 0;
-
-  /**
-   * Given an outer object, updates this context with that outer object.
-   */
-  virtual nsresult SetOuterObject(JSObject* aOuterObject) = 0;
-
-  /**
    * Prepares this context for use with the current inner window for the
    * context's global object. This must be called after CreateOuterObject.
    */
   virtual nsresult InitOuterWindow() = 0;
 
   /**
    * Check to see if context is as yet intialized. Used to prevent
    * reentrancy issues during the initialization process.
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2060,36 +2060,16 @@ nsJSContext::CreateNativeGlobalForInner(
   if (NS_FAILED(rv)) {
     return rv;
   }
   jsholder->GetJSObject(aNativeGlobal);
   jsholder.forget(aHolder);
   return NS_OK;
 }
 
-nsresult
-nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, JSObject *aOuterGlobal)
-{
-  NS_ENSURE_ARG(aNewInner);
-#ifdef DEBUG
-  JSObject *newInnerJSObject = aNewInner->GetGlobalJSObject();
-#endif
-
-  // Now that we're connecting the outer global to the inner one,
-  // we must have transplanted it. The JS engine tries to maintain
-  // the global object's compartment as its default compartment,
-  // so update that now since it might have changed.
-  JS_SetGlobalObject(mContext, aOuterGlobal);
-  NS_ASSERTION(JS_GetPrototype(aOuterGlobal) ==
-               JS_GetPrototype(newInnerJSObject),
-               "outer and inner globals should have the same prototype");
-
-  return NS_OK;
-}
-
 JSContext*
 nsJSContext::GetNativeContext()
 {
   return mContext;
 }
 
 nsresult
 nsJSContext::InitContext()
@@ -2104,57 +2084,16 @@ nsJSContext::InitContext()
   ::JS_SetErrorReporter(mContext, NS_ScriptErrorReporter);
 
   JSOptionChangedCallback(js_options_dot_str, this);
 
   return NS_OK;
 }
 
 nsresult
-nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
-                               nsIScriptGlobalObject *aCurrentInner)
-{
-  mGlobalObjectRef = aGlobalObject;
-
-  nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aGlobalObject));
-
-  if (chromeWindow) {
-    // Always enable E4X for XUL and other chrome content -- there is no
-    // need to preserve the <!-- script hiding hack from JS-in-HTML daze
-    // (introduced in 1995 for graceful script degradation in Netscape 1,
-    // Mosaic, and other pre-JS browsers).
-    JS_SetOptions(mContext, JS_GetOptions(mContext) | JSOPTION_XML);
-  }
-
-  JSObject *outer =
-    NS_NewOuterWindowProxy(mContext, aCurrentInner->GetGlobalJSObject());
-  if (!outer) {
-    return NS_ERROR_FAILURE;
-  }
-
-  js::SetProxyExtra(outer, 0, js::PrivateValue(aGlobalObject));
-
-  return SetOuterObject(outer);
-}
-
-nsresult
-nsJSContext::SetOuterObject(JSObject* aOuterObject)
-{
-  // Force our context's global object to be the outer.
-  // NB: JS_SetGlobalObject sets mContext->compartment.
-  JS_SetGlobalObject(mContext, aOuterObject);
-
-  // Set up the prototype for the outer object.
-  JSObject *inner = JS_GetParent(aOuterObject);
-  JS_SetPrototype(mContext, aOuterObject, JS_GetPrototype(inner));
-
-  return NS_OK;
-}
-
-nsresult
 nsJSContext::InitOuterWindow()
 {
   JSObject *global = JS_ObjectToInnerObject(mContext, JS_GetGlobalObject(mContext));
 
   nsresult rv = InitClasses(global); // this will complete global object initialization
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -34,16 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #ifndef nsJSEnvironment_h
 #define nsJSEnvironment_h
 
 #include "nsIScriptContext.h"
 #include "nsIScriptRuntime.h"
+#include "nsIScriptGlobalObject.h"
 #include "nsCOMPtr.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "nsIObserver.h"
 #include "nsIXPCScriptNotify.h"
 #include "prtime.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIXPConnect.h"
@@ -71,16 +72,21 @@ public:
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSContext,
                                                          nsIScriptContext)
 
   virtual nsIScriptObjectPrincipal* GetObjectPrincipal();
 
   virtual PRUint32 GetScriptTypeID()
     { return nsIProgrammingLanguage::JAVASCRIPT; }
 
+  virtual void SetGlobalObject(nsIScriptGlobalObject* aGlobalObject)
+  {
+    mGlobalObjectRef = aGlobalObject;
+  }
+
   virtual nsresult EvaluateString(const nsAString& aScript,
                                   JSObject* aScopeObject,
                                   nsIPrincipal *principal,
                                   nsIPrincipal *originPrincipal,
                                   const char *aURL,
                                   PRUint32 aLineNo,
                                   PRUint32 aVersion,
                                   nsAString *aRetValue,
@@ -135,22 +141,17 @@ public:
   virtual JSContext* GetNativeContext();
   virtual JSObject* GetNativeGlobal();
   virtual nsresult CreateNativeGlobalForInner(
                                       nsIScriptGlobalObject *aGlobal,
                                       bool aIsChrome,
                                       nsIPrincipal *aPrincipal,
                                       JSObject** aNativeGlobal,
                                       nsISupports **aHolder);
-  virtual nsresult ConnectToInner(nsIScriptGlobalObject *aNewInner,
-                                  JSObject *aOuterGlobal);
   virtual nsresult InitContext();
-  virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject,
-                                     nsIScriptGlobalObject *aCurrentInner);
-  virtual nsresult SetOuterObject(JSObject* aOuterObject);
   virtual nsresult InitOuterWindow();
   virtual bool IsContextInitialized();
 
   virtual void ScriptEvaluated(bool aTerminated);
   virtual void SetTerminationFunction(nsScriptTerminationFunc aFunc,
                                       nsIDOMWindow* aRef);
   virtual bool GetScriptsEnabled();
   virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -63,20 +63,20 @@ typedef PRUptrdiff PtrBits;
  *
  * The cache can store objects other than wrappers. We allow wrappers to use a
  * separate JSObject to store their state (mostly expandos). If the wrapper is
  * collected and we want to preserve this state we actually store the state
  * object in the cache.
  *
  * The cache can store 2 types of objects:
  *
- *  If WRAPPER_IS_PROXY is not set (IsProxy() returns false):
+ *  If WRAPPER_IS_DOM_BINDING is not set (IsDOMBinding() returns false):
  *    - a slim wrapper or the JSObject of an XPCWrappedNative wrapper
  *
- *  If WRAPPER_IS_PROXY is set (IsProxy() returns true):
+ *  If WRAPPER_IS_DOM_BINDING is set (IsDOMBinding() returns true):
  *    - a DOM binding object (proxy)
  *
  * The finalizer for the wrapper clears the cache.
  *
  * A number of the methods are implemented in nsWrapperCacheInlines.h because we
  * have to include some JS headers that don't play nicely with the rest of the
  * codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
  */
@@ -136,32 +136,32 @@ public:
     SetWrapperBits(NULL);
   }
 
   bool PreservingWrapper()
   {
     return (mWrapperPtrBits & WRAPPER_BIT_PRESERVED) != 0;
   }
 
-  void SetIsProxy()
+  void SetIsDOMBinding()
   {
     NS_ASSERTION(!mWrapperPtrBits,
                  "This flag should be set before creating any wrappers.");
-    mWrapperPtrBits = WRAPPER_IS_PROXY;
+    mWrapperPtrBits = WRAPPER_IS_DOM_BINDING;
   }
-  void ClearIsProxy()
+  void ClearIsDOMBinding()
   {
-    NS_ASSERTION(!mWrapperPtrBits || mWrapperPtrBits == WRAPPER_IS_PROXY,
+    NS_ASSERTION(!mWrapperPtrBits || mWrapperPtrBits == WRAPPER_IS_DOM_BINDING,
                  "This flag should be cleared before creating any wrappers.");
     mWrapperPtrBits = 0;
   }
 
-  bool IsProxy() const
+  bool IsDOMBinding() const
   {
-    return (mWrapperPtrBits & WRAPPER_IS_PROXY) != 0;
+    return (mWrapperPtrBits & WRAPPER_IS_DOM_BINDING) != 0;
   }
 
 
   /**
    * Wrap the object corresponding to this wrapper cache. If non-null is
    * returned, the object has already been stored in the wrapper cache and the
    * value set in triedToWrap is meaningless. If null is returned then
    * triedToWrap indicates whether an error occurred, if it's false then the
@@ -194,41 +194,39 @@ public:
 private:
   JSObject *GetJSObjectFromBits() const
   {
     return reinterpret_cast<JSObject*>(mWrapperPtrBits & ~kWrapperBitMask);
   }
   void SetWrapperBits(void *aWrapper)
   {
     mWrapperPtrBits = reinterpret_cast<PtrBits>(aWrapper) |
-                      (mWrapperPtrBits & WRAPPER_IS_PROXY);
+                      (mWrapperPtrBits & WRAPPER_IS_DOM_BINDING);
   }
 
   /**
    * If this bit is set then we're preserving the wrapper, which in effect ties
    * the lifetime of the JS object stored in the cache to the lifetime of the
    * native object. We rely on the cycle collector to break the cycle that this
    * causes between the native object and the JS object, so it is important that
    * any native object that supports preserving of its wrapper
    * traces/traverses/unlinks the cached JS object (see
    * NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER,
    * NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS and
    * NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER).
    */
   enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
 
   /**
-   * If this bit is set then the wrapper for the native object is a proxy. Note
-   * that that doesn't necessarily mean that the JS object stored in the cache
-   * is a JS proxy, as we sometimes store objects other than the wrapper in the
-   * cache.
+   * If this bit is set then the wrapper for the native object is a DOM binding
+   * (proxy).
    */
-  enum { WRAPPER_IS_PROXY = 1 << 1 };
+  enum { WRAPPER_IS_DOM_BINDING = 1 << 1 };
 
-  enum { kWrapperBitMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_PROXY) };
+  enum { kWrapperBitMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING) };
 
   PtrBits mWrapperPtrBits;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
 
 #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY                                   \
   if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -539,33 +539,44 @@ nsEditor::GetIsSelectionEditable(bool *a
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
 {
   NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
-  nsCOMPtr<nsIDOMDocument> doc;
-  GetDocument(getter_AddRefs(doc));
-  *aIsDocumentEditable = doc ? true : false;
+  nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
+  *aIsDocumentEditable = !!doc;
 
   return NS_OK;
 }
 
+already_AddRefed<nsIDocument>
+nsEditor::GetDocument()
+{
+  NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
+  nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
+  return doc.forget();
+}
+
+already_AddRefed<nsIDOMDocument>
+nsEditor::GetDOMDocument()
+{
+  NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
+  nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
+  return doc.forget();
+}
+
 NS_IMETHODIMP 
 nsEditor::GetDocument(nsIDOMDocument **aDoc)
 {
-  NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
-  *aDoc = nsnull; // init out param
-  NS_PRECONDITION(mDocWeak, "bad state, mDocWeak weak pointer not initialized");
-  nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(mDocWeak);
-  NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
-  NS_ADDREF(*aDoc = doc);
-  return NS_OK;
+  nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
+  doc.forget(aDoc);
+  return *aDoc ? NS_OK : NS_ERROR_NOT_INITIALIZED;
 }
 
 already_AddRefed<nsIPresShell>
 nsEditor::GetPresShell()
 {
   NS_PRECONDITION(mDocWeak, "bad state, null mDocWeak");
   nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocWeak);
   NS_ENSURE_TRUE(doc, NULL);
@@ -4958,20 +4969,17 @@ nsresult nsEditor::ClearSelection()
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
   return selection->RemoveAllRanges();  
 }
 
 nsresult
 nsEditor::CreateHTMLContent(const nsAString& aTag, nsIContent** aContent)
 {
-  nsCOMPtr<nsIDOMDocument> tempDoc;
-  GetDocument(getter_AddRefs(tempDoc));
-
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(tempDoc);
+  nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   // XXX Wallpaper over editor bug (editor tries to create elements with an
   //     empty nodename).
   if (aTag.IsEmpty()) {
     NS_ERROR("Don't pass an empty tag to nsEditor::CreateHTMLContent, "
              "check caller.");
     return NS_ERROR_FAILURE;
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -148,16 +148,18 @@ public:
 
 //Interfaces for addref and release and queryinterface
 //NOTE: Use   NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsEditor
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsEditor,
                                            nsIEditor)
 
   /* ------------ utility methods   -------------- */
+  already_AddRefed<nsIDOMDocument> GetDOMDocument();
+  already_AddRefed<nsIDocument> GetDocument();
   already_AddRefed<nsIPresShell> GetPresShell();
   void NotifyEditorObservers();
 
   /* ------------ nsIEditor methods -------------- */
   NS_DECL_NSIEDITOR
   /* ------------ nsIEditorIMESupport methods -------------- */
   NS_DECL_NSIEDITORIMESUPPORT
   
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -806,22 +806,21 @@ nsEditorEventListener::CanDrop(nsIDOMDra
   nsCOMPtr<nsIDOMNode> sourceNode;
   dataTransfer->GetMozSourceNode(getter_AddRefs(sourceNode));
   if (!sourceNode)
     return true;
 
   // There is a source node, so compare the source documents and this document.
   // Disallow drops on the same document.
 
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  nsresult rv = mEditor->GetDocument(getter_AddRefs(domdoc));
-  NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIDOMDocument> domdoc = mEditor->GetDOMDocument();
+  NS_ENSURE_TRUE(domdoc, false);
 
   nsCOMPtr<nsIDOMDocument> sourceDoc;
-  rv = sourceNode->GetOwnerDocument(getter_AddRefs(sourceDoc));
+  nsresult rv = sourceNode->GetOwnerDocument(getter_AddRefs(sourceDoc));
   NS_ENSURE_SUCCESS(rv, false);
   if (domdoc == sourceDoc)      // source and dest are the same document; disallow drops within the selection
   {
     nsCOMPtr<nsISelection> selection;
     rv = mEditor->GetSelection(getter_AddRefs(selection));
     if (NS_FAILED(rv) || !selection)
       return false;
     
--- a/editor/libeditor/html/nsHTMLAnonymousUtils.cpp
+++ b/editor/libeditor/html/nsHTMLAnonymousUtils.cpp
@@ -143,20 +143,17 @@ nsHTMLEditor::CreateAnonymousElement(con
 {
   NS_ENSURE_ARG_POINTER(aParentNode);
   NS_ENSURE_ARG_POINTER(aReturn);
   *aReturn = nsnull;
 
   nsCOMPtr<nsIContent> parentContent( do_QueryInterface(aParentNode) );
   NS_ENSURE_TRUE(parentContent, NS_OK);
 
-  // Get the document
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  GetDocument(getter_AddRefs(domDoc));
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+  nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
 
   // Get the pres shell
   nsCOMPtr<nsIPresShell> ps = GetPresShell();
   NS_ENSURE_TRUE(ps, NS_ERROR_NOT_INITIALIZED);
 
   // Create a new node through the element factory
   nsCOMPtr<nsIContent> newContent;
@@ -189,29 +186,24 @@ nsHTMLEditor::CreateAnonymousElement(con
     if (NS_FAILED(res)) {
       newContent->UnbindFromTree();
       return res;
     }
   }
 
   nsElementDeletionObserver* observer =
     new nsElementDeletionObserver(newContent, parentContent);
-  if (!observer) {
-    newContent->UnbindFromTree();
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
   NS_ADDREF(observer); // NodeWillBeDestroyed releases.
   parentContent->AddMutationObserver(observer);
   newContent->AddMutationObserver(observer);
 
   // display the element
   ps->RecreateFramesFor(newContent);
 
-  *aReturn = newElement;
-  NS_IF_ADDREF(*aReturn);
+  newElement.forget(aReturn);
   return NS_OK;
 }
 
 // Removes event listener and calls DeleteRefToAnonymousNode.
 void
 nsHTMLEditor::RemoveListenerAndDeleteRef(const nsAString& aEvent,
                                          nsIDOMEventListener* aListener,
                                          bool aUseCapture,
@@ -244,19 +236,17 @@ nsHTMLEditor::DeleteRefToAnonymousNode(n
       // In that case presContext->GetPresShell() returns nsnull.
       // See bug 338129.
       if (aShell && aShell->GetPresContext() &&
           aShell->GetPresContext()->GetPresShell() == aShell) {
         nsCOMPtr<nsIDocumentObserver> docObserver = do_QueryInterface(aShell);
         if (docObserver) {
           // Call BeginUpdate() so that the nsCSSFrameConstructor/PresShell
           // knows we're messing with the frame tree.
-          nsCOMPtr<nsIDOMDocument> domDocument;
-          GetDocument(getter_AddRefs(domDocument));
-          nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
+          nsCOMPtr<nsIDocument> document = GetDocument();
           if (document)
             docObserver->BeginUpdate(document, UPDATE_CONTENT_MODEL);
 
           docObserver->ContentRemoved(content->GetCurrentDoc(),
                                       aParentContent, content, -1,
                                       content->GetPreviousSibling());
           if (document)
             docObserver->EndUpdate(document, UPDATE_CONTENT_MODEL);
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -1200,20 +1200,18 @@ nsHTMLEditor::ParseCFHTML(nsCString & aC
   // we're done!  
   return NS_OK;
 }
 
 bool nsHTMLEditor::IsSafeToInsertData(nsIDOMDocument* aSourceDoc)
 {
   // Try to determine whether we should use a sanitizing fragment sink
   bool isSafe = false;
-  nsCOMPtr<nsIDOMDocument> destdomdoc;
-  GetDocument(getter_AddRefs(destdomdoc));
 
-  nsCOMPtr<nsIDocument> destdoc = do_QueryInterface(destdomdoc);
+  nsCOMPtr<nsIDocument> destdoc = GetDocument();
   NS_ASSERTION(destdoc, "Where is our destination doc?");
   nsCOMPtr<nsISupports> container = destdoc->GetContainer();
   nsCOMPtr<nsIDocShellTreeItem> dsti(do_QueryInterface(container));
   nsCOMPtr<nsIDocShellTreeItem> root;
   if (dsti)
     dsti->GetRootTreeItem(getter_AddRefs(root));
   nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
   PRUint32 appType;
@@ -1585,18 +1583,17 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt3
           textDataObj = do_QueryInterface(infoDataObj);
           textDataObj->GetData(text);
           NS_ASSERTION(text.Length() <= (infoLen/2), "Invalid length!");
           infoStr.Assign(text.get(), infoLen / 2);
         }
       }
 
       // handle transferable hooks
-      nsCOMPtr<nsIDOMDocument> domdoc;
-      GetDocument(getter_AddRefs(domdoc));
+      nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
       if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, trans))
         return NS_OK;
 
       rv = InsertFromTransferable(trans, nsnull, contextStr, infoStr,
                                   nsnull, 0, true);
     }
   }
 
@@ -1604,18 +1601,17 @@ NS_IMETHODIMP nsHTMLEditor::Paste(PRInt3
 }
 
 NS_IMETHODIMP nsHTMLEditor::PasteTransferable(nsITransferable *aTransferable)
 {
   if (!FireClipboardEvent(NS_PASTE))
     return NS_OK;
 
   // handle transferable hooks
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  GetDocument(getter_AddRefs(domdoc));
+  nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
   if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, aTransferable))
     return NS_OK;
 
   nsAutoString contextStr, infoStr;
   return InsertFromTransferable(aTransferable, nsnull, contextStr, infoStr,
                                 nsnull, 0, true);
 }
 
@@ -2244,20 +2240,17 @@ nsresult nsHTMLEditor::CreateDOMFragment
                                                   PRInt32 *outEndOffset,
                                                   bool aTrustedInput)
 {
   NS_ENSURE_TRUE(outFragNode && outStartNode && outEndNode, NS_ERROR_NULL_POINTER);
   nsCOMPtr<nsIDOMDocumentFragment> docfrag;
   nsCOMPtr<nsIDOMNode> contextAsNode, tmp;  
   nsresult res = NS_OK;
 
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  GetDocument(getter_AddRefs(domDoc));
-
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+  nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
   
   // if we have context info, create a fragment for that
   nsCOMPtr<nsIDOMDocumentFragment> contextfrag;
   nsCOMPtr<nsIDOMNode> contextLeaf, junk;
   if (!aContextStr.IsEmpty())
   {
     res = ParseFragment(aContextStr, nsnull, doc, address_of(contextAsNode),
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -342,19 +342,18 @@ nsHTMLEditRules::BeforeEdit(PRInt32 acti
       nsCOMPtr<nsIDOMNode> selNode = selStartNode;
       if (aDirection == nsIEditor::eNext)
         selNode = selEndNode;
       res = CacheInlineStyles(selNode);
       NS_ENSURE_SUCCESS(res, res);
     }
 
     // Stabilize the document against contenteditable count changes
-    nsCOMPtr<nsIDOMDocument> doc;
-    res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
-    NS_ENSURE_SUCCESS(res, res);
+    nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
+    NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
     NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
     if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
       htmlDoc->ChangeContentEditableCount(nsnull, +1);
       mRestoreContentEditableCount = true;
     }
 
     // check that selection is in subtree defined by body node
@@ -380,19 +379,18 @@ nsHTMLEditRules::AfterEdit(PRInt32 actio
     // do all the tricky stuff
     res = AfterEditInner(action, aDirection);
 
     // free up selectionState range item
     (mHTMLEditor->mRangeUpdater).DropRangeItem(&mRangeItem);
 
     // Reset the contenteditable count to its previous value
     if (mRestoreContentEditableCount) {
-      nsCOMPtr<nsIDOMDocument> doc;
-      res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
-      NS_ENSURE_SUCCESS(res, res);
+      nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
+      NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
       NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
       if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
         htmlDoc->ChangeContentEditableCount(nsnull, -1);
       }
       mRestoreContentEditableCount = false;
     }
   }
@@ -1256,20 +1254,18 @@ nsHTMLEditRules::WillInsert(nsISelection
       res = nsEditor::GetNodeLocation(priorNode, address_of(selNode), &selOffset);
       NS_ENSURE_SUCCESS(res, res);
       res = aSelection->Collapse(selNode,selOffset);
       NS_ENSURE_SUCCESS(res, res);
     }
   }
 
   // we need to get the doc
-  nsCOMPtr<nsIDOMDocument>doc;
-  res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
-  NS_ENSURE_SUCCESS(res, res);
-  NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
+  nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
+  NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     
   // for every property that is set, insert a new inline style node
   return CreateStyleForInsertText(aSelection, doc);
 }    
 
 #ifdef XXX_DEAD_CODE
 nsresult
 nsHTMLEditRules::DidInsert(nsISelection *aSelection, nsresult aResult)
@@ -1331,20 +1327,18 @@ nsHTMLEditRules::WillInsertText(PRInt32 
   NS_ENSURE_SUCCESS(res, res);
 
   // dont put text in places that can't have it
   if (!mHTMLEditor->IsTextNode(selNode) &&
       !mHTMLEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text")))
     return NS_ERROR_FAILURE;
 
   // we need to get the doc
-  nsCOMPtr<nsIDOMDocument>doc;
-  res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
-  NS_ENSURE_SUCCESS(res, res);
-  NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
+  nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
+  NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     
   if (aAction == kInsertTextIME) 
   { 
     // Right now the nsWSRunObject code bails on empty strings, but IME needs 
     // the InsertTextImpl() call to still happen since empty strings are meaningful there.
     if (inString->IsEmpty())
     {
       res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -1200,33 +1200,31 @@ nsHTMLEditor::BreakIsVisible(nsIDOMNode 
 }
 
 
 NS_IMETHODIMP
 nsHTMLEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
 {
   NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
 
-  nsCOMPtr<nsIDOMDocument> doc;
-  GetDocument(getter_AddRefs(doc));
-  *aIsDocumentEditable = doc ? IsModifiable() : false;
+  nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
+  *aIsDocumentEditable = doc && IsModifiable();
 
   return NS_OK;
 }
 
 bool nsHTMLEditor::IsModifiable()
 {
   return !IsReadonly();
 }
 
 NS_IMETHODIMP
 nsHTMLEditor::UpdateBaseURL()
 {
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  GetDocument(getter_AddRefs(domDoc));
+  nsCOMPtr<nsIDOMDocument> domDoc = GetDOMDocument();
   NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
 
   // Look for an HTML <base> tag
   nsCOMPtr<nsIDOMNodeList> nodeList;
   nsresult rv = domDoc->GetElementsByTagName(NS_LITERAL_STRING("base"), getter_AddRefs(nodeList));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDOMNode> baseNode;
@@ -3130,21 +3128,17 @@ nsHTMLEditor::GetLinkedObjects(nsISuppor
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_TRUE(*aNodeList, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIContentIterator> iter =
        do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &res);
   NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
   if ((NS_SUCCEEDED(res)))
   {
-    nsCOMPtr<nsIDOMDocument> domdoc;
-    nsEditor::GetDocument(getter_AddRefs(domdoc));
-    NS_ENSURE_TRUE(domdoc, NS_ERROR_UNEXPECTED);
-
-    nsCOMPtr<nsIDocument> doc (do_QueryInterface(domdoc));
+    nsCOMPtr<nsIDocument> doc = GetDocument();
     NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
 
     iter->Init(doc->GetRootElement());
 
     // loop through the content iterator for each content node
     while (!iter->IsDone())
     {
       nsCOMPtr<nsIDOMNode> node (do_QueryInterface(iter->GetCurrentNode()));
@@ -3435,21 +3429,17 @@ nsHTMLEditor::GetEmbeddedObjects(nsISupp
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(*aNodeList, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIContentIterator> iter =
       do_CreateInstance("@mozilla.org/content/post-content-iterator;1", &rv);
   NS_ENSURE_TRUE(iter, NS_ERROR_NULL_POINTER);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  nsEditor::GetDocument(getter_AddRefs(domdoc));
-  NS_ENSURE_TRUE(domdoc, NS_ERROR_UNEXPECTED);
-
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
+  nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
 
   iter->Init(doc->GetRootElement());
 
   // Loop through the content iterator for each content node.
   while (!iter->IsDone()) {
     nsINode* node = iter->GetCurrentNode();
     if (node->IsElement()) {
@@ -4046,42 +4036,16 @@ nsHTMLEditor::GetEnclosingTable(nsIDOMNo
     if (!tmp) break;
     if (nsHTMLEditUtils::IsTable(tmp)) tbl = tmp;
     node = tmp;
   }
   return tbl;
 }
 
 
-#ifdef PRE_NODE_IN_BODY
-nsCOMPtr<nsIDOMElement> nsHTMLEditor::FindPreElement()
-{
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  nsEditor::GetDocument(getter_AddRefs(domdoc));
-  NS_ENSURE_TRUE(domdoc, 0);
-
-  nsCOMPtr<nsIDocument> doc (do_QueryInterface(domdoc));
-  NS_ENSURE_TRUE(doc, 0);
-
-  nsCOMPtr<nsIContent> rootContent = doc->GetRootElement();
-  NS_ENSURE_TRUE(rootContent, 0);
-
-  nsCOMPtr<nsIDOMNode> rootNode (do_QueryInterface(rootContent));
-  NS_ENSURE_TRUE(rootNode, 0);
-
-  nsString prestr ("PRE");  // GetFirstNodeOfType requires capitals
-  nsCOMPtr<nsIDOMNode> preNode;
-  if (NS_FAILED(nsEditor::GetFirstNodeOfType(rootNode, prestr,
-                                             getter_AddRefs(preNode))))
-    return 0;
-
-  return do_QueryInterface(preNode);
-}
-#endif /* PRE_NODE_IN_BODY */
-
 /* this method scans the selection for adjacent text nodes
  * and collapses them into a single text node.
  * "adjacent" means literally adjacent siblings of the same parent.
  * Uses nsEditor::JoinNodes so action is undoable. 
  * Should be called within the context of a batch transaction.
  */
 NS_IMETHODIMP
 nsHTMLEditor::CollapseAdjacentTextNodes(nsIDOMRange *aInRange)
--- a/editor/libeditor/html/nsHTMLObjectResizer.cpp
+++ b/editor/libeditor/html/nsHTMLObjectResizer.cpp
@@ -388,19 +388,17 @@ nsHTMLEditor::ShowResizersInner(nsIDOMEl
   NS_ENSURE_SUCCESS(res, res);
 
   // and then the resizing info tooltip
   res = CreateResizingInfo(getter_AddRefs(mResizingInfo), parentNode);
   NS_ENSURE_SUCCESS(res, res);
 
   // and listen to the "resize" event on the window first, get the
   // window from the document...
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  GetDocument(getter_AddRefs(domDoc));
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+  nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(doc->GetWindow());
   if (!target) { return NS_ERROR_NULL_POINTER; }
 
   mResizeEventListenerP = new DocumentResizeEventListener(this);
   if (!mResizeEventListenerP) { return NS_ERROR_OUT_OF_MEMORY; }
   res = target->AddEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false);
@@ -484,19 +482,17 @@ nsHTMLEditor::HideResizers(void)
   if (target && mMouseMotionListenerP)
   {
     res = target->RemoveEventListener(NS_LITERAL_STRING("mousemove"),
                                       mMouseMotionListenerP, true);
     NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove mouse motion listener");
   }
   mMouseMotionListenerP = nsnull;
 
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  GetDocument(getter_AddRefs(domDoc));
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+  nsCOMPtr<nsIDocument> doc = GetDocument();
   if (!doc) { return NS_ERROR_NULL_POINTER; }
   target = do_QueryInterface(doc->GetWindow());
   if (!target) { return NS_ERROR_NULL_POINTER; }
 
   if (mResizeEventListenerP) {
     res = target->RemoveEventListener(NS_LITERAL_STRING("resize"), mResizeEventListenerP, false);
     NS_ASSERTION(NS_SUCCEEDED(res), "failed to remove resize event listener");
   }
@@ -662,18 +658,17 @@ nsHTMLEditor::SetResizeIncrements(PRInt3
   mWidthIncrementFactor = aW;
   mHeightIncrementFactor = aH;
   mPreserveRatio = aPreserveRatio;
 }
 
 nsresult
 nsHTMLEditor::SetResizingInfoPosition(PRInt32 aX, PRInt32 aY, PRInt32 aW, PRInt32 aH)
 {
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  nsEditor::GetDocument(getter_AddRefs(domdoc));
+  nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
 
   NS_NAMED_LITERAL_STRING(leftStr, "left");
   NS_NAMED_LITERAL_STRING(topStr, "top");
 
   // Determine the position of the resizing info box based upon the new
   // position and size of the element (aX, aY, aW, aH), and which
   // resizer is the "activated handle".  For example, place the resizing
   // info box at the bottom-right corner of the new element, if the element
--- a/editor/libeditor/text/nsPlaintextDataTransfer.cpp
+++ b/editor/libeditor/text/nsPlaintextDataTransfer.cpp
@@ -198,19 +198,18 @@ nsresult nsPlaintextEditor::InsertFromDr
 
   nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(aDropEvent));
   nsDragEvent* dragEventInternal = static_cast<nsDragEvent *>(privateEvent->GetInternalNSEvent());
   if (nsContentUtils::CheckForSubFrameDrop(dragSession, dragEventInternal)) {
     return NS_OK;
   }
 
   // Current doc is destination
-  nsCOMPtr<nsIDOMDocument> destdomdoc; 
-  rv = GetDocument(getter_AddRefs(destdomdoc)); 
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIDOMDocument> destdomdoc = GetDOMDocument();
+  NS_ENSURE_TRUE(destdomdoc, NS_ERROR_NOT_INITIALIZED);
 
   PRUint32 numItems = 0;
   rv = dataTransfer->GetMozItemCount(&numItems);
   NS_ENSURE_SUCCESS(rv, rv);
   if (numItems < 1) return NS_ERROR_FAILURE;  // nothing to drop?
 
   // Combine any deletion and drop insertion into one transaction
   nsAutoEditBatch beginBatching(this);
@@ -361,18 +360,17 @@ NS_IMETHODIMP nsPlaintextEditor::Paste(P
   nsCOMPtr<nsITransferable> trans;
   rv = PrepareTransferable(getter_AddRefs(trans));
   if (NS_SUCCEEDED(rv) && trans)
   {
     // Get the Data from the clipboard  
     if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
     {
       // handle transferable hooks
-      nsCOMPtr<nsIDOMDocument> domdoc;
-      GetDocument(getter_AddRefs(domdoc));
+      nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
       if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, trans))
         return NS_OK;
 
       rv = InsertTextFromTransferable(trans, nsnull, nsnull, true);
     }
   }
 
   return rv;
@@ -382,18 +380,17 @@ NS_IMETHODIMP nsPlaintextEditor::PasteTr
 {
   if (!FireClipboardEvent(NS_PASTE))
     return NS_OK;
 
   if (!IsModifiable())
     return NS_OK;
 
   // handle transferable hooks
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  GetDocument(getter_AddRefs(domdoc));
+  nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
   if (!nsEditorHookUtils::DoInsertionHook(domdoc, nsnull, aTransferable))
     return NS_OK;
 
   return InsertTextFromTransferable(aTransferable, nsnull, nsnull, true);
 }
 
 NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, bool *aCanPaste)
 {
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -230,20 +230,18 @@ nsPlaintextEditor::EndEditorInit()
 
 NS_IMETHODIMP
 nsPlaintextEditor::SetDocumentCharacterSet(const nsACString& characterSet)
 {
   nsresult rv = nsEditor::SetDocumentCharacterSet(characterSet);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Update META charset element.
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  rv = GetDocument(getter_AddRefs(domdoc));
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(domdoc, NS_ERROR_FAILURE);
+  nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
+  NS_ENSURE_TRUE(domdoc, NS_ERROR_NOT_INITIALIZED);
 
   if (UpdateMetaCharset(domdoc, characterSet)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMNodeList> headList;
   rv = domdoc->GetElementsByTagName(NS_LITERAL_STRING("head"), getter_AddRefs(headList));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -278,16 +276,17 @@ nsPlaintextEditor::SetDocumentCharacterS
                        true);
   return NS_OK;
 }
 
 bool
 nsPlaintextEditor::UpdateMetaCharset(nsIDOMDocument* aDocument,
                                      const nsACString& aCharacterSet)
 {
+  MOZ_ASSERT(aDocument);
   // get a list of META tags
   nsCOMPtr<nsIDOMNodeList> metaList;
   nsresult rv = aDocument->GetElementsByTagName(NS_LITERAL_STRING("meta"),
                                                 getter_AddRefs(metaList));
   NS_ENSURE_SUCCESS(rv, false);
   NS_ENSURE_TRUE(metaList, false);
 
   PRUint32 listLength = 0;
@@ -341,19 +340,18 @@ NS_IMETHODIMP nsPlaintextEditor::InitRul
 }
 
 
 NS_IMETHODIMP
 nsPlaintextEditor::GetIsDocumentEditable(bool *aIsDocumentEditable)
 {
   NS_ENSURE_ARG_POINTER(aIsDocumentEditable);
 
-  nsCOMPtr<nsIDOMDocument> doc;
-  GetDocument(getter_AddRefs(doc));
-  *aIsDocumentEditable = doc ? IsModifiable() : false;
+  nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
+  *aIsDocumentEditable = doc && IsModifiable();
 
   return NS_OK;
 }
 
 bool nsPlaintextEditor::IsModifiable()
 {
   return !IsReadonly();
 }
@@ -885,20 +883,18 @@ NS_IMETHODIMP nsPlaintextEditor::InsertL
     res = GetStartNodeAndOffset(selection, getter_AddRefs(selNode), &selOffset);
     NS_ENSURE_SUCCESS(res, res);
 
     // don't put text in places that can't have it
     if (!IsTextNode(selNode) && !CanContainTag(selNode, NS_LITERAL_STRING("#text")))
       return NS_ERROR_FAILURE;
 
     // we need to get the doc
-    nsCOMPtr<nsIDOMDocument> doc;
-    res = GetDocument(getter_AddRefs(doc));
-    NS_ENSURE_SUCCESS(res, res);
-    NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
+    nsCOMPtr<nsIDOMDocument> doc = GetDOMDocument();
+    NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
 
     // don't spaz my selection in subtransactions
     nsAutoTxnsConserveSelection dontSpazMySelection(this);
 
     // insert a linefeed character
     res = InsertTextImpl(NS_LITERAL_STRING("\n"), address_of(selNode),
                          &selOffset, doc);
     if (!selNode) res = NS_ERROR_NULL_POINTER; // don't return here, so DidDoAction is called
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -706,20 +706,18 @@ nsTextEditRules::WillInsertText(PRInt32 
   res = mEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
   NS_ENSURE_SUCCESS(res, res);
 
   // don't put text in places that can't have it
   if (!mEditor->IsTextNode(selNode) && !mEditor->CanContainTag(selNode, NS_LITERAL_STRING("#text")))
     return NS_ERROR_FAILURE;
 
   // we need to get the doc
-  nsCOMPtr<nsIDOMDocument>doc;
-  res = mEditor->GetDocument(getter_AddRefs(doc));
-  NS_ENSURE_SUCCESS(res, res);
-  NS_ENSURE_TRUE(doc, NS_ERROR_NULL_POINTER);
+  nsCOMPtr<nsIDOMDocument> doc = mEditor->GetDOMDocument();
+  NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     
   if (aAction == kInsertTextIME) 
   { 
     res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc);
     NS_ENSURE_SUCCESS(res, res);
   }
   else // aAction == kInsertText
   {
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -907,17 +907,17 @@ XPCConvert::NativeInterface2JSObject(XPC
     // to use is found as cache->GetWrapper(). If that is null, then the
     // object will create (and fill the cache) from its WrapObject call.
     nsWrapperCache *cache = aHelper.GetWrapperCache();
 
     bool tryConstructSlimWrapper = false;
     JSObject *flat;
     if (cache) {
         flat = cache->GetWrapper();
-        if (cache->IsProxy()) {
+        if (cache->IsDOMBinding()) {
             XPCCallContext &ccx = lccx.GetXPCCallContext();
             if (!ccx.IsValid())
                 return false;
 
             if (!flat) {
                 bool triedToWrap;
                 flat = cache->WrapObject(lccx.GetJSContext(),
                                          xpcscope->GetGlobalJSObject(),
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -374,22 +374,16 @@ TraceJSHolder(JSDHashTable *table, JSDHa
 {
     ObjectHolder* entry = reinterpret_cast<ObjectHolder*>(hdr);
 
     entry->tracer->Trace(entry->holder, TraceJSObject, arg);
 
     return JS_DHASH_NEXT;
 }
 
-struct ClearedGlobalObject : public JSDHashEntryHdr
-{
-    JSContext* mContext;
-    JSObject* mGlobalObject;
-};
-
 static PLDHashOperator
 TraceExpandos(XPCWrappedNative *wn, JSObject *&expando, void *aClosure)
 {
     if (wn->IsWrapperExpired())
         return PL_DHASH_REMOVE;
     JS_CALL_OBJECT_TRACER(static_cast<JSTracer *>(aClosure), expando, "expando object");
     return PL_DHASH_NEXT;
 }
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -56,19 +56,19 @@
 #include "nsContentUtils.h"
 
 #include "mozilla/Util.h"
 
 bool
 xpc_OkToHandOutWrapper(nsWrapperCache *cache)
 {
     NS_ABORT_IF_FALSE(cache->GetWrapper(), "Must have wrapper");
-    NS_ABORT_IF_FALSE(cache->IsProxy() || IS_WN_WRAPPER(cache->GetWrapper()),
+    NS_ABORT_IF_FALSE(cache->IsDOMBinding() || IS_WN_WRAPPER(cache->GetWrapper()),
                       "Must have proxy or XPCWrappedNative wrapper");
-    return cache->IsProxy() ?
+    return cache->IsDOMBinding() ?
         mozilla::dom::binding::instanceIsProxy(cache->GetWrapper()) :
         !static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(cache->GetWrapper()))->
             NeedsSOW();
 }
 
 /***************************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
--- a/js/xpconnect/src/dombindings.cpp
+++ b/js/xpconnect/src/dombindings.cpp
@@ -552,17 +552,17 @@ ListBase<LC>::create(JSContext *cx, JSOb
         if (!ac.enter(cx, parent))
             return NULL;
     }
 
     XPCWrappedNativeScope *xpcscope =
         XPCWrappedNativeScope::FindInJSObjectScope(cx, parent);
     JSObject *proto = getPrototype(cx, xpcscope, triedToWrap);
     if (!proto && !*triedToWrap)
-        aWrapperCache->ClearIsProxy();
+        aWrapperCache->ClearIsDOMBinding();
     if (!proto)
         return NULL;
     JSObject *obj = NewProxyObject(cx, &ListBase<LC>::instance,
                                    PrivateValue(aList), proto, parent);
     if (!obj)
         return NULL;
 
     NS_ADDREF(aList);
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -133,19 +133,19 @@ extern bool
 xpc_OkToHandOutWrapper(nsWrapperCache *cache);
 
 inline JSObject*
 xpc_FastGetCachedWrapper(nsWrapperCache *cache, JSObject *scope, jsval *vp)
 {
     if (cache) {
         JSObject* wrapper = cache->GetWrapper();
         NS_ASSERTION(!wrapper ||
-                     !cache->IsProxy() ||
+                     !cache->IsDOMBinding() ||
                      !IS_SLIM_WRAPPER(wrapper),
-                     "Should never have a slim wrapper when IsProxy()");
+                     "Should never have a slim wrapper when IsDOMBinding()");
         if (wrapper &&
             js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(scope) &&
             (IS_SLIM_WRAPPER(wrapper) ||
              xpc_OkToHandOutWrapper(cache))) {
             *vp = OBJECT_TO_JSVAL(wrapper);
             return wrapper;
         }
     }
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -1183,17 +1183,17 @@ nsIOService::TrackNetworkLinkStatusForOf
     return SetOffline(!isUp);
 }
 
 NS_IMETHODIMP
 nsIOService::EscapeString(const nsACString& aString,
                           PRUint32 aEscapeType,
                           nsACString& aResult)
 {
-  NS_ENSURE_ARG_RANGE(aEscapeType, 0, 4);
+  NS_ENSURE_ARG_MAX(aEscapeType, 4);
 
   nsCAutoString stringCopy(aString);
   nsCString result;
 
   if (!NS_Escape(stringCopy, result, (nsEscapeMask) aEscapeType))
     return NS_ERROR_OUT_OF_MEMORY;
 
   aResult.Assign(result);
--- a/xulrunner/Makefile.in
+++ b/xulrunner/Makefile.in
@@ -46,18 +46,16 @@ include $(DEPTH)/config/autoconf.mk
 PARALLEL_DIRS = \
   app \
   setup \
   stub \
   examples \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
-ifdef MOZ_INSTALLER
 DIRS += installer/windows
 endif
-endif
 
 ifeq ($(OS_ARCH),WINNT)
 PARALLEL_DIRS += tools/redit
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/xulrunner/makefiles.sh
+++ b/xulrunner/makefiles.sh
@@ -47,17 +47,17 @@ xulrunner/examples/simple/Makefile
 xulrunner/examples/simple/components/Makefile
 xulrunner/examples/simple/components/public/Makefile
 xulrunner/examples/simple/components/src/Makefile
 xulrunner/setup/Makefile
 xulrunner/stub/Makefile
 xulrunner/installer/Makefile
 "
 
-if [ "$OS_ARCH" = "WINNT" -a "$MOZ_INSTALLER" ]; then
+if [ "$OS_ARCH" = "WINNT" ]; then
   add_makefiles "
     xulrunner/installer/windows/Makefile
   "
 fi
 
 if [ "$OS_ARCH" = "Darwin" ]; then
   add_makefiles "
     xulrunner/installer/mac/Makefile