Merge mozilla-central to mozilla-inbound
authorEd Morley <emorley@mozilla.com>
Thu, 26 Jul 2012 13:25:04 +0100
changeset 100568 f50ebe5069160cbc6d6eb7dd74c39c7ec4dcbb86
parent 100567 29874fe36f9097a2ad100f1905e50c3ad2afcada (current diff)
parent 100555 20db7c6d82cc5bcb0e916bfe72d1ab04d3c3be2d (diff)
child 100569 b571e6fddb8fd7abf4b0bdd2cddc93880ab12dab
push id23185
push usermbrubeck@mozilla.com
push dateThu, 26 Jul 2012 20:58:28 +0000
treeherdermozilla-central@8a7ad0adcccf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone17.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 to mozilla-inbound
b2g/chrome/content/shell.js
content/events/src/nsDOMApplicationEvent.cpp
content/events/src/nsDOMApplicationEvent.h
content/events/src/nsDOMDeviceLightEvent.cpp
content/events/src/nsDOMDeviceLightEvent.h
content/events/src/nsDOMDeviceOrientationEvent.cpp
content/events/src/nsDOMDeviceOrientationEvent.h
--- a/b2g/chrome/content/dbg-browser-actors.js
+++ b/b2g/chrome/content/dbg-browser-actors.js
@@ -1,15 +1,19 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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/. */
 
 'use strict';
+/**
+ * B2G-specific actors that extend BrowserRootActor and BrowserTabActor,
+ * overriding some of their methods.
+ */
 
 /**
  * The function that creates the root actor. DebuggerServer expects to find this
  * function in the loaded actors in order to initialize properly.
  */
 function createRootActor(connection) {
   return new DeviceRootActor(connection);
 }
@@ -19,77 +23,62 @@ function createRootActor(connection) {
  * The root actor is responsible for the initial 'hello' packet and for
  * responding to a 'listTabs' request that produces the list of currently open
  * tabs.
  *
  * @param connection DebuggerServerConnection
  *        The conection to the client.
  */
 function DeviceRootActor(connection) {
-  this.conn = connection;
-  this._tabActors = new WeakMap();
-  this._tabActorPool = null;
-  this._actorFactories = null;
+  BrowserRootActor.call(this, connection);
   this.browser = Services.wm.getMostRecentWindow('navigator:browser');
 }
 
-DeviceRootActor.prototype = {
-  /**
-   * Return a 'hello' packet as specified by the Remote Debugging Protocol.
-   */
-  sayHello: function DRA_sayHello() {
-    return {
-      from: 'root',
-      applicationType: 'browser',
-      traits: []
-    };
-  },
+DeviceRootActor.prototype = new BrowserRootActor();
 
-  /**
-   * Disconnects the actor from the browser window.
-   */
-  disconnect: function DRA_disconnect() {
-    let actor = this._tabActors.get(this.browser);
-    if (actor) {
-      actor.exit();
-    }
-  },
+/**
+ * Disconnects the actor from the browser window.
+ */
+DeviceRootActor.prototype.disconnect = function DRA_disconnect() {
+  let actor = this._tabActors.get(this.browser);
+  if (actor) {
+    actor.exit();
+  }
+};
 
-  /**
-   * Handles the listTabs request.  Builds a list of actors for the single
-   * tab (window) running in the process. The actors will survive
-   * until at least the next listTabs request.
-   */
-  onListTabs: function DRA_onListTabs() {
-    let actor = this._tabActors.get(this.browser);
-    if (!actor) {
-      actor = new DeviceTabActor(this.conn, this.browser);
-      // this.actorID is set by ActorPool when an actor is put into one.
-      actor.parentID = this.actorID;
-      this._tabActors.set(this.browser, actor);
-    }
-
-    let actorPool = new ActorPool(this.conn);
-    actorPool.addActor(actor);
-
-    // Now drop the old actorID -> actor map. Actors that still mattered were
-    // added to the new map, others will go away.
-    if (this._tabActorPool) {
-      this.conn.removeActorPool(this._tabActorPool);
-    }
-    this._tabActorPool = actorPool;
-    this.conn.addActorPool(this._tabActorPool);
-
-    return {
-      'from': 'root',
-      'selected': 0,
-      'tabs': [actor.grip()]
-    };
+/**
+ * Handles the listTabs request.  Builds a list of actors for the single
+ * tab (window) running in the process. The actors will survive
+ * until at least the next listTabs request.
+ */
+DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() {
+  let actor = this._tabActors.get(this.browser);
+  if (!actor) {
+    actor = new DeviceTabActor(this.conn, this.browser);
+    // this.actorID is set by ActorPool when an actor is put into one.
+    actor.parentID = this.actorID;
+    this._tabActors.set(this.browser, actor);
   }
 
+  let actorPool = new ActorPool(this.conn);
+  actorPool.addActor(actor);
+
+  // Now drop the old actorID -> actor map. Actors that still mattered were
+  // added to the new map, others will go away.
+  if (this._tabActorPool) {
+    this.conn.removeActorPool(this._tabActorPool);
+  }
+  this._tabActorPool = actorPool;
+  this.conn.addActorPool(this._tabActorPool);
+
+  return {
+    'from': 'root',
+    'selected': 0,
+    'tabs': [actor.grip()]
+  };
 };
 
 /**
  * The request types this actor can handle.
  */
 DeviceRootActor.prototype.requestTypes = {
   'listTabs': DeviceRootActor.prototype.onListTabs
 };
@@ -99,224 +88,63 @@ DeviceRootActor.prototype.requestTypes =
  * and detaching.
  *
  * @param connection DebuggerServerConnection
  *        The connection to the client.
  * @param browser browser
  *        The browser instance that contains this tab.
  */
 function DeviceTabActor(connection, browser) {
-  this.conn = connection;
-  this._browser = browser;
+  BrowserTabActor.call(this, connection, browser);
 }
 
-DeviceTabActor.prototype = {
-  get browser() {
-    return this._browser;
-  },
-
-  get exited() {
-    return !this.browser;
-  },
-
-  get attached() {
-    return !!this._attached
-  },
-
-  _tabPool: null,
-  get tabActorPool() {
-    return this._tabPool;
-  },
-
-  _contextPool: null,
-  get contextActorPool() {
-    return this._contextPool;
-  },
-
-  /**
-   * Add the specified breakpoint to the default actor pool connection, in order
-   * to be alive as long as the server is.
-   *
-   * @param BreakpointActor actor
-   *        The actor object.
-   */
-  addToBreakpointPool: function DTA_addToBreakpointPool(actor) {
-    this.conn.addActor(actor);
-  },
-
-  /**
-   * Remove the specified breakpint from the default actor pool.
-   *
-   * @param string actor
-   *        The actor ID.
-   */
-  removeFromBreakpointPool: function DTA_removeFromBreakpointPool(actor) {
-    this.conn.removeActor(actor);
-  },
-
-  actorPrefix: 'tab',
-
-  grip: function DTA_grip() {
-    dbg_assert(!this.exited,
-               'grip() should not be called on exited browser actor.');
-    dbg_assert(this.actorID,
-               'tab should have an actorID.');
-    return {
-      'actor': this.actorID,
-      'title': this.browser.title,
-      'url': this.browser.document.documentURI
-    }
-  },
-
-  /**
-   * Called when the actor is removed from the connection.
-   */
-  disconnect: function DTA_disconnect() {
-    this._detach();
-  },
-
-  /**
-   * Called by the root actor when the underlying tab is closed.
-   */
-  exit: function DTA_exit() {
-    if (this.exited) {
-      return;
-    }
-
-    if (this.attached) {
-      this._detach();
-      this.conn.send({
-        'from': this.actorID,
-        'type': 'tabDetached'
-      });
-    }
-
-    this._browser = null;
-  },
-
-  /**
-   * Does the actual work of attaching to a tab.
-   */
-  _attach: function DTA_attach() {
-    if (this._attached) {
-      return;
-    }
-
-    // Create a pool for tab-lifetime actors.
-    dbg_assert(!this._tabPool, 'Should not have a tab pool if we were not attached.');
-    this._tabPool = new ActorPool(this.conn);
-    this.conn.addActorPool(this._tabPool);
-
-    // ... and a pool for context-lifetime actors.
-    this._pushContext();
+DeviceTabActor.prototype = new BrowserTabActor();
 
-    this._attached = true;
-  },
-
-  /**
-   * Creates a thread actor and a pool for context-lifetime actors. It then sets
-   * up the content window for debugging.
-   */
-  _pushContext: function DTA_pushContext() {
-    dbg_assert(!this._contextPool, "Can't push multiple contexts");
-
-    this._contextPool = new ActorPool(this.conn);
-    this.conn.addActorPool(this._contextPool);
-
-    this.threadActor = new ThreadActor(this);
-    this._addDebuggees(this.browser.wrappedJSObject);
-    this._contextPool.addActor(this.threadActor);
-  },
-
-  /**
-   * Add the provided window and all windows in its frame tree as debuggees.
-   */
-  _addDebuggees: function DTA__addDebuggees(content) {
-    this.threadActor.addDebuggee(content);
-    let frames = content.frames;
-    for (let i = 0; i < frames.length; i++) {
-      this._addDebuggees(frames[i]);
-    }
-  },
-
-  /**
-   * Exits the current thread actor and removes the context-lifetime actor pool.
-   * The content window is no longer being debugged after this call.
-   */
-  _popContext: function DTA_popContext() {
-    dbg_assert(!!this._contextPool, 'No context to pop.');
-
-    this.conn.removeActorPool(this._contextPool);
-    this._contextPool = null;
-    this.threadActor.exit();
-    this.threadActor = null;
-  },
-
-  /**
-   * Does the actual work of detaching from a tab.
-   */
-  _detach: function DTA_detach() {
-    if (!this.attached) {
-      return;
-    }
-
-    this._popContext();
-
-    // Shut down actors that belong to this tab's pool.
-    this.conn.removeActorPool(this._tabPool);
-    this._tabPool = null;
-
-    this._attached = false;
-  },
-
-  // Protocol Request Handlers
-
-  onAttach: function DTA_onAttach(aRequest) {
-    if (this.exited) {
-      return { type: 'exited' };
-    }
-
-    this._attach();
-
-    return { type: 'tabAttached', threadActor: this.threadActor.actorID };
-  },
-
-  onDetach: function DTA_onDetach(aRequest) {
-    if (!this.attached) {
-      return { error: 'wrongState' };
-    }
-
-    this._detach();
-
-    return { type: 'detached' };
-  },
-
-  /**
-   * Prepare to enter a nested event loop by disabling debuggee events.
-   */
-  preNest: function DTA_preNest() {
-    let windowUtils = this.browser
-                          .QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils);
-    windowUtils.suppressEventHandling(true);
-    windowUtils.suspendTimeouts();
-  },
-
-  /**
-   * Prepare to exit a nested event loop by enabling debuggee events.
-   */
-  postNest: function DTA_postNest(aNestData) {
-    let windowUtils = this.browser
-                          .QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils);
-    windowUtils.resumeTimeouts();
-    windowUtils.suppressEventHandling(false);
+DeviceTabActor.prototype.grip = function DTA_grip() {
+  dbg_assert(!this.exited,
+             'grip() should not be called on exited browser actor.');
+  dbg_assert(this.actorID,
+             'tab should have an actorID.');
+  return {
+    'actor': this.actorID,
+    'title': this.browser.title,
+    'url': this.browser.document.documentURI
   }
-
 };
 
 /**
- * The request types this actor can handle.
+ * Creates a thread actor and a pool for context-lifetime actors. It then sets
+ * up the content window for debugging.
+ */
+DeviceTabActor.prototype._pushContext = function DTA_pushContext() {
+  dbg_assert(!this._contextPool, "Can't push multiple contexts");
+
+  this._contextPool = new ActorPool(this.conn);
+  this.conn.addActorPool(this._contextPool);
+
+  this.threadActor = new ThreadActor(this);
+  this._addDebuggees(this.browser.wrappedJSObject);
+  this._contextPool.addActor(this.threadActor);
+};
+
+// Protocol Request Handlers
+
+/**
+ * Prepare to enter a nested event loop by disabling debuggee events.
  */
-DeviceTabActor.prototype.requestTypes = {
-  'attach': DeviceTabActor.prototype.onAttach,
-  'detach': DeviceTabActor.prototype.onDetach
+DeviceTabActor.prototype.preNest = function DTA_preNest() {
+  let windowUtils = this.browser
+                        .QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIDOMWindowUtils);
+  windowUtils.suppressEventHandling(true);
+  windowUtils.suspendTimeouts();
 };
+
+/**
+ * Prepare to exit a nested event loop by enabling debuggee events.
+ */
+DeviceTabActor.prototype.postNest = function DTA_postNest(aNestData) {
+  let windowUtils = this.browser
+                        .QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIDOMWindowUtils);
+  windowUtils.resumeTimeouts();
+  windowUtils.suppressEventHandling(false);
+};
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -539,16 +539,17 @@ var WebappsHelper = {
   }
 }
 
 // Start the debugger server.
 function startDebugger() {
   if (!DebuggerServer.initialized) {
     // Allow remote connections.
     DebuggerServer.init(function () { return true; });
+    DebuggerServer.addBrowserActors();
     DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js');
   }
 
   let port = Services.prefs.getIntPref('devtools.debugger.remote-port') || 6000;
   try {
     DebuggerServer.openListener(port);
   } catch (e) {
     dump('Unable to start debugger server: ' + e + '\n');
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -83,27 +83,28 @@
     <command id="cmd_fullZoomReduce"  oncommand="FullZoom.reduce()"/>
     <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
     <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
     <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
     <command id="Browser:OpenLocation" oncommand="openLocation();"/>
 
     <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
     <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
-    <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();"/>
+    <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focus();" disabled="true"/>
     <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
-    <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
-    <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();"/>
-    <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();"/>
-    <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();"/>
-    <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();"/>
-    <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();"/>
-    <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();"/>
+    <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true" hidden="true"/>
+    <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true" hidden="true"/>
+    <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true" hidden="true"/>
+    <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/>
+    <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/>
+    <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true" hidden="true"/>
+    <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/>
     <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
+    <command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" disabled="true" hidden="true"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
     <command id="Social:SharePage" oncommand="SocialShareButton.sharePage();"/>
     <command id="Social:UnsharePage" oncommand="SocialShareButton.unsharePage();"/>
@@ -185,69 +186,60 @@
     <broadcaster id="workOfflineMenuitemState"/>
     <broadcaster id="socialSidebarBroadcaster" hidden="true"/>
 
     <!-- DevTools broadcasters -->
     <broadcaster id="devtoolsMenuBroadcaster_DevToolbar"
                  label="&devToolbarMenu.label;"
                  type="checkbox" autocheck="false"
                  command="Tools:DevToolbar"
-                 key="key_devToolbar"
-                 disabled="true" hidden="true"/>
+                 key="key_devToolbar"/>
     <broadcaster id="devtoolsMenuBroadcaster_WebConsole"
                  label="&webConsoleCmd.label;"
                  type="checkbox" autocheck="false"
                  key="key_webConsole"
                  command="Tools:WebConsole"/>
     <broadcaster id="devtoolsMenuBroadcaster_Inspect"
                  label="&inspectMenu.label;"
                  type="checkbox" autocheck="false"
                  command="Tools:Inspect"
-                 key="key_inspect"
-                 disabled="true" hidden="true"/>
+                 key="key_inspect"/>
     <broadcaster id="devtoolsMenuBroadcaster_Debugger"
                  label="&debuggerMenu.label2;"
                  type="checkbox" autocheck="false"
                  command="Tools:Debugger"
-                 key="key_debugger"
-                 disabled="true" hidden="true"/>
+                 key="key_debugger"/>
     <broadcaster id="devtoolsMenuBroadcaster_RemoteDebugger"
                  label="&remoteDebuggerMenu.label;"
-                 command="Tools:RemoteDebugger"
-                 disabled="true" hidden="true"/>
+                 command="Tools:RemoteDebugger"/>
     <broadcaster id="devtoolsMenuBroadcaster_ChromeDebugger"
                  label="&chromeDebuggerMenu.label;"
-                 command="Tools:ChromeDebugger"
-                 disabled="true" hidden="true"/>
+                 command="Tools:ChromeDebugger"/>
     <broadcaster id="devtoolsMenuBroadcaster_Scratchpad"
                  label="&scratchpad.label;"
                  command="Tools:Scratchpad"
-                 key="key_scratchpad"
-                 disabled="true" hidden="true"/>
+                 key="key_scratchpad"/>
     <broadcaster id="devtoolsMenuBroadcaster_StyleEditor"
                  label="&styleeditor.label;"
                  type="checkbox" autocheck="false"
                  command="Tools:StyleEditor"
-                 key="key_styleeditor"
-                 disabled="true" hidden="true"/>
+                 key="key_styleeditor"/>
     <broadcaster id="devtoolsMenuBroadcaster_ResponsiveUI"
                  label="&responsiveDesignTool.label;"
                  type="checkbox" autocheck="false"
                  command="Tools:ResponsiveUI"
-                 key="key_responsiveUI"
-                 disabled="true" hidden="true"/>
+                 key="key_responsiveUI"/>
     <broadcaster id="devtoolsMenuBroadcaster_PageSource"
                  label="&pageSourceCmd.label;"
                  key="key_viewSource"
                  command="View:PageSource"/>
     <broadcaster id="devtoolsMenuBroadcaster_ErrorConsole"
-                 hidden="true"
                  label="&errorConsoleCmd.label;"
                  key="key_errorConsole"
-                 oncommand="toJavaScriptConsole();"/>
+                 command="Tools:ErrorConsole"/>
     <broadcaster id="devtoolsMenuBroadcaster_GetMoreTools"
                  label="&getMoreDevtoolsCmd.label;"
                  oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/>
   </broadcasterset>
 
   <keyset id="mainKeyset">
     <key id="key_newNavigator"
          key="&newNavigatorCmd.key;"
@@ -287,17 +279,17 @@
 #endif
 #ifdef XP_GNOME
     <key id="key_search2" key="&searchFocusUnix.commandkey;" command="Tools:Search" modifiers="accel"/>
     <key id="key_openDownloads" key="&downloadsUnix.commandkey;" command="Tools:Downloads" modifiers="accel,shift"/>
 #else
     <key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/>
 #endif
     <key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
-    <key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift" disabled="true"/>
+    <key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" command="Tools:ErrorConsole" modifiers="accel,shift"/>
     <key id="key_devToolbar" keycode="&devToolbar.keycode;" modifiers="shift"
          keytext="&devToolbar.keytext;" command="Tools:DevToolbarFocus"/>
     <key id="key_webConsole" key="&webConsoleCmd.commandkey;" oncommand="HUDConsoleUI.toggleHUD();"
 #ifdef XP_MACOSX
         modifiers="accel,alt"
 #else
         modifiers="accel,shift"
 #endif
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1397,101 +1397,101 @@ var gBrowserInit = {
     window.addEventListener("resize", function resizeHandler(event) {
       if (event.target == window)
         setUrlAndSearchBarWidthForConditionalForwardButton();
     });
 
     // Enable developer toolbar?
     let devToolbarEnabled = gPrefService.getBoolPref("devtools.toolbar.enabled");
     if (devToolbarEnabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_DevToolbar");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:DevToolbar");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
       document.getElementById("Tools:DevToolbarFocus").removeAttribute("disabled");
 
       // Show the toolbar if it was previously visible
       if (gPrefService.getBoolPref("devtools.toolbar.visible")) {
         DeveloperToolbar.show(false);
       }
     }
 
     // Enable Inspector?
     let enabled = gPrefService.getBoolPref("devtools.inspector.enabled");
     if (enabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Inspect");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:Inspect");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
     }
 
     // Enable Debugger?
     let enabled = gPrefService.getBoolPref("devtools.debugger.enabled");
     if (enabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Debugger");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:Debugger");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
     }
 
     // Enable Remote Debugger?
     let enabled = gPrefService.getBoolPref("devtools.debugger.remote-enabled");
     if (enabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_RemoteDebugger");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:RemoteDebugger");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
     }
 
     // Enable Chrome Debugger?
     let enabled = gPrefService.getBoolPref("devtools.chrome.enabled") &&
                   gPrefService.getBoolPref("devtools.debugger.chrome-enabled") &&
                   gPrefService.getBoolPref("devtools.debugger.remote-enabled");
     if (enabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ChromeDebugger");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:ChromeDebugger");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
     }
 
     // Enable Error Console?
     // XXX Temporarily always-enabled, see bug 601201
     let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled");
     if (consoleEnabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ErrorConsole");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:ErrorConsole");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
     }
 
     // Enable Scratchpad in the UI, if the preference allows this.
     let scratchpadEnabled = gPrefService.getBoolPref(Scratchpad.prefEnabledName);
     if (scratchpadEnabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Scratchpad");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:Scratchpad");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
     }
 
     // Enable Style Editor?
     let styleEditorEnabled = gPrefService.getBoolPref(StyleEditor.prefEnabledName);
     if (styleEditorEnabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_StyleEditor");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:StyleEditor");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
     }
 
 #ifdef MENUBAR_CAN_AUTOHIDE
     // If the user (or the locale) hasn't enabled the top-level "Character
     // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
     // hide it.
     if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
                                                Ci.nsIPrefLocalizedString).data)
       document.getElementById("appmenu_charsetMenu").hidden = true;
 #endif
 
     // Enable Responsive UI?
     let responsiveUIEnabled = gPrefService.getBoolPref("devtools.responsiveUI.enabled");
     if (responsiveUIEnabled) {
-      let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ResponsiveUI");
-      broadcaster.removeAttribute("disabled");
-      broadcaster.removeAttribute("hidden");
+      let cmd = document.getElementById("Tools:ResponsiveUI");
+      cmd.removeAttribute("disabled");
+      cmd.removeAttribute("hidden");
     }
 
     let appMenuButton = document.getElementById("appmenu-button");
     let appMenuPopup = document.getElementById("appmenu-popup");
     if (appMenuButton && appMenuPopup) {
       let appMenuOpening = null;
       appMenuButton.addEventListener("mousedown", function(event) {
         if (event.button == 0)
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1065,35 +1065,35 @@
       <tabbrowser id="content" disablehistory="true"
                   flex="1" contenttooltip="aHTMLTooltip"
                   tabcontainer="tabbrowser-tabs"
                   contentcontextmenu="contentAreaContextMenu"
                   autocompletepopup="PopupAutoComplete"
                   onclick="contentAreaClick(event, false);"/>
       <statuspanel id="statusbar-display" inactive="true"/>
     </vbox>
+    <splitter id="devtools-side-splitter" hidden="true"/>
+    <vbox id="devtools-sidebar-box" hidden="true"
+          style="min-width: 18em; width: 22em; max-width: 42em;" persist="width">
+      <toolbar id="devtools-sidebar-toolbar"
+               class="devtools-toolbar"
+               nowindowdrag="true"/>
+      <deck id="devtools-sidebar-deck" flex="1"/>
+    </vbox>
     <splitter id="social-sidebar-splitter"
               class="chromeclass-extrachrome"
               observes="socialSidebarBroadcaster"/>
     <vbox id="social-sidebar-box"
           class="chromeclass-extrachrome"
           observes="socialSidebarBroadcaster">
       <browser id="social-sidebar-browser"
                type="content"
                flex="1"
                style="min-width: 14em; width: 18em; max-width: 36em;"/>
     </vbox>
-    <splitter id="devtools-side-splitter" hidden="true"/>
-    <vbox id="devtools-sidebar-box" hidden="true"
-          style="min-width: 18em; width: 22em; max-width: 42em;" persist="width">
-      <toolbar id="devtools-sidebar-toolbar"
-               class="devtools-toolbar"
-               nowindowdrag="true"/>
-      <deck id="devtools-sidebar-deck" flex="1"/>
-    </vbox>
     <vbox id="browser-border-end" hidden="true" layer="true"/>
   </hbox>
 
   <hbox id="full-screen-warning-container" hidden="true" fadeout="true">
     <hbox style="width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. -->
       <vbox id="full-screen-warning-message" align="center">
         <description id="full-screen-domain-text"/>
         <description class="full-screen-description" value="&fullscreenExitHint.value;"/>
--- a/browser/devtools/tilt/test/Makefile.in
+++ b/browser/devtools/tilt/test/Makefile.in
@@ -5,21 +5,18 @@
 DEPTH			= ../../../..
 topsrcdir		= @top_srcdir@
 srcdir			= @srcdir@
 VPATH			= @srcdir@
 relativesrcdir 	= browser/devtools/tilt/test
 
 include $(DEPTH)/config/autoconf.mk
 
-MOCHITEST_BROWSER_FILES = head.js
-
-# browser_tilt* disabled on Linux due to bug 759157
-ifneq (gtk2,$(MOZ_WIDGET_TOOLKIT))
-MOCHITEST_BROWSER_FILES += \
+MOCHITEST_BROWSER_FILES = \
+	head.js \
 	browser_tilt_01_lazy_getter.js \
 	browser_tilt_02_notifications-seq.js \
 	browser_tilt_02_notifications.js \
 	browser_tilt_03_tab_switch.js \
 	browser_tilt_04_initialization-key.js \
 	browser_tilt_04_initialization.js \
 	browser_tilt_05_destruction-esc.js \
 	browser_tilt_05_destruction-url.js \
@@ -55,11 +52,10 @@ MOCHITEST_BROWSER_FILES += \
 	browser_tilt_utils03.js \
 	browser_tilt_utils04.js \
 	browser_tilt_utils05.js \
 	browser_tilt_utils06.js \
 	browser_tilt_utils07.js \
 	browser_tilt_visualizer.js \
 	browser_tilt_zoom.js \
 	$(NULL)
-endif
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/webconsole/WebConsoleUtils.jsm
+++ b/browser/devtools/webconsole/WebConsoleUtils.jsm
@@ -533,17 +533,17 @@ var WebConsoleUtils = {
         value = "";
         presentable = {type: TYPES.GETTER, display: "Getter"};
       }
       else {
         try {
           value = aObject[propName];
           presentable = this.presentableValueFor(value);
         }
-	      catch (ex) {
+        catch (ex) {
           continue;
         }
       }
 
       let pair = {};
       pair.name = propName;
       pair.value = presentable.display;
       pair.inspectable = false;
@@ -730,16 +730,18 @@ const STATE_DQUOTE = 3;
 const OPEN_BODY = "{[(".split("");
 const CLOSE_BODY = "}])".split("");
 const OPEN_CLOSE_BODY = {
   "{": "}",
   "[": "]",
   "(": ")",
 };
 
+const MAX_COMPLETIONS = 256;
+
 /**
  * Analyses a given string to find the last statement that is interesting for
  * later completion.
  *
  * @param   string aStr
  *          A string to analyse.
  *
  * @returns object
@@ -890,19 +892,19 @@ function JSPropertyProvider(aScope, aInp
   if (properties.length > 1) {
     matchProp = properties.pop().trimLeft();
     for (let i = 0; i < properties.length; i++) {
       let prop = properties[i].trim();
       if (!prop) {
         return null;
       }
 
-      // If obj is undefined or null, then there is no chance to run completion
-      // on it. Exit here.
-      if (typeof obj === "undefined" || obj === null) {
+      // If obj is undefined or null (which is what "== null" does),
+      // then there is no chance to run completion on it. Exit here.
+      if (obj == null) {
         return null;
       }
 
       // Check if prop is a getter function on obj. Functions can change other
       // stuff so we can't execute them to get the next object. Stop here.
       if (WCU.isNonNativeGetter(obj, prop)) {
         return null;
       }
@@ -913,34 +915,79 @@ function JSPropertyProvider(aScope, aInp
         return null;
       }
     }
   }
   else {
     matchProp = properties[0].trimLeft();
   }
 
-  // If obj is undefined or null, then there is no chance to run
-  // completion on it. Exit here.
-  if (typeof obj === "undefined" || obj === null) {
+  // If obj is undefined or null (which is what "== null" does),
+  // then there is no chance to run completion on it. Exit here.
+  if (obj == null) {
     return null;
   }
 
   // Skip Iterators and Generators.
   if (WCU.isIteratorOrGenerator(obj)) {
     return null;
   }
 
-  let matches = [];
-  for (let prop in obj) {
-    if (prop.indexOf(matchProp) == 0) {
-      matches.push(prop);
-    }
-  }
+  let matches = Object.keys(getMatchedProps(obj, matchProp));
 
   return {
     matchProp: matchProp,
     matches: matches.sort(),
   };
 }
 
+/**
+ * Get all accessible properties on this object.
+ * Filter those properties by name.
+ * Take only a certain number of those.
+ *
+ * @param object obj
+ *        Object whose properties we want to collect.
+ *
+ * @param string matchProp
+ *        Filter for properties that match this one.
+ *        Defaults to the empty string (which always matches).
+ *
+ * @return object
+ *         Object whose keys are all accessible properties on the object.
+ */
+function getMatchedProps(aObj, aMatchProp = "")
+{
+  let c = MAX_COMPLETIONS;
+  let names = {};   // Using an Object to avoid duplicates.
+  let ownNames = Object.getOwnPropertyNames(aObj);
+  for (let i = 0; i < ownNames.length; i++) {
+    if (ownNames[i].indexOf(aMatchProp) == 0) {
+      if (names[ownNames[i]] != true) {
+        c--;
+        if (c < 0) {
+          return names;
+        }
+        names[ownNames[i]] = true;
+      }
+    }
+  }
+
+  // We need to recursively go up the prototype chain.
+  aObj = Object.getPrototypeOf(aObj);
+  if (aObj !== null) {
+    let parentScope = getMatchedProps(aObj, aMatchProp);
+    for (let name in parentScope) {
+      if (names[name] != true) {
+        c--;
+        if (c < 0) {
+          return names;
+        }
+        names[name] = true;
+      }
+    }
+  }
+  return names;
+}
+
+
 return JSPropertyProvider;
 })(WebConsoleUtils);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
@@ -30,43 +30,68 @@ function consoleOpened(aHud) {
 
   ok(!popup.isOpen, "popup is not open");
 
   popup._panel.addEventListener("popupshown", function onShown() {
     popup._panel.removeEventListener("popupshown", onShown, false);
 
     ok(popup.isOpen, "popup is open");
 
-    is(popup.itemCount, 4, "popup.itemCount is correct");
+    // 4 values, and the following properties:
+    // __defineGetter__  __defineSetter__ __lookupGetter__ __lookupSetter__
+    // hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString
+    // toSource unwatch valueOf watch constructor.
+    is(popup.itemCount, 18, "popup.itemCount is correct");
 
-    let sameItems = popup.getItems();
-    is(sameItems.every(function(aItem, aIndex) {
-      return aItem.label == "item" + aIndex;
-    }), true, "getItems returns back the same items");
+    let sameItems = popup.getItems().map(function(e) {return e.label;});
+    ok(sameItems.every(function(prop, index) {
+      return [
+        "__defineGetter__",
+        "__defineSetter__",
+        "__lookupGetter__",
+        "__lookupSetter__",
+        "constructor",
+        "hasOwnProperty",
+        "isPrototypeOf",
+        "item0",
+        "item1",
+        "item2",
+        "item3",
+        "propertyIsEnumerable",
+        "toLocaleString",
+        "toSource",
+        "toString",
+        "unwatch",
+        "valueOf",
+        "watch",
+      ][index] === prop}), "getItems returns the items we expect");
 
     is(popup.selectedIndex, -1, "no index is selected");
     EventUtils.synthesizeKey("VK_DOWN", {});
-      
+
     let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
 
     is(popup.selectedIndex, 0, "index 0 is selected");
-    is(popup.selectedItem.label, "item0", "item0 is selected");
-    is(completeNode.value, prefix + "item0", "completeNode.value holds item0");
+    is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected");
+    is(completeNode.value, prefix + "__defineGetter__",
+        "completeNode.value holds __defineGetter__");
 
     EventUtils.synthesizeKey("VK_DOWN", {});
 
     is(popup.selectedIndex, 1, "index 1 is selected");
-    is(popup.selectedItem.label, "item1", "item1 is selected");
-    is(completeNode.value, prefix + "item1", "completeNode.value holds item1");
+    is(popup.selectedItem.label, "__defineSetter__", "__defineSetter__ is selected");
+    is(completeNode.value, prefix + "__defineSetter__",
+        "completeNode.value holds __defineSetter__");
 
     EventUtils.synthesizeKey("VK_UP", {});
 
     is(popup.selectedIndex, 0, "index 0 is selected");
-    is(popup.selectedItem.label, "item0", "item0 is selected");
-    is(completeNode.value, prefix + "item0", "completeNode.value holds item0");
+    is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected");
+    is(completeNode.value, prefix + "__defineGetter__",
+        "completeNode.value holds __defineGetter__");
 
     popup._panel.addEventListener("popuphidden", autocompletePopupHidden, false);
 
     EventUtils.synthesizeKey("VK_TAB", {});
   }, false);
 
   jsterm.setInputValue("window.foobarBug585991");
   EventUtils.synthesizeKey(".", {});
@@ -78,36 +103,37 @@ function autocompletePopupHidden()
   let popup = jsterm.autocompletePopup;
   let completeNode = jsterm.completeNode;
   let inputNode = jsterm.inputNode;
 
   popup._panel.removeEventListener("popuphidden", autocompletePopupHidden, false);
 
   ok(!popup.isOpen, "popup is not open");
 
-  is(inputNode.value, "window.foobarBug585991.item0",
+  is(inputNode.value, "window.foobarBug585991.__defineGetter__",
      "completion was successful after VK_TAB");
 
   ok(!completeNode.value, "completeNode is empty");
 
   popup._panel.addEventListener("popupshown", function onShown() {
     popup._panel.removeEventListener("popupshown", onShown, false);
 
     ok(popup.isOpen, "popup is open");
 
-    is(popup.itemCount, 4, "popup.itemCount is correct");
+    is(popup.itemCount, 18, "popup.itemCount is correct");
 
     is(popup.selectedIndex, -1, "no index is selected");
     EventUtils.synthesizeKey("VK_DOWN", {});
-    
+
     let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
 
     is(popup.selectedIndex, 0, "index 0 is selected");
-    is(popup.selectedItem.label, "item0", "item0 is selected");
-    is(completeNode.value, prefix + "item0", "completeNode.value holds item0");
+    is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected");
+    is(completeNode.value, prefix + "__defineGetter__",
+        "completeNode.value holds __defineGetter__");
 
     popup._panel.addEventListener("popuphidden", function onHidden() {
       popup._panel.removeEventListener("popuphidden", onHidden, false);
 
       ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
 
       is(inputNode.value, "window.foobarBug585991.",
          "completion was cancelled");
@@ -135,39 +161,41 @@ function testReturnKey()
   let completeNode = jsterm.completeNode;
   let inputNode = jsterm.inputNode;
 
   popup._panel.addEventListener("popupshown", function onShown() {
     popup._panel.removeEventListener("popupshown", onShown, false);
 
     ok(popup.isOpen, "popup is open");
 
-    is(popup.itemCount, 4, "popup.itemCount is correct");
-    
+    is(popup.itemCount, 18, "popup.itemCount is correct");
+
     is(popup.selectedIndex, -1, "no index is selected");
     EventUtils.synthesizeKey("VK_DOWN", {});
 
     let prefix = jsterm.inputNode.value.replace(/[\S]/g, " ");
 
     is(popup.selectedIndex, 0, "index 0 is selected");
-    is(popup.selectedItem.label, "item0", "item0 is selected");
-    is(completeNode.value, prefix + "item0", "completeNode.value holds item0");
+    is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected");
+    is(completeNode.value, prefix + "__defineGetter__",
+        "completeNode.value holds __defineGetter__");
 
     EventUtils.synthesizeKey("VK_DOWN", {});
 
     is(popup.selectedIndex, 1, "index 1 is selected");
-    is(popup.selectedItem.label, "item1", "item1 is selected");
-    is(completeNode.value, prefix + "item1", "completeNode.value holds item1");
+    is(popup.selectedItem.label, "__defineSetter__", "__defineSetter__ is selected");
+    is(completeNode.value, prefix + "__defineSetter__",
+        "completeNode.value holds __defineSetter__");
 
     popup._panel.addEventListener("popuphidden", function onHidden() {
       popup._panel.removeEventListener("popuphidden", onHidden, false);
 
       ok(!popup.isOpen, "popup is not open after VK_RETURN");
 
-      is(inputNode.value, "window.foobarBug585991.item1",
+      is(inputNode.value, "window.foobarBug585991.__defineSetter__",
          "completion was successful after VK_RETURN");
 
       ok(!completeNode.value, "completeNode is empty");
 
       executeSoon(finishTest);
     }, false);
 
     EventUtils.synthesizeKey("VK_RETURN", {});
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js
@@ -29,18 +29,22 @@ function consoleOpened(aHud) {
 
   ok(!popup.isOpen, "popup is not open");
 
   popup._panel.addEventListener("popupshown", function onShown() {
     popup._panel.removeEventListener("popupshown", onShown, false);
 
     ok(popup.isOpen, "popup is open");
 
+    // |props| values, and the following properties:
+    // __defineGetter__  __defineSetter__ __lookupGetter__ __lookupSetter__
+    // constructor hasOwnProperty isPrototypeOf propertyIsEnumerable
+    // toLocaleString toSource toString unwatch valueOf watch.
     let props = WCU.namesAndValuesOf(content.wrappedJSObject.document.body);
-    is(popup.itemCount, props.length, "popup.itemCount is correct");
+    is(popup.itemCount, 14 + props.length, "popup.itemCount is correct");
 
     popup._panel.addEventListener("popuphidden", autocompletePopupHidden, false);
 
     EventUtils.synthesizeKey("VK_ESCAPE", {});
   }, false);
 
   jsterm.setInputValue("document.body");
   EventUtils.synthesizeKey(".", {});
--- a/browser/devtools/webconsole/test/browser_webconsole_completion.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_completion.js
@@ -45,16 +45,24 @@ function testCompletion(hud) {
   jsterm.complete(jsterm.COMPLETE_FORWARD, testNext);
   yield;
 
   is(input.value, "document", "'docu' tab completion");
   is(input.selectionStart, 8, "start selection is alright");
   is(input.selectionEnd, 8, "end selection is alright");
   is(jsterm.completeNode.value.replace(/ /g, ""), "", "'docu' completed");
 
+  // Test typing 'window.O' and press tab.
+  input.value = "window.O";
+  input.setSelectionRange(8, 8);
+  jsterm.complete(jsterm.COMPLETE_FORWARD, testNext);
+  yield;
+
+  is(input.value, "window.Object", "'window.O' tab completion");
+
   // Test typing 'document.getElem'.
   input.value = "document.getElem";
   input.setSelectionRange(16, 16);
   jsterm.complete(jsterm.COMPLETE_FORWARD, testNext);
   yield;
 
   is(input.value, "document.getElem", "'document.getElem' completion");
   is(jsterm.completeNode.value, "                entById", "'document.getElem' completion");
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -449,66 +449,66 @@ addonDesc=Manipulate add-ons
 # LOCALIZATION NOTE (addonListDesc) A very short description of the 'addon list'
 # command. This string is designed to be shown in a menu alongside the command
 # name, which is why it should be as short as possible.
 addonListDesc=List installed add-ons
 
 # LOCALIZATION NOTE (addonListTypeDesc) A very short description of the
 # 'addon list <type>' command. This string is designed to be shown in a menu
 # alongside the command name, which is why it should be as short as possible.
-addonListTypeDesc=Select an addon type
+addonListTypeDesc=Select an add-on type
 
 # LOCALIZATION NOTE (addonListDictionaryHeading, addonListExtensionHeading,
 # addonListLocaleHeading, addonListPluginHeading, addonListThemeHeading,
 # addonListUnknownHeading) Used in the output of the 'addon list' command as the
 # first line of output.
 addonListDictionaryHeading=The following dictionaries are currently installed:
 addonListExtensionHeading=The following extensions are currently installed:
 addonListLocaleHeading=The following locales are currently installed:
 addonListPluginHeading=The following plugins are currently installed:
 addonListThemeHeading=The following themes are currently installed:
-addonListAllHeading=The following addons are currently installed:
-addonListUnknownHeading=The following addons of the selected type are currently installed:
+addonListAllHeading=The following add-ons are currently installed:
+addonListUnknownHeading=The following add-ons of the selected type are currently installed:
 
 # LOCALIZATION NOTE (addonNameDesc) A very short description of the
-# name parameter of numerous addon commands. This string is designed to be shown
+# name parameter of numerous add-on commands. This string is designed to be shown
 # in a menu alongside the command name, which is why it should be as short as
 # possible.
 addonNameDesc=The name of the add-on
 
 # LOCALIZATION NOTE (addonNoneOfType) Used in the output of the 'addon list'
-# command when a search for addons of a particular type were not found.
-addonNoneOfType=There are no addons of that type installed.
+# command when a search for add-ons of a particular type were not found.
+addonNoneOfType=There are no add-ons of that type installed.
 
 # LOCALIZATION NOTE (addonEnableDesc) A very short description of the
 # 'addon enable <type>' command. This string is designed to be shown in a menu
 # alongside the command name, which is why it should be as short as possible.
 addonEnableDesc=Enable the specified add-on
 
 # LOCALIZATION NOTE (addonAlreadyEnabled) Used in the output of the
-# 'addon enable' command when an attempt is made to enable an addon is already
+# 'addon enable' command when an attempt is made to enable an add-on is already
 # enabled.
 addonAlreadyEnabled=%S is already enabled.
 
 # LOCALIZATION NOTE (addonEnabled) Used in the output of the 'addon enable'
-# command when an addon is enabled.
+# command when an add-on is enabled.
 addonEnabled=%S enabled.
 
 # LOCALIZATION NOTE (addonDisableDesc) A very short description of the
 # 'addon disable <type>' command. This string is designed to be shown in a menu
 # alongside the command name, which is why it should be as short as possible.
 addonDisableDesc=Disable the specified add-on
 
 # LOCALIZATION NOTE (addonAlreadyDisabled) Used in the output of the
-# 'addon disable' command when an attempt is made to disable an addon is already
+# 'addon disable' command when an attempt is made to disable an add-on is already
 # disabled.
 addonAlreadyDisabled=%S is already disabled.
 
 # LOCALIZATION NOTE (addonDisabled) Used in the output of the 'addon disable'
-# command when an addon is disabled.
+# command when an add-on is disabled.
 addonDisabled=%S disabled.
 
 # LOCALIZATION NOTE (exportDesc) A very short description of the 'export'
 # command. This string is designed to be shown in a menu alongside the command
 # name, which is why it should be as short as possible.
 exportDesc=Export resources
 
 # LOCALIZATION NOTE (exportHtmlDesc) A very short description of the 'export
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -31,18 +31,16 @@ CPPSRCS		= \
 		nsDOMUIEvent.cpp \
 		nsDOMKeyboardEvent.cpp \
 		nsDOMTextEvent.cpp \
 		nsDOMMouseEvent.cpp \
 		nsDOMMouseScrollEvent.cpp \
 		nsDOMDragEvent.cpp \
 		nsDOMMutationEvent.cpp \
 		nsDOMPopupBlockedEvent.cpp \
-		nsDOMDeviceLightEvent.cpp \
-		nsDOMDeviceOrientationEvent.cpp \
 		nsDOMDeviceMotionEvent.cpp \
 		nsDOMBeforeUnloadEvent.cpp \
 		nsDOMXULCommandEvent.cpp \
 		nsDOMCommandEvent.cpp \
 		nsDOMMessageEvent.cpp \
 		nsPaintRequest.cpp \
 		nsPrivateTextRange.cpp \
 		nsXMLEventsManager.cpp \
@@ -60,17 +58,16 @@ CPPSRCS		= \
 		nsDOMMozTouchEvent.cpp \
 		nsDOMEventTargetHelper.cpp \
 		nsDOMScrollAreaEvent.cpp \
 		nsDOMTransitionEvent.cpp \
 		nsDOMAnimationEvent.cpp \
 		nsDOMSettingsEvent.cpp \
 		nsDOMTouchEvent.cpp \
 		nsDOMCompositionEvent.cpp \
-		nsDOMApplicationEvent.cpp \
 		$(NULL)
 
 ifdef MOZ_B2G_RIL
 CPPSRCS += \
     nsDOMWifiEvent.cpp \
     $(NULL)
 endif
 
deleted file mode 100644
--- a/content/events/src/nsDOMApplicationEvent.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
- 
-#include "nsDOMApplicationEvent.h"
-#include "nsContentUtils.h"
-#include "DictionaryHelpers.h"
-#include "nsDOMClassInfoID.h"
- 
-DOMCI_DATA(MozApplicationEvent, nsDOMMozApplicationEvent)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMozApplicationEvent)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mApplication)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mApplication)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMMozApplicationEvent)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMMozApplicationEvent)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozApplicationEvent)
-NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
- 
-NS_IMPL_ADDREF_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent)
-NS_IMPL_RELEASE_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent)
- 
-NS_IMETHODIMP
-nsDOMMozApplicationEvent::GetApplication(mozIDOMApplication** aApplication)
-{
-  NS_IF_ADDREF(*aApplication = mApplication);
-  return NS_OK;
-}
- 
-NS_IMETHODIMP
-nsDOMMozApplicationEvent::InitMozApplicationEvent(const nsAString& aType,
-                                                  bool aCanBubble,
-                                                  bool aCancelable,
-                                                  mozIDOMApplication* aApplication)
-{
-  nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
-  NS_ENSURE_SUCCESS(rv, rv);
- 
-  mApplication = aApplication;
-
-  return NS_OK;
-}
- 
-nsresult
-nsDOMMozApplicationEvent::InitFromCtor(const nsAString& aType, JSContext* aCx, jsval* aVal)
-{
-  mozilla::dom::MozApplicationEventInit d;
-  nsresult rv = d.Init(aCx, aVal);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return InitMozApplicationEvent(aType, d.bubbles, d.cancelable, d.application);
-}
- 
-nsresult
-NS_NewDOMMozApplicationEvent(nsIDOMEvent** aInstancePtrResult,
-                             nsPresContext* aPresContext,
-                             nsEvent* aEvent)
-{
-  nsDOMMozApplicationEvent* e = new nsDOMMozApplicationEvent(aPresContext, aEvent);
-  return CallQueryInterface(e, aInstancePtrResult);
-}
deleted file mode 100644
--- a/content/events/src/nsDOMApplicationEvent.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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/. */
- 
-#ifndef nsDOMApplicationEvent_h__
-#define nsDOMApplicationEvent_h__
- 
-#include "nsIDOMApplicationRegistry.h"
-#include "nsDOMEvent.h"
- 
-class nsDOMMozApplicationEvent : public nsDOMEvent,
-                                 public nsIDOMMozApplicationEvent
-{
-public:
-  nsDOMMozApplicationEvent(nsPresContext* aPresContext, nsEvent* aEvent)
-    : nsDOMEvent(aPresContext, aEvent) {}
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent)
-  // Forward to base class
-  NS_FORWARD_TO_NSDOMEVENT
- 
-  NS_DECL_NSIDOMMOZAPPLICATIONEVENT
- 
-  virtual nsresult InitFromCtor(const nsAString& aType, JSContext* aCx, jsval* aVal);
-
-private:
-  nsCOMPtr<mozIDOMApplication> mApplication;
-};
- 
-#endif // nsDOMContactChangeEvent_h__
\ No newline at end of file
deleted file mode 100644
--- a/content/events/src/nsDOMDeviceLightEvent.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/* 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/. */
-
-#include "nsDOMClassInfoID.h"
-#include "nsDOMDeviceLightEvent.h"
-#include "DictionaryHelpers.h"
-
-NS_IMPL_ADDREF_INHERITED(nsDOMDeviceLightEvent, nsDOMEvent)
-NS_IMPL_RELEASE_INHERITED(nsDOMDeviceLightEvent, nsDOMEvent)
-
-DOMCI_DATA(DeviceLightEvent, nsDOMDeviceLightEvent)
-
-NS_INTERFACE_MAP_BEGIN(nsDOMDeviceLightEvent)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceLightEvent)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceLightEvent)
-NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
-
-NS_IMETHODIMP
-nsDOMDeviceLightEvent::InitDeviceLightEvent(const nsAString & aEventTypeArg,
-                                            bool aCanBubbleArg,
-                                            bool aCancelableArg,
-                                            double aValue)
-{
-  nsresult rv = nsDOMEvent::InitEvent(aEventTypeArg, aCanBubbleArg, aCancelableArg);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mValue = aValue;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsDOMDeviceLightEvent::GetValue(double *aValue)
-{
-  NS_ENSURE_ARG_POINTER(aValue);
-  *aValue = mValue;
-  return NS_OK;
-}
-
-nsresult
-nsDOMDeviceLightEvent::InitFromCtor(const nsAString& aType,
-                                    JSContext* aCx, jsval* aVal)
-{
-  mozilla::dom::DeviceLightEventInit d;
-  nsresult rv = d.Init(aCx, aVal);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return InitDeviceLightEvent(aType, d.bubbles, d.cancelable, d.value);
-}
-
-nsresult
-NS_NewDOMDeviceLightEvent(nsIDOMEvent** aInstancePtrResult,
-                          nsPresContext* aPresContext,
-                          nsEvent *aEvent) 
-{
-  NS_ENSURE_ARG_POINTER(aInstancePtrResult);
-  nsDOMDeviceLightEvent* it = new nsDOMDeviceLightEvent(aPresContext, aEvent);
-  return CallQueryInterface(it, aInstancePtrResult);
-}
deleted file mode 100644
--- a/content/events/src/nsDOMDeviceLightEvent.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* 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/. */
-
-#ifndef nsDOMDeviceLightEvent_h__
-#define nsDOMDeviceLightEvent_h__
-
-#include "nsIDOMDeviceLightEvent.h"
-#include "nsDOMEvent.h"
-
-class nsDOMDeviceLightEvent
- : public nsDOMEvent
- , public nsIDOMDeviceLightEvent
-{
-public:
-
-  nsDOMDeviceLightEvent(nsPresContext* aPresContext, nsEvent* aEvent)
-  : nsDOMEvent(aPresContext, aEvent),
-    mValue(0) {}
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-  // Forward to nsDOMEvent
-  NS_FORWARD_TO_NSDOMEVENT
-
-  // nsIDOMDeviceLightEvent Interface
-  NS_DECL_NSIDOMDEVICELIGHTEVENT
-
-  virtual nsresult InitFromCtor(const nsAString& aType,
-                                JSContext* aCx,
-                                jsval* aVal);
-protected:
-  double mValue;
-};
-
-#endif
deleted file mode 100644
--- a/content/events/src/nsDOMDeviceOrientationEvent.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/* 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/. */
-
-#include "nsDOMClassInfoID.h"
-#include "nsDOMDeviceOrientationEvent.h"
-
-NS_IMPL_ADDREF_INHERITED(nsDOMDeviceOrientationEvent, nsDOMEvent)
-NS_IMPL_RELEASE_INHERITED(nsDOMDeviceOrientationEvent, nsDOMEvent)
-
-DOMCI_DATA(DeviceOrientationEvent, nsDOMDeviceOrientationEvent)
-
-NS_INTERFACE_MAP_BEGIN(nsDOMDeviceOrientationEvent)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceOrientationEvent)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceOrientationEvent)
-NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
-
-NS_IMETHODIMP nsDOMDeviceOrientationEvent::InitDeviceOrientationEvent(const nsAString & aEventTypeArg,
-                                                                      bool aCanBubbleArg,
-                                                                      bool aCancelableArg,
-                                                                      double aAlpha,
-                                                                      double aBeta,
-                                                                      double aGamma,
-                                                                      bool aAbsolute)
-{
-  nsresult rv = nsDOMEvent::InitEvent(aEventTypeArg, aCanBubbleArg, aCancelableArg);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mAlpha = aAlpha;
-  mBeta = aBeta;
-  mGamma = aGamma;
-  mAbsolute = aAbsolute;
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsDOMDeviceOrientationEvent::GetAlpha(double *aAlpha)
-{
-  NS_ENSURE_ARG_POINTER(aAlpha);
-
-  *aAlpha = mAlpha;
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsDOMDeviceOrientationEvent::GetBeta(double *aBeta)
-{
-  NS_ENSURE_ARG_POINTER(aBeta);
-
-  *aBeta = mBeta;
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsDOMDeviceOrientationEvent::GetGamma(double *aGamma)
-{
-  NS_ENSURE_ARG_POINTER(aGamma);
-
-  *aGamma = mGamma;
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsDOMDeviceOrientationEvent::GetAbsolute(bool *aAbsolute)
-{
-  NS_ENSURE_ARG_POINTER(aAbsolute);
-
-  *aAbsolute = mAbsolute;
-  return NS_OK;
-}
-
-nsresult NS_NewDOMDeviceOrientationEvent(nsIDOMEvent** aInstancePtrResult,
-                                         nsPresContext* aPresContext,
-                                         nsEvent *aEvent) 
-{
-  NS_ENSURE_ARG_POINTER(aInstancePtrResult);
-
-  nsDOMDeviceOrientationEvent* it = new nsDOMDeviceOrientationEvent(aPresContext, aEvent);
-  if (nsnull == it) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  return CallQueryInterface(it, aInstancePtrResult);
-}
deleted file mode 100644
--- a/content/events/src/nsDOMDeviceOrientationEvent.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* 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/. */
-
-#ifndef nsDOMDeviceOrientationEvent_h__
-#define nsDOMDeviceOrientationEvent_h__
-
-#include "nsIDOMDeviceOrientationEvent.h"
-#include "nsDOMEvent.h"
-
-class nsDOMDeviceOrientationEvent : public nsDOMEvent,
-                                    public nsIDOMDeviceOrientationEvent
-{
-public:
-
-  nsDOMDeviceOrientationEvent(nsPresContext* aPresContext, nsEvent* aEvent)
-  : nsDOMEvent(aPresContext, aEvent),
-    mAlpha(0),
-    mBeta(0),
-    mGamma(0),
-    mAbsolute(true) {}
-
-  NS_DECL_ISUPPORTS_INHERITED
-
-  // Forward to nsDOMEvent
-  NS_FORWARD_TO_NSDOMEVENT
-
-  // nsIDOMDeviceOrientationEvent Interface
-  NS_DECL_NSIDOMDEVICEORIENTATIONEVENT
-
-protected:
-  double mAlpha, mBeta, mGamma;
-  bool mAbsolute;
-};
-
-#endif
--- a/content/events/test/test_eventctors.html
+++ b/content/events/test/test_eventctors.html
@@ -420,16 +420,34 @@ is(receivedEvent, e, "Wrong event!");
 // DeviceLightEvent
 e = new DeviceLightEvent("hello", {value: 1} );
 ok(e.type, "hello", "Wrong event type!");
 ok(!e.isTrusted, "Event should not be trusted");
 is(e.value, 1, "value should be 1");
 document.dispatchEvent(e);
 is(receivedEvent, e, "Wrong event!");
 
+// DeviceOrientationEvent
+e = new DeviceOrientationEvent("hello");
+ok(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event should not be trusted");
+is(e.alpha, 0);
+is(e.beta, 0);
+is(e.gamma, 0);
+is(e.absolute, false);
+
+e = new DeviceOrientationEvent("hello", { alpha: 1, beta: 2, gamma: 3, absolute: true } );
+ok(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event should not be trusted");
+is(e.alpha, 1);
+is(e.beta, 2);
+is(e.gamma, 3);
+is(e.absolute, true);
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
 
 // MouseEvent
 
 try {
   e = new MouseEvent();
 } catch(exp) {
   ex = true;
 }
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -293,18 +293,16 @@
 #include "nsIDOMMozCSSKeyframesRule.h"
 #include "nsIDOMCSSPrimitiveValue.h"
 #include "nsIDOMCSSStyleRule.h"
 #include "nsIDOMCSSStyleSheet.h"
 #include "nsDOMCSSValueList.h"
 #define MOZ_GENERATED_EVENTS_INCLUDES
 #include "GeneratedEvents.h"
 #undef MOZ_GENERATED_EVENTS_INCLUDES
-#include "nsIDOMDeviceLightEvent.h"
-#include "nsIDOMDeviceOrientationEvent.h"
 #include "nsIDOMDeviceMotionEvent.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMNodeIterator.h"
 #include "nsIDOMTreeWalker.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIDOMXULElement.h"
 #include "nsIDOMXULCommandDispatcher.h"
 #include "nsIDOMCrypto.h"
@@ -810,30 +808,24 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(DragEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(KeyboardEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CompositionEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(PopupBlockedEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  // Device Light
-  NS_DEFINE_CLASSINFO_DATA(DeviceLightEvent, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 #define MOZ_GENERATED_EVENT_LIST
 #define MOZ_GENERATED_EVENT(_event_interface)                \
   NS_DEFINE_CLASSINFO_DATA(_event_interface, nsDOMGenericSH, \
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #include "GeneratedEvents.h"
 #undef MOZ_GENERATED_EVENT_LIST
 
-  // Device Orientation
-  NS_DEFINE_CLASSINFO_DATA(DeviceOrientationEvent, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(DeviceMotionEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(DeviceAcceleration, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(DeviceRotationRate, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // Misc HTML classes
@@ -1636,18 +1628,16 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(MediaQueryList, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MutationObserver, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MutationRecord, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozSettingsEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(MozApplicationEvent, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_CLASSINFO_DATA(MozWifiStatusChangeEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MozWifiConnectionInfoEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(Telephony, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
@@ -1722,20 +1712,18 @@ static const nsContractIDMapData kConstr
     nsIDOMEvent* e = nsnull;                                \
     nsresult rv = NS_NewDOM##_class(&e, nsnull, nsnull);    \
     *aInstancePtrResult = e;                                \
     return rv;                                              \
   }
 
 NS_DEFINE_EVENT_CTOR(Event)
 NS_DEFINE_EVENT_CTOR(MozSettingsEvent)
-NS_DEFINE_EVENT_CTOR(MozApplicationEvent)
 NS_DEFINE_EVENT_CTOR(UIEvent)
 NS_DEFINE_EVENT_CTOR(MouseEvent)
-NS_DEFINE_EVENT_CTOR(DeviceLightEvent)
 #ifdef MOZ_B2G_RIL
 NS_DEFINE_EVENT_CTOR(MozWifiStatusChangeEvent)
 NS_DEFINE_EVENT_CTOR(MozWifiConnectionInfoEvent)
 #endif
 
 #define MOZ_GENERATED_EVENT_LIST
 #define MOZ_GENERATED_EVENT(_event_interface) \
   NS_DEFINE_EVENT_CTOR(_event_interface)
@@ -1770,20 +1758,18 @@ struct nsConstructorFuncMapData
 
 static const nsConstructorFuncMapData kConstructorFuncMap[] =
 {
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(Blob, nsDOMMultipartFile::NewBlob)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(File, nsDOMFileFile::NewFile)
   NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozBlobBuilder, NS_NewBlobBuilder)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(Event)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozSettingsEvent)
-  NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozApplicationEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(UIEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MouseEvent)
-  NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(DeviceLightEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(StorageEvent)
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozWifiStatusChangeEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozWifiConnectionInfoEvent)
 #endif
 #define MOZ_GENERATED_EVENT_LIST
 #define MOZ_GENERATED_EVENT(_event_interface) \
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(_event_interface)
@@ -2595,35 +2581,25 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(PopupBlockedEvent, nsIDOMPopupBlockedEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPopupBlockedEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(DeviceLightEvent, nsIDOMDeviceLightEvent)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceLightEvent)
-    DOM_CLASSINFO_EVENT_MAP_ENTRIES
-  DOM_CLASSINFO_MAP_END
-
 #define MOZ_GENERATED_EVENT_LIST
 #define MOZ_GENERATED_EVENT(_event_interface)                         \
   DOM_CLASSINFO_MAP_BEGIN(_event_interface, nsIDOM##_event_interface) \
     DOM_CLASSINFO_MAP_ENTRY(nsIDOM##_event_interface)                 \
     DOM_CLASSINFO_EVENT_MAP_ENTRIES                                   \
   DOM_CLASSINFO_MAP_END
 #include "GeneratedEvents.h"
 #undef MOZ_GENERATED_EVENT_LIST
 
-  DOM_CLASSINFO_MAP_BEGIN(DeviceOrientationEvent, nsIDOMDeviceOrientationEvent)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceOrientationEvent)
-    DOM_CLASSINFO_EVENT_MAP_ENTRIES
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(DeviceMotionEvent, nsIDOMDeviceMotionEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceMotionEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DeviceAcceleration, nsIDOMDeviceAcceleration)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceAcceleration)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
@@ -4404,21 +4380,16 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMutationRecord)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozSettingsEvent, nsIDOMMozSettingsEvent)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSettingsEvent)
      DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(MozApplicationEvent, nsIDOMMozApplicationEvent)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozApplicationEvent)
-    DOM_CLASSINFO_EVENT_MAP_ENTRIES
-  DOM_CLASSINFO_MAP_END
-
 #ifdef MOZ_B2G_RIL
   DOM_CLASSINFO_MAP_BEGIN(MozWifiStatusChangeEvent, nsIDOMMozWifiStatusChangeEvent)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozWifiStatusChangeEvent)
      DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozWifiConnectionInfoEvent, nsIDOMMozWifiConnectionInfoEvent)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozWifiConnectionInfoEvent)
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -38,22 +38,20 @@ DOMCI_CLASS(Event)
 DOMCI_CLASS(MutationEvent)
 DOMCI_CLASS(UIEvent)
 DOMCI_CLASS(MouseEvent)
 DOMCI_CLASS(MouseScrollEvent)
 DOMCI_CLASS(DragEvent)
 DOMCI_CLASS(KeyboardEvent)
 DOMCI_CLASS(CompositionEvent)
 DOMCI_CLASS(PopupBlockedEvent)
-DOMCI_CLASS(DeviceLightEvent)
 #define MOZ_GENERATED_EVENT_LIST
 #define MOZ_GENERATED_EVENT(_event_interface) DOMCI_CLASS(_event_interface)
 #include "GeneratedEvents.h"
 #undef MOZ_GENERATED_EVENT_LIST
-DOMCI_CLASS(DeviceOrientationEvent)
 DOMCI_CLASS(DeviceMotionEvent)
 DOMCI_CLASS(DeviceAcceleration)
 DOMCI_CLASS(DeviceRotationRate)
 
 // HTML classes
 DOMCI_CLASS(HTMLDocument)
 DOMCI_CLASS(HTMLOptionsCollection)
 DOMCI_CLASS(HTMLCollection)
@@ -504,18 +502,16 @@ DOMCI_CLASS(MozCSSKeyframesRule)
 
 DOMCI_CLASS(MediaQueryList)
 
 DOMCI_CLASS(MutationObserver)
 DOMCI_CLASS(MutationRecord)
 
 DOMCI_CLASS(MozSettingsEvent)
 
-DOMCI_CLASS(MozApplicationEvent)
-
 #ifdef MOZ_B2G_RIL
 DOMCI_CLASS(MozWifiStatusChangeEvent)
 DOMCI_CLASS(MozWifiConnectionInfoEvent)
 DOMCI_CLASS(Telephony)
 DOMCI_CLASS(TelephonyCall)
 DOMCI_CLASS(CallEvent)
 DOMCI_CLASS(MozVoicemail)
 DOMCI_CLASS(MozVoicemailEvent)
--- a/dom/interfaces/apps/Makefile.in
+++ b/dom/interfaces/apps/Makefile.in
@@ -12,11 +12,12 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE         = dom
 XPIDL_MODULE   = dom_apps
 GRE_MODULE     = 1
 
 XPIDLSRCS =                               \
             nsIDOMApplicationRegistry.idl \
             nsIAppsService.idl \
+            nsIDOMMozApplicationEvent.idl \
             $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
+++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
@@ -1,14 +1,13 @@
 /* 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/. */
 
 #include "domstubs.idl"
-#include "nsIDOMEvent.idl"
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMDOMRequest;
 interface nsIArray;
 
 [scriptable, uuid(9583b825-46b1-4e8f-bb48-9fed660a95e6)]
 interface mozIDOMApplication  : nsISupports
 {
@@ -38,32 +37,16 @@ interface mozIDOMApplication  : nsISuppo
    */
   attribute nsIDOMEventListener onprogress;
 
   /* startPoint will be used when several launch_path exists for an app */
   nsIDOMDOMRequest launch([optional] in DOMString startPoint);
   nsIDOMDOMRequest uninstall();
 };
 
-[scriptable, builtinclass, uuid(8f2bfba8-f10e-4f63-a5e0-7a7056e1dbe6)]
-interface nsIDOMMozApplicationEvent : nsIDOMEvent
-{
-  readonly attribute mozIDOMApplication application;
-
-  [noscript] void initMozApplicationEvent(in DOMString aType,
-                                          in boolean aCanBubble,
-                                          in boolean aCancelable,
-                                          in mozIDOMApplication aApplication);
-};
-
-dictionary MozApplicationEventInit : EventInit
-{
-  mozIDOMApplication application;
-};
-
 [scriptable, uuid(bd304874-d532-4e13-8034-544211445583)]
 interface mozIDOMApplicationMgmt : nsISupports
 {
   /**
    * the request will return the all the applications installed. Only accessible
    * to privileged callers.
    */
   nsIDOMDOMRequest getAll();
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/apps/nsIDOMMozApplicationEvent.idl
@@ -0,0 +1,23 @@
+/* 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/. */
+
+#include "nsIDOMEvent.idl"
+
+interface mozIDOMApplication;
+
+[scriptable, builtinclass, uuid(8f2bfba8-f10e-4f63-a5e0-7a7056e1dbe6)]
+interface nsIDOMMozApplicationEvent : nsIDOMEvent
+{
+  readonly attribute mozIDOMApplication application;
+
+  [noscript] void initMozApplicationEvent(in DOMString aType,
+                                          in boolean aCanBubble,
+                                          in boolean aCancelable,
+                                          in mozIDOMApplication aApplication);
+};
+
+dictionary MozApplicationEventInit : EventInit
+{
+  mozIDOMApplication application;
+};
--- a/dom/interfaces/events/nsIDOMDeviceOrientationEvent.idl
+++ b/dom/interfaces/events/nsIDOMDeviceOrientationEvent.idl
@@ -36,8 +36,15 @@ interface nsIDOMDeviceOrientationEvent :
    */
 
   readonly attribute double alpha;
   readonly attribute double beta;
   readonly attribute double gamma;
   readonly attribute boolean absolute;
 };
 
+dictionary DeviceOrientationEventInit : EventInit
+{
+  double alpha;
+  double beta;
+  double gamma;
+  boolean absolute;
+};
--- a/dom/interfaces/events/nsIDOMEvent.idl
+++ b/dom/interfaces/events/nsIDOMEvent.idl
@@ -199,20 +199,16 @@ nsresult
 NS_NewDOMKeyboardEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsKeyEvent *aEvent);
 nsresult
 NS_NewDOMCompositionEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsCompositionEvent *aEvent);
 nsresult
 NS_NewDOMMutationEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsMutationEvent* aEvent);
 nsresult
 NS_NewDOMPopupBlockedEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent);
 nsresult
-NS_NewDOMDeviceOrientationEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent);
-nsresult
-NS_NewDOMDeviceLightEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent);
-nsresult
 NS_NewDOMDeviceMotionEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent);
 nsresult
 NS_NewDOMTextEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsTextEvent* aEvent);
 nsresult
 NS_NewDOMBeforeUnloadEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent);
 nsresult
 NS_NewDOMSVGEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsEvent* aEvent);
 nsresult
--- a/js/xpconnect/src/dictionary_helper_gen.conf
+++ b/js/xpconnect/src/dictionary_helper_gen.conf
@@ -11,26 +11,25 @@ dictionaries = [
      [ 'IDBIndexParameters', 'nsIIDBObjectStore.idl' ],
      [ 'StorageEventInit', 'nsIDOMStorageEvent.idl' ],
      [ 'BlobPropertyBag', 'nsIDOMFile.idl' ],
      [ 'MutationObserverInit', 'nsIDOMMutationObserver.idl' ],
      [ 'SettingsEventInit', 'nsIDOMSettingsManager.idl' ],
      [ 'WifiConnectionInfoEventInit', 'nsIWifiEventInits.idl' ],
      [ 'WifiStatusChangeEventInit', 'nsIWifiEventInits.idl' ],
      [ 'GeoPositionOptions', 'nsIDOMGeoGeolocation.idl' ],
-     [ 'DeviceLightEventInit', 'nsIDOMDeviceLightEvent.idl' ],
-     [ 'MozApplicationEventInit', 'nsIDOMApplicationRegistry.idl' ],
      [ 'DOMFileMetadataParameters', 'nsIDOMLockedFile.idl' ],
      [ 'XMLHttpRequestParameters', 'nsIXMLHttpRequest.idl' ],
      [ 'DeviceStorageEnumerationParameters', 'nsIDOMDeviceStorage.idl' ]
    ]
 
 # include file names
 special_includes = [
     'nsContentUtils.h',
-    'XPCQuickStubs.h'
+    'XPCQuickStubs.h',
+    'nsIDOMApplicationRegistry.h'
   ]
 
 # name of the type to not include using #include "typename.h"
 exclude_automatic_type_include = [
     'nsISupports',
     'mozIDOMApplication'
   ]
--- a/js/xpconnect/src/dictionary_helper_gen.py
+++ b/js/xpconnect/src/dictionary_helper_gen.py
@@ -299,16 +299,18 @@ def write_getter(a, iface, fd):
                  % (get_jsid(a.name), a.name))
     else:
         fd.write("    NS_ENSURE_STATE(JS_GetPropertyById(aCx, aObj, %s, &v));\n"
                  % get_jsid(a.name))
     if realtype.count("bool"):
         fd.write("    JSBool b;\n")
         fd.write("    MOZ_ALWAYS_TRUE(JS_ValueToBoolean(aCx, v, &b));\n")
         fd.write("    aDict.%s = b;\n" % a.name)
+    elif realtype.count("PRUint32"):
+        fd.write("    NS_ENSURE_STATE(JS_ValueToECMAUint32(aCx, v, &aDict.%s));\n" % a.name)
     elif realtype.count("PRInt32"):
         fd.write("    NS_ENSURE_STATE(JS_ValueToECMAInt32(aCx, v, &aDict.%s));\n" % a.name)
     elif realtype.count("double"):
         fd.write("    NS_ENSURE_STATE(JS_ValueToNumber(aCx, v, &aDict.%s));\n" % a.name)
     elif realtype.count("float"):
         fd.write("    double d;\n")
         fd.write("    NS_ENSURE_STATE(JS_ValueToNumber(aCx, v, &d));")
         fd.write("    aDict.%s = (float) d;\n" % a.name)
--- a/js/xpconnect/src/event_impl_gen.conf.in
+++ b/js/xpconnect/src/event_impl_gen.conf.in
@@ -10,21 +10,26 @@
 simple_events = [
     'DeviceProximityEvent',
     'UserProximityEvent',
     'CustomEvent',
     'PageTransitionEvent',
     'PopStateEvent',
     'HashChangeEvent',
     'CloseEvent',
-    'MozContactChangeEvent'
+    'MozContactChangeEvent',
+    'DeviceOrientationEvent',
+    'DeviceLightEvent',
+    'MozApplicationEvent'
   ]
 
 """ include file names """
 special_includes = [
     'DictionaryHelpers.h',
-    'nsContentUtils.h'
+    'nsContentUtils.h',
+    'nsIDOMApplicationRegistry.h'
   ]
 
 """ name of the type to not include using #include "typename.h" """
 exclude_automatic_type_include = [
-    'nsISupports'
+    'nsISupports',
+    'mozIDOMApplication'
   ]
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -6093,16 +6093,17 @@ var RemoteDebugger = {
     this._stop();
     this._start();
   },
 
   _start: function rd_start() {
     try {
       if (!DebuggerServer.initialized) {
         DebuggerServer.init(this._allowConnection);
+        DebuggerServer.addBrowserActors();
         DebuggerServer.addActors("chrome://browser/content/dbg-browser-actors.js");
       }
 
       let port = this._getPort();
       DebuggerServer.openListener(port);
       dump("Remote debugger listening on port " + port);
     } catch(e) {
       dump("Remote debugger didn't start: " + e);
--- a/mobile/android/chrome/content/dbg-browser-actors.js
+++ b/mobile/android/chrome/content/dbg-browser-actors.js
@@ -1,435 +1,119 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 "use strict";
 /**
- * Fennec-specific actors.
+ * Fennec-specific root actor that extends BrowserRootActor and overrides some
+ * of its methods.
  */
 
-var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"]
-  .getService(Ci.nsIWindowMediator);
-
+/**
+ * The function that creates the root actor. DebuggerServer expects to find this
+ * function in the loaded actors in order to initialize properly.
+ */
 function createRootActor(aConnection) {
-  return new BrowserRootActor(aConnection);
+  return new FennecRootActor(aConnection);
 }
 
 /**
  * Creates the root actor that client-server communications always start with.
  * The root actor is responsible for the initial 'hello' packet and for
  * responding to a 'listTabs' request that produces the list of currently open
  * tabs.
  *
  * @param aConnection DebuggerServerConnection
  *        The conection to the client.
  */
-function BrowserRootActor(aConnection) {
-  this.conn = aConnection;
-  this._tabActors = new WeakMap();
-  this._tabActorPool = null;
-  this._actorFactories = null;
-
-  this.onTabClosed = this.onTabClosed.bind(this);
-  windowMediator.addListener(this);
+function FennecRootActor(aConnection) {
+  BrowserRootActor.call(this, aConnection);
 }
 
-BrowserRootActor.prototype = {
-  /**
-   * Return a 'hello' packet as specified by the Remote Debugging Protocol.
-   */
-  sayHello: function BRA_sayHello() {
-    return { from: "root",
-             applicationType: "browser",
-             traits: [] };
-  },
+FennecRootActor.prototype = new BrowserRootActor();
 
-  /**
-   * Disconnects the actor from the browser window.
-   */
-  disconnect: function BRA_disconnect() {
-    windowMediator.removeListener(this);
+/**
+ * Handles the listTabs request.  Builds a list of actors
+ * for the tabs running in the process.  The actors will survive
+ * until at least the next listTabs request.
+ */
+FennecRootActor.prototype.onListTabs = function FRA_onListTabs() {
+  // Get actors for all the currently-running tabs (reusing
+  // existing actors where applicable), and store them in
+  // an ActorPool.
 
-    // We may have registered event listeners on browser windows to
-    // watch for tab closes, remove those.
-    let win = windowMediator.getMostRecentWindow("navigator:browser");
-    this.unwatchWindow(win);
-
-    // Signal our imminent shutdown.
-    let evt = win.document.createEvent("Event");
-    evt.initEvent("Debugger:Shutdown", true, false);
-    win.document.documentElement.dispatchEvent(evt);
-  },
+  let actorPool = new ActorPool(this.conn);
+  let actorList = [];
 
-  /**
-   * Handles the listTabs request.  Builds a list of actors
-   * for the tabs running in the process.  The actors will survive
-   * until at least the next listTabs request.
-   */
-  onListTabs: function BRA_onListTabs() {
-    // Get actors for all the currently-running tabs (reusing
-    // existing actors where applicable), and store them in
-    // an ActorPool.
+  let win = windowMediator.getMostRecentWindow("navigator:browser");
+  this.browser = win.BrowserApp.selectedBrowser;
 
-    let actorPool = new ActorPool(this.conn);
-    let actorList = [];
-
-    let win = windowMediator.getMostRecentWindow("navigator:browser");
-    this.browser = win.BrowserApp.selectedBrowser;
-
-    // Watch the window for tab closes so we can invalidate
-    // actors as needed.
-    this.watchWindow(win);
+  // Watch the window for tab closes so we can invalidate
+  // actors as needed.
+  this.watchWindow(win);
 
-    let tabs = win.BrowserApp.tabs;
-    let selected;
-
-    for each (let tab in tabs) {
-      let browser = tab.browser;
-
-      if (browser == this.browser) {
-        selected = actorList.length;
-      }
+  let tabs = win.BrowserApp.tabs;
+  let selected;
 
-      let actor = this._tabActors.get(browser);
-      if (!actor) {
-        actor = new BrowserTabActor(this.conn, browser);
-        actor.parentID = this.actorID;
-        this._tabActors.set(browser, actor);
-      }
+  for each (let tab in tabs) {
+    let browser = tab.browser;
 
-      actorPool.addActor(actor);
-      actorList.push(actor);
+    if (browser == this.browser) {
+      selected = actorList.length;
     }
 
-    // Now drop the old actorID -> actor map.  Actors that still
-    // mattered were added to the new map, others will go
-    // away.
-    if (this._tabActorPool) {
-      this.conn.removeActorPool(this._tabActorPool);
+    let actor = this._tabActors.get(browser);
+    if (!actor) {
+      actor = new BrowserTabActor(this.conn, browser);
+      actor.parentID = this.actorID;
+      this._tabActors.set(browser, actor);
     }
 
-    this._tabActorPool = actorPool;
-    this.conn.addActorPool(this._tabActorPool);
-
-    return { "from": "root",
-             "selected": selected,
-             "tabs": [actor.grip()
-                      for each (actor in actorList)] };
-  },
-
-  /**
-   * Watch a window that was visited during onListTabs for
-   * tab closures.
-   */
-  watchWindow: function BRA_watchWindow(aWindow) {
-    let tabContainer = aWindow.document.getElementById("browsers");
-    tabContainer.addEventListener("TabClose",
-                                  this.onTabClosed,
-                                  false);
-  },
-
-  /**
-   * Stop watching a window for tab closes.
-   */
-  unwatchWindow: function BRA_unwatchWindow(aWindow) {
-    let tabContainer = aWindow.document.getElementById("browsers");
-    tabContainer.removeEventListener("TabClose", this.onTabClosed);
-    this.exitTabActor(aWindow);
-  },
+    actorPool.addActor(actor);
+    actorList.push(actor);
+  }
 
-  /**
-   * When a tab is closed, exit its tab actor.  The actor
-   * will be dropped at the next listTabs request.
-   */
-  onTabClosed: function BRA_onTabClosed(aEvent) {
-    this.exitTabActor(aEvent.target.browser);
-  },
+  // Now drop the old actorID -> actor map.  Actors that still
+  // mattered were added to the new map, others will go
+  // away.
+  if (this._tabActorPool) {
+    this.conn.removeActorPool(this._tabActorPool);
+  }
 
-  /**
-   * Exit the tab actor of the specified tab.
-   */
-  exitTabActor: function BRA_exitTabActor(aWindow) {
-    let actor = this._tabActors.get(aWindow);
-    if (actor) {
-      actor.exit();
-    }
-  },
+  this._tabActorPool = actorPool;
+  this.conn.addActorPool(this._tabActorPool);
 
-  // nsIWindowMediatorListener
-  onWindowTitleChange: function BRA_onWindowTitleChange(aWindow, aTitle) { },
-  onOpenWindow: function BRA_onOpenWindow(aWindow) { },
-  onCloseWindow: function BRA_onCloseWindow(aWindow) {
-    if (aWindow.BrowserApp) {
-      this.unwatchWindow(aWindow);
-    }
-  }
-}
-
-/**
- * The request types this actor can handle.
- */
-BrowserRootActor.prototype.requestTypes = {
-  "listTabs": BrowserRootActor.prototype.onListTabs
+  return { "from": "root",
+           "selected": selected,
+           "tabs": [actor.grip()
+                    for each (actor in actorList)] };
 };
 
 /**
- * Creates a tab actor for handling requests to a browser tab, like attaching
- * and detaching.
- *
- * @param aConnection DebuggerServerConnection
- *        The conection to the client.
- * @param aBrowser browser
- *        The browser instance that contains this tab.
+ * Return the tab container for the specified window.
  */
-function BrowserTabActor(aConnection, aBrowser)
-{
-  this.conn = aConnection;
-  this._browser = aBrowser;
-
-  this._onWindowCreated = this.onWindowCreated.bind(this);
-}
-
-// XXX (bug 710213): BrowserTabActor attach/detach/exit/disconnect is a
-// *complete* mess, needs to be rethought asap.
-
-BrowserTabActor.prototype = {
-  get browser() { return this._browser; },
-
-  get exited() { return !this._browser; },
-  get attached() { return !!this._attached },
-
-  _tabPool: null,
-  get tabActorPool() { return this._tabPool; },
-
-  _contextPool: null,
-  get contextActorPool() { return this._contextPool; },
-
-  /**
-   * Add the specified breakpoint to the default actor pool connection, in order
-   * to be alive as long as the server is.
-   *
-   * @param BreakpointActor aActor
-   *        The actor object.
-   */
-  addToBreakpointPool: function BTA_addToBreakpointPool(aActor) {
-    this.conn.addActor(aActor);
-  },
-
-  /**
-   * Remove the specified breakpint from the default actor pool.
-   *
-   * @param string aActor
-   *        The actor ID.
-   */
-  removeFromBreakpointPool: function BTA_removeFromBreakpointPool(aActor) {
-    this.conn.removeActor(aActor);
-  },
-
-  actorPrefix: "tab",
-
-  grip: function BTA_grip() {
-    dbg_assert(!this.exited,
-               "grip() shouldn't be called on exited browser actor.");
-    dbg_assert(this.actorID,
-               "tab should have an actorID.");
-    return { actor: this.actorID,
-             title: this._browser.contentTitle,
-             url: this._browser.currentURI.spec }
-  },
-
-  /**
-   * Called when the actor is removed from the connection.
-   */
-  disconnect: function BTA_disconnect() {
-    this._detach();
-  },
-
-  /**
-   * Called by the root actor when the underlying tab is closed.
-   */
-  exit: function BTA_exit() {
-    if (this.exited) {
-      return;
-    }
-
-    if (this.attached) {
-      this._detach();
-      this.conn.send({ from: this.actorID,
-                       type: "tabDetached" });
-    }
-
-    this._browser = null;
-  },
-
-  /**
-   * Does the actual work of attching to a tab.
-   */
-  _attach: function BTA_attach() {
-    if (this._attached) {
-      return;
-    }
-
-    // Create a pool for tab-lifetime actors.
-    dbg_assert(!this._tabPool, "Shouldn't have a tab pool if we weren't attached.");
-    this._tabPool = new ActorPool(this.conn);
-    this.conn.addActorPool(this._tabPool);
-
-    // ... and a pool for context-lifetime actors.
-    this._pushContext();
-
-    // Watch for globals being created in this tab.
-    this._browser.addEventListener("DOMWindowCreated", this._onWindowCreated, true);
-
-    this._attached = true;
-  },
+FennecRootActor.prototype.getTabContainer = function FRA_getTabContainer(aWindow) {
+  return aWindow.document.getElementById("browsers");
+};
 
-  /**
-   * Creates a thread actor and a pool for context-lifetime actors. It then sets
-   * up the content window for debugging.
-   */
-  _pushContext: function BTA_pushContext() {
-    dbg_assert(!this._contextPool, "Can't push multiple contexts");
-
-    this._contextPool = new ActorPool(this.conn);
-    this.conn.addActorPool(this._contextPool);
-
-    this.threadActor = new ThreadActor(this);
-    this._addDebuggees(this._browser.contentWindow.wrappedJSObject);
-    this._contextPool.addActor(this.threadActor);
-  },
-
-  /**
-   * Add the provided window and all windows in its frame tree as debuggees.
-   */
-  _addDebuggees: function BTA__addDebuggees(aWindow) {
-    this.threadActor.addDebuggee(aWindow);
-    let frames = aWindow.frames;
-    for (let i = 0; i < frames.length; i++) {
-      this._addDebuggees(frames[i]);
-    }
-  },
-
-  /**
-   * Exits the current thread actor and removes the context-lifetime actor pool.
-   * The content window is no longer being debugged after this call.
-   */
-  _popContext: function BTA_popContext() {
-    dbg_assert(!!this._contextPool, "No context to pop.");
-
-    this.conn.removeActorPool(this._contextPool);
-    this._contextPool = null;
-    this.threadActor.exit();
-    this.threadActor = null;
-  },
-
-  /**
-   * Does the actual work of detaching from a tab.
-   */
-  _detach: function BTA_detach() {
-    if (!this.attached) {
-      return;
-    }
-
-    this._browser.removeEventListener("DOMWindowCreated", this._onWindowCreated, true);
-
-    this._popContext();
-
-    // Shut down actors that belong to this tab's pool.
-    this.conn.removeActorPool(this._tabPool);
-    this._tabPool = null;
-
-    this._attached = false;
-  },
-
-  // Protocol Request Handlers
+/**
+ * When a tab is closed, exit its tab actor.  The actor
+ * will be dropped at the next listTabs request.
+ */
+FennecRootActor.prototype.onTabClosed = function FRA_onTabClosed(aEvent) {
+  this.exitTabActor(aEvent.target.browser);
+};
 
-  onAttach: function BTA_onAttach(aRequest) {
-    if (this.exited) {
-      return { type: "exited" };
-    }
-
-    this._attach();
-
-    return { type: "tabAttached", threadActor: this.threadActor.actorID };
-  },
-
-  onDetach: function BTA_onDetach(aRequest) {
-    if (!this.attached) {
-      return { error: "wrongState" };
-    }
-
-    this._detach();
-
-    return { type: "detached" };
-  },
-
-  /**
-   * Prepare to enter a nested event loop by disabling debuggee events.
-   */
-  preNest: function BTA_preNest() {
-    if (!this._browser) {
-      // The tab is already closed.
-      return;
-    }
-    let windowUtils = this._browser.contentWindow
-                          .QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils);
-    windowUtils.suppressEventHandling(true);
-    windowUtils.suspendTimeouts();
-  },
-
-  /**
-   * Prepare to exit a nested event loop by enabling debuggee events.
-   */
-  postNest: function BTA_postNest(aNestData) {
-    if (!this._browser) {
-      // The tab is already closed.
-      return;
-    }
-    let windowUtils = this._browser.contentWindow
-                          .QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils);
-    windowUtils.resumeTimeouts();
-    windowUtils.suppressEventHandling(false);
-  },
-
-  /**
-   * Handle location changes, by sending a tabNavigated notification to the
-   * client.
-   */
-  onWindowCreated: function BTA_onWindowCreated(evt) {
-    if (evt.target === this._browser.contentDocument) {
-      if (this._attached) {
-        this.conn.send({ from: this.actorID, type: "tabNavigated",
-                         url: this._browser.contentDocument.URL });
-      }
-    }
+// nsIWindowMediatorListener
+FennecRootActor.prototype.onCloseWindow = function FRA_onCloseWindow(aWindow) {
+  if (aWindow.BrowserApp) {
+    this.unwatchWindow(aWindow);
   }
 };
 
 /**
  * The request types this actor can handle.
  */
-BrowserTabActor.prototype.requestTypes = {
-  "attach": BrowserTabActor.prototype.onAttach,
-  "detach": BrowserTabActor.prototype.onDetach
+FennecRootActor.prototype.requestTypes = {
+  "listTabs": FennecRootActor.prototype.onListTabs
 };
-
-/**
- * Registers handlers for new request types defined dynamically. This is used
- * for example by add-ons to augment the functionality of the tab actor.
- *
- * @param aName string
- *        The name of the new request type.
- * @param aFunction function
- *        The handler for this request type.
- */
-DebuggerServer.addTabRequest = function DS_addTabRequest(aName, aFunction) {
-  BrowserTabActor.prototype.requestTypes[aName] = function(aRequest) {
-    if (!this.attached) {
-      return { error: "wrongState" };
-    }
-    return aFunction(this, aRequest);
-  }
-};
--- a/services/common/storageserver.js
+++ b/services/common/storageserver.js
@@ -127,26 +127,38 @@ ServerBSO.prototype = {
     let body;
 
     function sendResponse() {
       response.setStatusLine(request.httpVersion, code, status);
       writeHttpBody(response, body);
     }
 
     if (request.hasHeader("x-if-modified-since")) {
-      let headerModified = parseInt(request.getHeader("x-if-modified-since"));
+      let headerModified = parseInt(request.getHeader("x-if-modified-since"),
+                                    10);
       CommonUtils.ensureMillisecondsTimestamp(headerModified);
 
       if (headerModified >= this.modified) {
         code = 304;
         status = "Not Modified";
 
         sendResponse();
         return;
       }
+    } else if (request.hasHeader("x-if-unmodified-since")) {
+      let requestModified = parseInt(request.getHeader("x-if-unmodified-since"),
+                                     10);
+      let serverModified = this.modified;
+
+      if (serverModified > requestModified) {
+        code = 412;
+        status = "Precondition Failed";
+        sendResponse();
+        return;
+      }
     }
 
     if (!this.deleted) {
       body = JSON.stringify(this.toJSON());
       response.setHeader("Content-Type", "application/json", false);
       response.setHeader("X-Last-Modified", "" + this.modified, false);
     } else {
       code = 404;
@@ -406,36 +418,16 @@ StorageServerCollection.prototype = {
     }
 
     if (options.older) {
       if (bso.modified >= options.older) {
         return false;
       }
     }
 
-    if (options.index_above) {
-      if (bso.sortindex === undefined) {
-        return false;
-      }
-
-      if (bso.sortindex <= options.index_above) {
-        return false;
-      }
-    }
-
-    if (options.index_below) {
-      if (bso.sortindex === undefined) {
-        return false;
-      }
-
-      if (bso.sortindex >= options.index_below) {
-        return false;
-      }
-    }
-
     return true;
   },
 
   count: function count(options) {
     options = options || {};
     let c = 0;
     for (let [id, bso] in Iterator(this._bsos)) {
       if (bso.modified && this._inResultSet(bso, options)) {
@@ -607,20 +599,21 @@ StorageServerCollection.prototype = {
   parseOptions: function parseOptions(request) {
     let options = {};
 
     for each (let chunk in request.queryString.split("&")) {
       if (!chunk) {
         continue;
       }
       chunk = chunk.split("=");
+      let key = decodeURIComponent(chunk[0]);
       if (chunk.length == 1) {
-        options[chunk[0]] = "";
+        options[key] = "";
       } else {
-        options[chunk[0]] = chunk[1];
+        options[key] = decodeURIComponent(chunk[1]);
       }
     }
 
     if (options.ids) {
       options.ids = options.ids.split(",");
     }
 
     if (options.newer) {
@@ -636,28 +629,16 @@ StorageServerCollection.prototype = {
       if (!isInteger(options.older)) {
         throw HTTP_400;
       }
 
       CommonUtils.ensureMillisecondsTimestamp(options.older);
       options.older = parseInt(options.older, 10);
     }
 
-    if (options.index_above) {
-      if (!isInteger(options.index_above)) {
-        throw HTTP_400;
-      }
-    }
-
-    if (options.index_below) {
-      if (!isInteger(options.index_below)) {
-        throw HTTP_400;
-      }
-    }
-
     if (options.limit) {
       if (!isInteger(options.limit)) {
         throw HTTP_400;
       }
 
       options.limit = parseInt(options.limit, 10);
     }
 
@@ -678,16 +659,26 @@ StorageServerCollection.prototype = {
         }
       }
 
       if (requestModified >= newestBSO) {
         response.setHeader("X-Last-Modified", "" + newestBSO);
         response.setStatusLine(request.httpVersion, 304, "Not Modified");
         return;
       }
+    } else if (request.hasHeader("x-if-unmodified-since")) {
+      let requestModified = parseInt(request.getHeader("x-if-unmodified-since"),
+                                     10);
+      let serverModified = this.timestamp;
+
+      if (serverModified > requestModified) {
+        response.setHeader("X-Last-Modified", "" + serverModified);
+        response.setStatusLine(request.httpVersion, 412, "Precondition Failed");
+        return;
+      }
     }
 
     if (options.full) {
       data = data.map(function map(bso) {
         return bso.toJSON();
       });
     } else {
       data = data.map(function map(bso) {
@@ -705,19 +696,17 @@ StorageServerCollection.prototype = {
         throw HTTP_406;
       }
     }
 
     let body;
     if (newlines) {
       response.setHeader("Content-Type", "application/newlines", false);
       let normalized = data.map(function map(d) {
-        let result = JSON.stringify(d);
-
-        return result.replace("\n", "\\u000a");
+        return JSON.stringify(d);
       });
 
       body = normalized.join("\n") + "\n";
     } else {
       response.setHeader("Content-Type", "application/json", false);
       body = JSON.stringify({items: data});
     }
 
@@ -750,20 +739,19 @@ StorageServerCollection.prototype = {
       }
 
       if (!Array.isArray(input)) {
         this._log.info("Input JSON type not an array!");
         return sendMozSvcError(request, response, "8");
       }
     } else if (inputMediaType == "application/newlines") {
       for each (let line in inputBody.split("\n")) {
-        let json = line.replace("\\u000a", "\n");
         let record;
         try {
-          record = JSON.parse(json);
+          record = JSON.parse(line);
         } catch (ex) {
           this._log.info("JSON parse error on line!");
           return sendMozSvcError(request, response, "8");
         }
 
         input.push(record);
       }
     } else {
--- a/services/common/storageservice.js
+++ b/services/common/storageservice.js
@@ -865,19 +865,22 @@ StorageServiceRequest.prototype = {
         this._log.error("415 HTTP response seen from server! This should " +
                         "never happen!");
         this._error = new StorageServiceRequestError();
         this._error.client = new Error("415 Unsupported Media Type received!");
         callOnComplete();
         return;
       }
 
-      if (response.status == 503) {
+      if (response.status >= 500 && response.status <= 599) {
+        this._log.error(response.status + " seen from server!");
         this._error = new StorageServiceRequestError();
-        this._error.server = new Error("503 Received.");
+        this._error.server = new Error(response.status + " status code.");
+        callOnComplete();
+        return;
       }
 
       callOnComplete();
 
     } catch (ex) {
       this._clientError = ex;
       this._log.info("Exception when processing _onComplete: " + ex);
 
@@ -989,30 +992,16 @@ StorageCollectionGetRequest.prototype = 
     if (value) {
       this._namedArgs.full = "1";
     } else {
       delete this._namedArgs["full"];
     }
   },
 
   /**
-   * Only retrieve BSOs whose sortindex is higher than this integer value.
-   */
-  set index_above(value) {
-    this._namedArgs.index_above = value;
-  },
-
-  /**
-   * Only retrieve BSOs whose sortindex is lower than this integer value.
-   */
-  set index_below(value) {
-    this._namedArgs.index_below = value;
-  },
-
-  /**
    * Limit the max number of returned BSOs to this integer number.
    */
   set limit(value) {
     this._namedArgs.limit = value;
   },
 
   /**
    * If set with any value, sort the results based on modification time, oldest
@@ -1079,25 +1068,31 @@ StorageCollectionGetRequest.prototype = 
 /**
  * Represents a request that sets data in a collection
  *
  * Instances of this type are returned by StorageServiceClient.setBSOs().
  */
 function StorageCollectionSetRequest() {
   StorageServiceRequest.call(this);
 
-  this._lines = [];
-  this._size  = 0;
+  this.size = 0;
 
-  this.successfulIDs = new Set();
-  this.failures      = new Map();
+  // TODO Bug 775781 convert to Set and Map once iterable.
+  this.successfulIDs = [];
+  this.failures      = {};
+
+  this._lines = [];
 }
 StorageCollectionSetRequest.prototype = {
   __proto__: StorageServiceRequest.prototype,
 
+  get count() {
+    return this._lines.length;
+  },
+
   /**
    * Add a BasicStorageObject to this request.
    *
    * Please note that the BSO content is retrieved when the BSO is added to
    * the request. If the BSO changes after it is added to a request, those
    * changes will not be reflected in the request.
    *
    * @param bso
@@ -1107,44 +1102,395 @@ StorageCollectionSetRequest.prototype = 
     if (!bso instanceof BasicStorageObject) {
       throw new Error("argument must be a BasicStorageObject instance.");
     }
 
     if (!bso.id) {
       throw new Error("Passed BSO must have id defined.");
     }
 
-    let line = JSON.stringify(bso).replace("\n", "\u000a");
+    this.addLine(JSON.stringify(bso));
+  },
 
+  /**
+   * Add a BSO (represented by its serialized newline-delimited form).
+   *
+   * You probably shouldn't use this. It is used for batching.
+   */
+  addLine: function addLine(line) {
     // This is off by 1 in the larger direction. We don't care.
-    this._size += line.length + "\n".length;
+    this.size += line.length + 1;
     this._lines.push(line);
   },
 
   _onDispatch: function _onDispatch() {
     this._data = this._lines.join("\n");
+    this.size = this._data.length;
   },
 
   _completeParser: function _completeParser(response) {
     let result = JSON.parse(response.body);
 
     for (let id of result.success) {
-      this.successfulIDs.add(id);
+      this.successfulIDs.push(id);
     }
 
     this.allSucceeded = true;
 
-    for (let [id, reasons] in result.failed) {
+    for (let [id, reasons] in Iterator(result.failed)) {
       this.failures[id] = reasons;
       this.allSucceeded = false;
     }
   },
 };
 
 /**
+ * Represents a batch upload of BSOs to an individual collection.
+ *
+ * This is a more intelligent way to upload may BSOs to the server. It will
+ * split the uploaded data into multiple requests so size limits, etc aren't
+ * exceeded.
+ *
+ * Once a client obtains an instance of this type, it calls `addBSO` for each
+ * BSO to be uploaded. When the client is done providing BSOs to be uploaded,
+ * it calls `finish`. When `finish` is called, no more BSOs can be added to the
+ * batch. When all requests created from this batch have finished, the callback
+ * provided to `finish` will be invoked.
+ *
+ * Clients can also explicitly flush pending outgoing BSOs via `flush`. This
+ * allows callers to control their own batching/chunking.
+ *
+ * Interally, this maintains a queue of StorageCollectionSetRequest to be
+ * issued. At most one request is allowed to be in-flight at once. This is to
+ * avoid potential conflicts on the server. And, in the case of conditional
+ * requests, it prevents requests from being declined due to the server being
+ * updated by another request issued by us.
+ *
+ * If a request errors for any reason, all queued uploads are abandoned and the
+ * `finish` callback is invoked as soon as possible. The `successfulIDs` and
+ * `failures` properties will contain data from all requests that had this
+ * response data. In other words, the IDs have BSOs that were never sent to the
+ * server are not lumped in to either property.
+ *
+ * Requests can be made conditional by setting `locallyModifiedVersion` to the
+ * most recent version of server data. As responses from the server are seen,
+ * the last server version is carried forward to subsequent requests.
+ *
+ * The server version from the last request is available in the
+ * `serverModifiedVersion` property. It should only be accessed during or
+ * after the callback passed to `finish`.
+ *
+ * @param client
+ *        (StorageServiceClient) Client instance to use for uploading.
+ *
+ * @param collection
+ *        (string) Collection the batch operation will upload to.
+ */
+function StorageCollectionBatchedSet(client, collection) {
+  this.client     = client;
+  this.collection = collection;
+
+  this._log = client._log;
+
+  this.locallyModifiedVersion = null;
+  this.serverModifiedVersion  = null;
+
+  // TODO Bug 775781 convert to Set and Map once iterable.
+  this.successfulIDs = [];
+  this.failures      = {};
+
+  // Request currently being populated.
+  this._stagingRequest = client.setBSOs(this.collection);
+
+  // Requests ready to be sent over the wire.
+  this._outgoingRequests = [];
+
+  // Whether we are waiting for a response.
+  this._requestInFlight = false;
+
+  this._onFinishCallback = null;
+  this._finished         = false;
+  this._errorEncountered = false;
+}
+StorageCollectionBatchedSet.prototype = {
+  /**
+   * Add a BSO to be uploaded as part of this batch.
+   */
+  addBSO: function addBSO(bso) {
+    if (this._errorEncountered) {
+      return;
+    }
+
+    let line = JSON.stringify(bso);
+
+    if (line.length > this.client.REQUEST_SIZE_LIMIT) {
+      throw new Error("BSO is larger than allowed limit: " + line.length +
+                      " > " + this.client.REQUEST_SIZE_LIMIT);
+    }
+
+    if (this._stagingRequest.size + line.length > this.client.REQUEST_SIZE_LIMIT) {
+      this._log.debug("Sending request because payload size would be exceeded");
+      this._finishStagedRequest();
+
+      this._stagingRequest.addLine(line);
+      return;
+    }
+
+    // We are guaranteed to fit within size limits.
+    this._stagingRequest.addLine(line);
+
+    if (this._stagingRequest.count >= this.client.REQUEST_BSO_COUNT_LIMIT) {
+      this._log.debug("Sending request because BSO count threshold reached.");
+      this._finishStagedRequest();
+      return;
+    }
+  },
+
+  finish: function finish(cb) {
+    if (this._finished) {
+      throw new Error("Batch request has already been finished.");
+    }
+
+    this.flush();
+
+    this._onFinishCallback = cb;
+    this._finished = true;
+    this._stagingRequest = null;
+  },
+
+  flush: function flush() {
+    if (this._finished) {
+      throw new Error("Batch request has been finished.");
+    }
+
+    if (!this._stagingRequest.count) {
+      return;
+    }
+
+    this._finishStagedRequest();
+  },
+
+  _finishStagedRequest: function _finishStagedRequest() {
+    this._outgoingRequests.push(this._stagingRequest);
+    this._sendOutgoingRequest();
+    this._stagingRequest = this.client.setBSOs(this.collection);
+  },
+
+  _sendOutgoingRequest: function _sendOutgoingRequest() {
+    if (this._requestInFlight || this._errorEncountered) {
+      return;
+    }
+
+    if (!this._outgoingRequests.length) {
+      return;
+    }
+
+    let request = this._outgoingRequests.shift();
+
+    if (this.locallyModifiedVersion) {
+      request.locallyModifiedVersion = this.locallyModifiedVersion;
+    }
+
+    request.dispatch(this._onBatchComplete.bind(this));
+    this._requestInFlight = true;
+  },
+
+  _onBatchComplete: function _onBatchComplete(error, request) {
+    this._requestInFlight = false;
+
+    this.serverModifiedVersion = request.serverTime;
+
+    // Only update if we had a value before. Otherwise, this breaks
+    // unconditional requests!
+    if (this.locallyModifiedVersion) {
+      this.locallyModifiedVersion = request.serverTime;
+    }
+
+    for (let id of request.successfulIDs) {
+      this.successfulIDs.push(id);
+    }
+
+    for (let [id, reason] in Iterator(request.failures)) {
+      this.failures[id] = reason;
+    }
+
+    if (request.error) {
+      this._errorEncountered = true;
+    }
+
+    this._checkFinish();
+  },
+
+  _checkFinish: function _checkFinish() {
+    if (this._outgoingRequests.length && !this._errorEncountered) {
+      this._sendOutgoingRequest();
+      return;
+    }
+
+    if (!this._onFinishCallback) {
+      return;
+    }
+
+    try {
+      this._onFinishCallback(this);
+    } catch (ex) {
+      this._log.warn("Exception when calling finished callback: " +
+                     CommonUtils.exceptionStr(ex));
+    }
+  },
+};
+Object.freeze(StorageCollectionBatchedSet.prototype);
+
+/**
+ * Manages a batch of BSO deletion requests.
+ *
+ * A single instance of this virtual request allows deletion of many individual
+ * BSOs without having to worry about server limits.
+ *
+ * Instances are obtained by calling `deleteBSOsBatching` on
+ * StorageServiceClient.
+ *
+ * Usage is roughly the same as StorageCollectionBatchedSet. Callers obtain
+ * an instance and select individual BSOs for deletion by calling `addID`.
+ * When the caller is finished marking BSOs for deletion, they call `finish`
+ * with a callback which will be invoked when all deletion requests finish.
+ *
+ * When the finished callback is invoked, any encountered errors will be stored
+ * in the `errors` property of this instance (which is passed to the callback).
+ * This will be an empty array if no errors were encountered. Else, it will
+ * contain the errors from the `onComplete` handler of request instances. The
+ * set of succeeded and failed IDs is not currently available.
+ *
+ * Deletes can be made conditional by setting `locallyModifiedVersion`. The
+ * behavior is the same as request types. The only difference is that the
+ * updated version from the server as a result of requests is carried forward
+ * to subsequent requests.
+ *
+ * The server version from the last request is stored in the
+ * `serverModifiedVersion` property. It is not safe to access this until the
+ * callback from `finish`.
+ *
+ * Like StorageCollectionBatchedSet, requests are issued serially to avoid
+ * race conditions on the server.
+ *
+ * @param client
+ *        (StorageServiceClient) Client request is associated with.
+ * @param collection
+ *        (string) Collection being operated on.
+ */
+function StorageCollectionBatchedDelete(client, collection) {
+  this.client     = client;
+  this.collection = collection;
+
+  this._log = client._log;
+
+  this.locallyModifiedVersion = null;
+  this.serverModifiedVersion  = null;
+  this.errors                 = [];
+
+  this._pendingIDs          = [];
+  this._requestInFlight     = false;
+  this._finished            = false;
+  this._finishedCallback    = null;
+}
+StorageCollectionBatchedDelete.prototype = {
+  addID: function addID(id) {
+    if (this._finished) {
+      throw new Error("Cannot add IDs to a finished instance.");
+    }
+
+    // If we saw errors already, don't do any work. This is an optimization
+    // and isn't strictly required, as _sendRequest() should no-op.
+    if (this.errors.length) {
+      return;
+    }
+
+    this._pendingIDs.push(id);
+
+    if (this._pendingIDs.length >= this.client.REQUEST_BSO_DELETE_LIMIT) {
+      this._sendRequest();
+    }
+  },
+
+  /**
+   * Finish this batch operation.
+   *
+   * No more IDs can be added to this operation. Existing IDs are flushed as
+   * a request. The passed callback will be called when all requests have
+   * finished.
+   */
+  finish: function finish(cb) {
+    if (this._finished) {
+      throw new Error("Batch delete instance has already been finished.");
+    }
+
+    this._finished = true;
+    this._finishedCallback = cb;
+
+    if (this._pendingIDs.length) {
+      this._sendRequest();
+    }
+  },
+
+  _sendRequest: function _sendRequest() {
+    // Only allow 1 active request at a time and don't send additional
+    // requests if one has failed.
+    if (this._requestInFlight || this.errors.length) {
+      return;
+    }
+
+    let ids = this._pendingIDs.splice(0, this.client.REQUEST_BSO_DELETE_LIMIT);
+    let request = this.client.deleteBSOs(this.collection, ids);
+
+    if (this.locallyModifiedVersion) {
+      request.locallyModifiedVersion = this.locallyModifiedVersion;
+    }
+
+    request.dispatch(this._onRequestComplete.bind(this));
+    this._requestInFlight = true;
+  },
+
+  _onRequestComplete: function _onRequestComplete(error, request) {
+    this._requestInFlight = false;
+
+    if (error) {
+      // We don't currently track metadata of what failed. This is an obvious
+      // feature that could be added.
+      this._log.warn("Error received from server: " + error);
+      this.errors.push(error);
+    }
+
+    this.serverModifiedVersion = request.serverTime;
+
+    // If performing conditional requests, carry forward the new server version
+    // so subsequent conditional requests work.
+    if (this.locallyModifiedVersion) {
+      this.locallyModifiedVersion = request.serverTime;
+    }
+
+    if (this._pendingIDs.length && !this.errors.length) {
+      this._sendRequest();
+      return;
+    }
+
+    if (!this._finishedCallback) {
+      return;
+    }
+
+    try {
+      this._finishedCallback(this);
+    } catch (ex) {
+      this._log.warn("Exception when invoking finished callback: " +
+                     CommonUtils.exceptionStr(ex));
+    }
+  },
+};
+Object.freeze(StorageCollectionBatchedDelete.prototype);
+
+/**
  * Construct a new client for the SyncStorage API, version 2.0.
  *
  * Clients are constructed against a base URI. This URI is typically obtained
  * from the token server via the endpoint component of a successful token
  * response.
  *
  * The purpose of this type is to serve as a middleware between a client's core
  * logic and the HTTP API. It hides the details of how the storage API is
@@ -1190,16 +1536,37 @@ function StorageServiceClient(baseURI) {
 StorageServiceClient.prototype = {
   /**
    * The user agent sent with every request.
    *
    * You probably want to change this.
    */
   userAgent: "StorageServiceClient",
 
+  /**
+   * Maximum size of entity bodies.
+   *
+   * TODO this should come from the server somehow. See bug 769759.
+   */
+  REQUEST_SIZE_LIMIT: 512000,
+
+  /**
+   * Maximum number of BSOs in requests.
+   *
+   * TODO this should come from the server somehow. See bug 769759.
+   */
+  REQUEST_BSO_COUNT_LIMIT: 100,
+
+  /**
+   * Maximum number of BSOs that can be deleted in a single DELETE.
+   *
+   * TODO this should come from the server. See bug 769759.
+   */
+  REQUEST_BSO_DELETE_LIMIT: 100,
+
   _baseURI: null,
   _log: null,
 
   _listeners: null,
 
   //----------------------------
   // Event Listener Management |
   //----------------------------
@@ -1579,21 +1946,19 @@ StorageServiceClient.prototype = {
    *
    * The request can be made conditional by setting `locallyModifiedVersion`
    * on the returned request instance.
    *
    * This function returns a StorageCollectionSetRequest instance. This type
    * has additional functions and properties specific to this operation. See
    * its documentation for more.
    *
-   * Future improvement: support streaming of uploaded records. Currently, data
-   * is buffered in the client before going over the wire. Ideally, we'd support
-   * sending over the wire as soon as data is available. This will require
-   * support in RESTRequest, which doesn't support streaming on requests, only
-   * responses.
+   * Most consumers interested in submitting multiple BSOs to the server will
+   * want to use `setBSOsBatching` instead. That API intelligently splits up
+   * requests as necessary, etc.
    *
    * Example usage:
    *
    *   let request = client.setBSOs("collection0");
    *   let bso0 = new BasicStorageObject("id0");
    *   bso0.payload = "payload0";
    *
    *   let bso1 = new BasicStorageObject("id1");
@@ -1631,16 +1996,40 @@ StorageServiceClient.prototype = {
       accept:            "application/json",
       allowIfUnmodified: true,
     });
 
     return request;
   },
 
   /**
+   * This is a batching variant of setBSOs.
+   *
+   * Whereas `setBSOs` is a 1:1 mapping between function calls and HTTP
+   * requests issued, this one is a 1:N mapping. It will intelligently break
+   * up outgoing BSOs into multiple requests so size limits, etc aren't
+   * exceeded.
+   *
+   * Please see the documentation for `StorageCollectionBatchedSet` for
+   * usage info.
+   *
+   * @param collection
+   *        (string) Collection to operate on.
+   * @return
+   *        (StorageCollectionBatchedSet) Batched set instance.
+   */
+  setBSOsBatching: function setBSOsBatching(collection) {
+    if (!collection) {
+      throw new Error("collection argument must be defined.");
+    }
+
+    return new StorageCollectionBatchedSet(this, collection);
+  },
+
+  /**
    * Deletes a single BSO from a collection.
    *
    * The request can be made conditional by setting `locallyModifiedVersion`
    * on the returned request instance.
    *
    * @param collection
    *        (string) Collection to operate on.
    * @param id
@@ -1665,16 +2054,20 @@ StorageServiceClient.prototype = {
    * Delete multiple BSOs from a specific collection.
    *
    * This is functional equivalent to calling deleteBSO() for every ID but
    * much more efficient because it only results in 1 round trip to the server.
    *
    * The request can be made conditional by setting `locallyModifiedVersion`
    * on the returned request instance.
    *
+   * If the number of BSOs to delete is potentially large, it is preferred to
+   * use `deleteBSOsBatching`. That API automatically splits the operation into
+   * multiple requests so server limits aren't exceeded.
+   *
    * @param collection
    *        (string) Name of collection to delete BSOs from.
    * @param ids
    *        (iterable of strings) Set of BSO IDs to delete.
    */
   deleteBSOs: function deleteBSOs(collection, ids) {
     // In theory we should URL encode. However, IDs are supposed to be URL
     // safe. If we get garbage in, we'll get garbage out and the server will
@@ -1684,16 +2077,34 @@ StorageServiceClient.prototype = {
     let uri = this._baseURI + "storage/" + collection + "?ids=" + s;
 
     return this._getRequest(uri, "DELETE", {
       allowIfUnmodified: true,
     });
   },
 
   /**
+   * Bulk deletion of BSOs with no size limit.
+   *
+   * This allows a large amount of BSOs to be deleted easily. It will formulate
+   * multiple `deleteBSOs` queries so the client does not exceed server limits.
+   *
+   * @param collection
+   *        (string) Name of collection to delete BSOs from.
+   * @return StorageCollectionBatchedDelete
+   */
+  deleteBSOsBatching: function deleteBSOsBatching(collection) {
+    if (!collection) {
+      throw new Error("collection argument must be defined.");
+    }
+
+    return new StorageCollectionBatchedDelete(this, collection);
+  },
+
+  /**
    * Deletes a single collection from the server.
    *
    * The request can be made conditional by setting `locallyModifiedVersion`
    * on the returned request instance.
    *
    * @param collection
    *        (string) Name of collection to delete.
    */
--- a/services/common/tests/unit/test_storage_server.js
+++ b/services/common/tests/unit/test_storage_server.js
@@ -248,16 +248,38 @@ add_test(function test_bso_get_existing(
   do_check_eq(bso.modified, coll.bso("bso").modified);
   let payload = JSON.parse(bso.payload);
   do_check_attribute_count(payload, 1);
   do_check_eq(payload.foo, "bar");
 
   server.stop(run_next_test);
 });
 
+add_test(function test_percent_decoding() {
+  _("Ensure query string arguments with percent encoded are handled.");
+
+  let server = new StorageServer();
+  server.registerUser("123", "password");
+  server.startSynchronous(PORT);
+
+  let coll = server.user("123").createCollection("test");
+  coll.insert("001", {foo: "bar"});
+  coll.insert("002", {bar: "foo"});
+
+  let request = localRequest("/2.0/123/storage/test?ids=001%2C002", "123",
+                             "password");
+  let error = doGetRequest(request);
+  do_check_null(error);
+  do_check_eq(request.response.status, 200);
+  let items = JSON.parse(request.response.body).items;
+  do_check_attribute_count(items, 2);
+
+  server.stop(run_next_test);
+});
+
 add_test(function test_bso_404() {
   _("Ensure the server responds with a 404 if a BSO does not exist.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.createContents("123", {
     test: {}
   });
@@ -437,16 +459,70 @@ add_test(function test_bso_delete_unmodi
   let error = doDeleteRequest(request);
   do_check_eq(error, null);
   do_check_eq(request.response.status, 204);
   do_check_true(coll.bso("myid").deleted);
 
   server.stop(run_next_test);
 });
 
+add_test(function test_collection_get_unmodified_since() {
+  _("Ensure conditional unmodified get on collection works when it should.");
+
+  let server = new StorageServer();
+  server.registerUser("123", "password");
+  server.startSynchronous(PORT);
+  let collection = server.user("123").createCollection("testcoll");
+  collection.insert("bso0", {foo: "bar"});
+
+  let serverModified = collection.timestamp;
+
+  let request1 = localRequest("/2.0/123/storage/testcoll", "123", "password");
+  request1.setHeader("X-If-Unmodified-Since", serverModified);
+  let error = doGetRequest(request1);
+  do_check_null(error);
+  do_check_eq(request1.response.status, 200);
+
+  let request2 = localRequest("/2.0/123/storage/testcoll", "123", "password");
+  request2.setHeader("X-If-Unmodified-Since", serverModified - 1);
+  let error = doGetRequest(request2);
+  do_check_null(error);
+  do_check_eq(request2.response.status, 412);
+
+  server.stop(run_next_test);
+});
+
+add_test(function test_bso_get_unmodified_since() {
+  _("Ensure conditional unmodified get on BSO works appropriately.");
+
+  let server = new StorageServer();
+  server.registerUser("123", "password");
+  server.startSynchronous(PORT);
+  let collection = server.user("123").createCollection("testcoll");
+  let bso = collection.insert("bso0", {foo: "bar"});
+
+  let serverModified = bso.modified;
+
+  let request1 = localRequest("/2.0/123/storage/testcoll/bso0", "123",
+                              "password");
+  request1.setHeader("X-If-Unmodified-Since", serverModified);
+  let error = doGetRequest(request1);
+  do_check_null(error);
+  do_check_eq(request1.response.status, 200);
+
+  let request2 = localRequest("/2.0/123/storage/testcoll/bso0", "123",
+                              "password");
+  request2.setHeader("X-If-Unmodified-Since", serverModified - 1);
+  let error = doGetRequest(request2);
+  do_check_null(error);
+  do_check_eq(request2.response.status, 412);
+
+  server.stop(run_next_test);
+});
+
 add_test(function test_missing_collection_404() {
   _("Ensure a missing collection returns a 404.");
 
   let server = new StorageServer();
   server.registerUser("123", "password");
   server.startSynchronous(PORT);
 
   let request = localRequest("/2.0/123/storage/none", "123", "password");
--- a/services/common/tests/unit/test_storageservice_client.js
+++ b/services/common/tests/unit/test_storageservice_client.js
@@ -7,40 +7,44 @@ Cu.import("resource://testing-common/ser
 const BASE_URI = "http://localhost:8080/2.0";
 
 function run_test() {
   initTestLogging("Trace");
 
   run_next_test();
 }
 
-function getEmptyServer(user="765", password="password") {
+function getRandomUser() {
+  return "" + (Math.floor(Math.random() * 100000) + 1);
+}
+
+function getEmptyServer(user=getRandomUser(), password="password") {
   let users = {};
   users[user] = password;
 
   return storageServerForUsers(users, {
     meta:    {},
     clients: {},
     crypto:  {},
   });
 }
 
-function getClient(user="765", password="password") {
+function getClient(user=getRandomUser(), password="password") {
   let client = new StorageServiceClient(BASE_URI + "/" + user);
   client.addListener({
     onDispatch: function onDispatch(request) {
       let up = user + ":" + password;
       request.request.setHeader("authorization", "Basic " + btoa(up));
     }
   });
 
   return client;
 }
 
-function getServerAndClient(user="765", password="password") {
+function getServerAndClient(user=getRandomUser(), password="password") {
   let server = getEmptyServer(user, password);
   let client = getClient(user, password);
 
   return [server, client, user, password];
 }
 
 add_test(function test_auth_failure_listener() {
   _("Ensure the onAuthFailure listener is invoked.");
@@ -639,19 +643,19 @@ add_test(function test_set_bsos_simple()
   let request = client.setBSOs("testcollection");
   request.addBSO(bso0);
   request.addBSO(bso1);
 
   request.dispatch(function onComplete(error, req) {
     do_check_null(error);
 
     let successful = req.successfulIDs;
-    do_check_eq(successful.size(), 2);
-    do_check_true(successful.has(bso0.id));
-    do_check_true(successful.has(bso1.id));
+    do_check_eq(successful.length, 2);
+    do_check_eq(successful.indexOf(bso0.id), 0);
+    do_check_true(successful.indexOf(bso1.id), 1);
 
     server.stop(run_next_test);
   });
 });
 
 add_test(function test_set_bsos_invalid_bso() {
   _("Ensure that adding an invalid BSO throws.");
 
@@ -696,17 +700,17 @@ add_test(function test_set_bsos_newline(
   request.addBSO(bso0);
 
   let bso1 = new BasicStorageObject("bso1");
   bso1.payload = "foobar";
   request.addBSO(bso1);
 
   request.dispatch(function onComplete(error, request) {
     do_check_null(error);
-    do_check_eq(request.successfulIDs.size(), 2);
+    do_check_eq(request.successfulIDs.length, 2);
 
     let coll = user.collection("testcoll");
     do_check_eq(coll.bso("bso0").payload, bso0.payload);
     do_check_eq(coll.bso("bso1").payload, bso1.payload);
 
     server.stop(run_next_test);
   });
 });
@@ -960,8 +964,415 @@ add_test(function test_network_error_lis
     }
   });
   let request = client.getCollectionInfo();
   request.dispatch(function() {
     do_check_true(listenerCalled);
     run_next_test();
   });
 });
+
+add_test(function test_batching_set_too_large() {
+  _("Ensure we throw when attempting to add a BSO that is too large to fit.");
+
+  let [server, client, username] = getServerAndClient();
+
+  let request = client.setBSOsBatching("testcoll");
+  let payload = "";
+
+  // The actual length of the payload is a little less. But, this ensures we
+  // exceed it.
+  for (let i = 0; i < client.REQUEST_SIZE_LIMIT; i++) {
+    payload += i;
+  }
+
+  let bso = new BasicStorageObject("bso");
+  bso.payload = payload;
+  do_check_throws(function add() { request.addBSO(bso); });
+
+  server.stop(run_next_test);
+});
+
+add_test(function test_batching_set_basic() {
+  _("Ensure batching set works with single requests.");
+
+  let [server, client, username] = getServerAndClient();
+
+  let request = client.setBSOsBatching("testcoll");
+  for (let i = 0; i < 10; i++) {
+    let bso = new BasicStorageObject("bso" + i);
+    bso.payload = "payload" + i;
+    request.addBSO(bso);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(request.successfulIDs.length, 10);
+
+    let collection = server.user(username).collection("testcoll");
+    do_check_eq(collection.timestamp, request.serverModifiedVersion);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batching_set_batch_count() {
+  _("Ensure multiple outgoing request batching works when count is exceeded.");
+
+  let [server, client, username] = getServerAndClient();
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+  }
+
+  let request = client.setBSOsBatching("testcoll");
+  for (let i = 1; i <= 300; i++) {
+    let bso = new BasicStorageObject("bso" + i);
+    bso.payload = "XXXXXXX";
+    request.addBSO(bso);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(request.successfulIDs.length, 300);
+    do_check_eq(requestCount, 3);
+
+    let collection = server.user(username).collection("testcoll");
+    do_check_eq(collection.timestamp, request.serverModifiedVersion);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batching_set_batch_size() {
+  _("Ensure outgoing requests batch when size is exceeded.");
+
+  let [server, client, username] = getServerAndClient();
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+  };
+
+  let limit = client.REQUEST_SIZE_LIMIT;
+
+  let request = client.setBSOsBatching("testcoll");
+
+  // JavaScript: Y U NO EASY REPETITION FUNCTIONALITY?
+  let data = [];
+  for (let i = (limit / 2) - 100; i; i -= 1) {
+    data.push("X");
+  }
+
+  let payload = data.join("");
+
+  for (let i = 0; i < 4; i++) {
+    let bso = new BasicStorageObject("bso" + i);
+    bso.payload = payload;
+    request.addBSO(bso);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(request.successfulIDs.length, 4);
+    do_check_eq(requestCount, 2);
+
+    let collection = server.user(username).collection("testcoll");
+    do_check_eq(collection.timestamp, request.serverModifiedVersion);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batching_set_flush() {
+  _("Ensure flushing batch sets works.");
+
+  let [server, client, username] = getServerAndClient();
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+  }
+
+  let request = client.setBSOsBatching("testcoll");
+  for (let i = 1; i < 101; i++) {
+    let bso = new BasicStorageObject("bso" + i);
+    bso.payload = "foo";
+    request.addBSO(bso);
+
+    if (i % 10 == 0) {
+      request.flush();
+    }
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(request.successfulIDs.length, 100);
+    do_check_eq(requestCount, 10);
+
+    let collection = server.user(username).collection("testcoll");
+    do_check_eq(collection.timestamp, request.serverModifiedVersion);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batching_set_conditional_success() {
+  _("Ensure conditional requests for batched sets work properly.");
+
+  let [server, client, username] = getServerAndClient();
+
+  let collection = server.user(username).createCollection("testcoll");
+
+  let lastServerVersion = Date.now();
+  collection.insertBSO(new ServerBSO("foo", "bar", lastServerVersion));
+  collection.timestamp = lastServerVersion;
+  do_check_eq(collection.timestamp, lastServerVersion);
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+  }
+
+  let request = client.setBSOsBatching("testcoll");
+  request.locallyModifiedVersion = collection.timestamp;
+
+  for (let i = 1; i < 251; i++) {
+    let bso = new BasicStorageObject("bso" + i);
+    bso.payload = "foo" + i;
+    request.addBSO(bso);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(requestCount, 3);
+
+    do_check_eq(collection.timestamp, request.serverModifiedVersion);
+    do_check_eq(collection.timestamp, request.locallyModifiedVersion);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batching_set_initial_failure() {
+  _("Ensure that an initial request failure setting BSOs is handled properly.");
+
+  let [server, client, username] = getServerAndClient();
+
+  let collection = server.user(username).createCollection("testcoll");
+  collection.timestamp = Date.now();
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+  }
+
+  let request = client.setBSOsBatching("testcoll");
+  request.locallyModifiedVersion = collection.timestamp - 1;
+
+  for (let i = 1; i < 250; i++) {
+    let bso = new BasicStorageObject("bso" + i);
+    bso.payload = "foo" + i;
+    request.addBSO(bso);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(requestCount, 1);
+
+    do_check_eq(request.successfulIDs.length, 0);
+    do_check_eq(Object.keys(request.failures).length, 0);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batching_set_subsequent_failure() {
+  _("Ensure a non-initial failure during batching set is handled properly.");
+
+  let [server, client, username] = getServerAndClient();
+  let collection = server.user(username).createCollection("testcoll");
+  collection.timestamp = Date.now();
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+
+    if (requestCount == 1) {
+      return;
+    }
+
+    collection.timestamp++;
+  }
+
+  let request = client.setBSOsBatching("testcoll");
+  request.locallyModifiedVersion = collection.timestamp;
+
+  for (let i = 0; i < 250; i++) {
+    let bso = new BasicStorageObject("bso" + i);
+    bso.payload = "foo" + i;
+    request.addBSO(bso);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(requestCount, 2);
+    do_check_eq(request.successfulIDs.length, 100);
+    do_check_eq(Object.keys(request.failures).length, 0);
+
+    server.stop(run_next_test);
+  });
+});
+
+function getBatchedDeleteData(collection="testcoll") {
+  let [server, client, username] = getServerAndClient();
+
+  let serverBSOs = {};
+  for (let i = 1000; i; i -= 1) {
+    serverBSOs["bso" + i] = new ServerBSO("bso" + i, "payload" + i);
+  }
+
+  let user = server.user(username);
+  user.createCollection(collection, serverBSOs);
+
+  return [server, client, username, collection];
+}
+
+add_test(function test_batched_delete_single() {
+  _("Ensure batched delete with single request works.");
+
+  let [server, client, username, collection] = getBatchedDeleteData();
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount += 1;
+  }
+
+  let request = client.deleteBSOsBatching(collection);
+  for (let i = 1; i < 51; i += 1) {
+    request.addID("bso" + i);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(requestCount, 1);
+    do_check_eq(request.errors.length, 0);
+
+    let coll = server.user(username).collection(collection);
+    do_check_eq(coll.count(), 950);
+
+    do_check_eq(request.serverModifiedVersion, coll.timestamp);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batched_delete_multiple() {
+  _("Ensure batched delete splits requests properly.");
+
+  let [server, client, username, collection] = getBatchedDeleteData();
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount += 1;
+  }
+
+  let request = client.deleteBSOsBatching(collection);
+  for (let i = 1; i < 251; i += 1) {
+    request.addID("bso" + i);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(requestCount, 3);
+    do_check_eq(request.errors.length, 0);
+
+    let coll = server.user(username).collection(collection);
+    do_check_eq(coll.count(), 750);
+
+    do_check_eq(request.serverModifiedVersion, coll.timestamp);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batched_delete_conditional_success() {
+  _("Ensure conditional batched delete all work.");
+
+  let [server, client, username, collection] = getBatchedDeleteData();
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+  }
+
+  let serverCollection = server.user(username).collection(collection);
+  let initialTimestamp = serverCollection.timestamp;
+
+  let request = client.deleteBSOsBatching(collection);
+  request.locallyModifiedVersion = initialTimestamp;
+
+  for (let i = 1; i < 251; i += 1) {
+    request.addID("bso" + 1);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(requestCount, 3);
+    do_check_eq(request.errors.length, 0);
+
+    do_check_true(request.locallyModifiedVersion > initialTimestamp);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batched_delete_conditional_initial_failure() {
+  _("Ensure conditional batched delete failure on initial request works.");
+
+  // The client needs to issue multiple requests but the first one was
+  // rejected. The client should only issue that initial request.
+  let [server, client, username, collection] = getBatchedDeleteData();
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+  }
+
+  let serverCollection = server.user(username).collection(collection);
+  let request = client.deleteBSOsBatching(collection);
+  request.locallyModifiedVersion = serverCollection.timestamp - 1;
+
+  for (let i = 1; i < 251; i += 1) {
+    request.addID("bso" + i);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(requestCount, 1);
+    do_check_eq(request.errors.length, 1);
+
+    server.stop(run_next_test);
+  });
+});
+
+add_test(function test_batched_delete_conditional_subsequent_failure() {
+  _("Ensure conditional batched delete failure on non-initial request.");
+
+  let [server, client, username, collection] = getBatchedDeleteData();
+
+  let serverCollection = server.user(username).collection(collection);
+
+  let requestCount = 0;
+  server.callback.onRequest = function onRequest() {
+    requestCount++;
+
+    if (requestCount <= 1) {
+      return;
+    }
+
+    // Advance collection's timestamp on subsequent requests so request is
+    // rejected.
+    serverCollection.timestamp++;
+  }
+
+  let request = client.deleteBSOsBatching(collection);
+  request.locallyModifiedVersion = serverCollection.timestamp;
+
+  for (let i = 1; i < 251; i += 1) {
+    request.addID("bso" + i);
+  }
+
+  request.finish(function onFinish(request) {
+    do_check_eq(requestCount, 2);
+    do_check_eq(request.errors.length, 1);
+
+    server.stop(run_next_test);
+  });
+});
--- a/toolkit/devtools/debugger/server/dbg-browser-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js
@@ -123,31 +123,38 @@ BrowserRootActor.prototype = {
                       for each (actor in actorList)] };
   },
 
   /**
    * Watch a window that was visited during onListTabs for
    * tab closures.
    */
   watchWindow: function BRA_watchWindow(aWindow) {
-    aWindow.getBrowser().tabContainer.addEventListener("TabClose",
-                                                       this.onTabClosed,
-                                                       false);
+    this.getTabContainer(aWindow).addEventListener("TabClose",
+                                                   this.onTabClosed,
+                                                   false);
   },
 
   /**
    * Stop watching a window for tab closes.
    */
   unwatchWindow: function BRA_unwatchWindow(aWindow) {
-    aWindow.getBrowser().tabContainer.removeEventListener("TabClose",
-                                                          this.onTabClosed);
+    this.getTabContainer(aWindow).removeEventListener("TabClose",
+                                                      this.onTabClosed);
     this.exitTabActor(aWindow);
   },
 
   /**
+   * Return the tab container for the specified window.
+   */
+  getTabContainer: function BRA_getTabContainer(aWindow) {
+    return aWindow.getBrowser().tabContainer;
+  },
+
+  /**
    * When a tab is closed, exit its tab actor.  The actor
    * will be dropped at the next listTabs request.
    */
   onTabClosed: function BRA_onTabClosed(aEvent) {
     this.exitTabActor(aEvent.target.linkedBrowser);
   },
 
   /**
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -6802,20 +6802,20 @@ nsWindow::SetupKeyModifiersSequence(nsTA
   for (PRUint32 i = 0; i < ArrayLength(sModifierKeyMap); ++i) {
     const PRUint32* map = sModifierKeyMap[i];
     if (aModifiers & map[0]) {
       aArray->AppendElement(KeyPair(map[1], map[2]));
     }
   }
 }
 
-static BOOL WINAPI EnumFirstChild(HWND hwnd, LPARAM lParam)
-{
-  *((HWND*)lParam) = hwnd;
-  return FALSE;
+static BOOL WINAPI EnumFirstChild(HWND hwnd, LPARAM lParam)
+{
+  *((HWND*)lParam) = hwnd;
+  return FALSE;
 }
 
 static void InvalidatePluginAsWorkaround(nsWindow *aWindow, const nsIntRect &aRect)
 {
   aWindow->Invalidate(aRect);
 
   // XXX - Even more evil workaround!! See bug 762948, flash's bottom
   // level sandboxed window doesn't seem to get our invalidate. We send