Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 23 Apr 2013 15:05:21 -0400
changeset 140617 55f7505902597229a4af764555def7fb06830b73
parent 140616 35f98c472fb1c715e17ad8be968fad00816c6133 (current diff)
parent 140565 8b1a7228674af2f0470f2fbe3c52f69fe78b5e58 (diff)
child 140618 9a8232d88dc4272613a69c5fe5d60e5922925b78
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.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 m-c to inbound.
browser/base/content/browser.js
--- a/b2g/components/DirectoryProvider.js
+++ b/b2g/components/DirectoryProvider.js
@@ -86,17 +86,17 @@ DirectoryProvider.prototype = {
   // The VolumeService only exists on the device, and not on desktop
   volumeHasFreeSpace: function dp_volumeHasFreeSpace(volumePath, requiredSpace) {
     if (!volumePath) {
       return false;
     }
     if (!Services.volumeService) {
       return false;
     }
-    let volume = Services.volumeService.getVolumeByPath(volumePath);
+    let volume = Services.volumeService.createOrGetVolumeByPath(volumePath);
     if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
       return false;
     }
     let stat = volume.getStats();
     if (!stat) {
       return false;
     }
     return requiredSpace <= stat.freeBytes;
@@ -196,17 +196,17 @@ DirectoryProvider.prototype = {
 
   getDefaultUpdateDir: function dp_getDefaultUpdateDir() {
     let path = gExtStorage;
     if (!path) {
       path = LOCAL_DIR;
     }
 
     if (Services.volumeService) {
-      let extVolume = Services.volumeService.getVolumeByPath(path);
+      let extVolume = Services.volumeService.createOrGetVolumeByPath(path);
       if (!extVolume) {
         path = LOCAL_DIR;
       }
     }
 
     let dir = Cc["@mozilla.org/file/local;1"]
                  .createInstance(Ci.nsILocalFile)
     dir.initWithPath(path);
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1077,16 +1077,17 @@ pref("devtools.debugger.ui.variables-sea
 // Enable the Profiler
 pref("devtools.profiler.enabled", true);
 
 // Enable the Network Monitor
 pref("devtools.netmonitor.enabled", true);
 
 // The default Network Monitor UI settings
 pref("devtools.netmonitor.panes-network-details-width", 450);
+pref("devtools.netmonitor.panes-network-details-height", 450);
 
 // Enable the Tilt inspector
 pref("devtools.tilt.enabled", true);
 pref("devtools.tilt.intro_transition", true);
 pref("devtools.tilt.outro_transition", true);
 
 // Enable the Scratchpad tool.
 pref("devtools.scratchpad.enabled", true);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6762,18 +6762,20 @@ let gPrivateBrowsingUI = {
             newWindow.label = newPrivateWindow.label;
             newWindow.accessKey = newPrivateWindow.accessKey;
             newWindow.command = newPrivateWindow.command;
           }
         });
       }
     }
 
-    if (gURLBar) {
-      // Disable switch to tab autocompletion for private windows
+    if (gURLBar &&
+        !PrivateBrowsingUtils.permanentPrivateBrowsing) {
+      // Disable switch to tab autocompletion for private windows 
+      // (not for "Always use private browsing" mode)
       gURLBar.setAttribute("autocompletesearchparam", "");
     }
   }
 };
 
 
 /**
  * Switch to a tab that has a given URI, and focusses its browser window.
@@ -6786,19 +6788,20 @@ let gPrivateBrowsingUI = {
  *        True to open a new tab and switch to it, if no existing tab is found.
  *        If no suitable window is found, a new one will be opened.
  * @return True if an existing tab was found, false otherwise
  */
 function switchToTabHavingURI(aURI, aOpenNew) {
   // This will switch to the tab in aWindow having aURI, if present.
   function switchIfURIInWindow(aWindow) {
     // Only switch to the tab if neither the source and desination window are
-    // private.
-    if (PrivateBrowsingUtils.isWindowPrivate(window) ||
-        PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
+    // private and they are not in permanent private borwsing mode
+    if ((PrivateBrowsingUtils.isWindowPrivate(window) ||
+        PrivateBrowsingUtils.isWindowPrivate(aWindow)) &&
+        !PrivateBrowsingUtils.permanentPrivateBrowsing) {
       return false;
     }
 
     let browsers = aWindow.gBrowser.browsers;
     for (let i = 0; i < browsers.length; i++) {
       let browser = browsers[i];
       if (browser.currentURI.equals(aURI)) {
         // Focus the matching window & tab
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -645,17 +645,18 @@
                 let autocomplete = this.mTabBrowser._placesAutocomplete;
                 if (this.mBrowser.registeredOpenURI) {
                   autocomplete.unregisterOpenPage(this.mBrowser.registeredOpenURI);
                   delete this.mBrowser.registeredOpenURI;
                 }
                 // Tabs in private windows aren't registered as "Open" so
                 // that they don't appear as switch-to-tab candidates.
                 if (!isBlankPageURL(aLocation.spec) &&
-                    !PrivateBrowsingUtils.isWindowPrivate(window)) {
+                    (!PrivateBrowsingUtils.isWindowPrivate(window) ||
+                    PrivateBrowsingUtils.permanentPrivateBrowsing)) {
                   autocomplete.registerOpenPage(aLocation);
                   this.mBrowser.registeredOpenURI = aLocation;
                 }
               }
 
               if (!this.mBlank) {
                 this._callProgressListeners("onLocationChange",
                                             [aWebProgress, aRequest, aLocation,
--- a/browser/devtools/commandline/gcli.jsm
+++ b/browser/devtools/commandline/gcli.jsm
@@ -7303,22 +7303,20 @@ Output.prototype.complete = function(dat
 };
 
 exports.Output = Output;
 
 /**
  * Functions and data related to the execution of a command
  */
 exports.createExecutionContext = function(requisition) {
-  return {
+  var context = {
     exec: requisition.exec.bind(requisition),
     update: requisition.update.bind(requisition),
     updateExec: requisition.updateExec.bind(requisition),
-    document: requisition.document,
-    environment: requisition.environment,
     createView: view.createView,
     typedData: function(data, type) {
       return {
         isTypedData: true,
         data: data,
         type: type
       };
     },
@@ -7329,16 +7327,28 @@ exports.createExecutionContext = functio
     /**
      * @deprecated Use defer() instead, which does the same thing, but is not
      * confusingly named
      */
     createPromise: function() {
       return Promise.defer();
     }
   };
+
+  Object.defineProperty(context, 'environment', {
+    get: function() { return requisition.environment; },
+    enumerable : true
+  });
+
+  Object.defineProperty(context, 'document', {
+    get: function() { return requisition.document; },
+    enumerable : true
+  });
+
+  return context;
 };
 
 
 });
 define("text!gcli/ui/intro.html", [], "\n" +
   "<div>\n" +
   "  <p>${l10n.introTextOpening2}</p>\n" +
   "\n" +
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-data.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-data.js
@@ -648,51 +648,51 @@ function testKeyboardAccessibility(callb
               is(gVariablesView.getFocusedItem().name, "someProp5",
                 "The someProp5 item should be focused now.");
 
               EventUtils.sendKey("DOWN", gDebugger);
               is(gVariablesView.getFocusedItem().name, "0",
                 "The 0 item should be focused now.");
 
               EventUtils.sendKey("END", gDebugger);
-              is(gVariablesView.getFocusedItem().name, "foo",
-                "The foo item should be focused now.");
+              is(gVariablesView.getFocusedItem().name, "bar",
+                "The bar item should be focused now.");
 
               EventUtils.sendKey("DOWN", gDebugger);
               is(gVariablesView.getFocusedItem().name, "bar",
-                "The bar item should be focused now.");
+                "The bar item should still be focused now.");
 
               EventUtils.sendKey("UP", gDebugger);
               is(gVariablesView.getFocusedItem().name, "foo",
                 "The foo item should be focused now.");
 
               EventUtils.sendKey("RIGHT", gDebugger);
               is(gVariablesView.getFocusedItem().name, "foo",
                 "The foo item should still be focused now.");
 
               EventUtils.sendKey("PAGE_DOWN", gDebugger);
-              is(gVariablesView.getFocusedItem().name, "foo",
-                "The foo item should still be focused now.");
+              is(gVariablesView.getFocusedItem().name, "bar",
+                "The bar item should be focused now.");
 
               EventUtils.sendKey("PAGE_UP", gDebugger);
+              is(gVariablesView.getFocusedItem().name, "someProp7",
+                "The someProp7 item should be focused now.");
+
+              EventUtils.sendKey("UP", gDebugger);
               is(gVariablesView.getFocusedItem().name, "__proto__",
                 "The __proto__ item should be focused now.");
 
               EventUtils.sendKey("UP", gDebugger);
               is(gVariablesView.getFocusedItem().name, "set",
                 "The set item should be focused now.");
 
               EventUtils.sendKey("UP", gDebugger);
               is(gVariablesView.getFocusedItem().name, "get",
                 "The get item should be focused now.");
 
-              EventUtils.sendKey("UP", gDebugger);
-              is(gVariablesView.getFocusedItem().name, "p8",
-                "The p8 item should be focused now.");
-
               EventUtils.sendKey("HOME", gDebugger);
               is(gVariablesView.getFocusedItem().name, "someProp0",
                 "The someProp0 item should be focused now.");
 
               EventUtils.sendKey("UP", gDebugger);
               is(gVariablesView.getFocusedItem().name, "someProp0",
                 "The someProp0 item should still be focused now.");
 
@@ -823,16 +823,28 @@ function testKeyboardAccessibility(callb
                 "The top-level __proto__ item should be expanded.");
 
               EventUtils.sendKey("LEFT", gDebugger);
               is(gVariablesView.getFocusedItem().name, "__proto__",
                 "The top-level __proto__ item should still be focused.");
               is(gVariablesView.getFocusedItem().expanded, false,
                 "The top-level __proto__ item should not be expanded.");
 
+              EventUtils.sendKey("END", gDebugger);
+              is(gVariablesView.getFocusedItem().name, "foo",
+                "The foo scope should be focused.");
+
+              EventUtils.sendKey("PAGE_UP", gDebugger);
+              is(gVariablesView.getFocusedItem().name, "__proto__",
+                "The __proto__ property should be focused.");
+
+              EventUtils.sendKey("PAGE_DOWN", gDebugger);
+              is(gVariablesView.getFocusedItem().name, "foo",
+                "The foo scope should be focused.");
+
               executeSoon(callback);
             });
           });
         });
       });
     });
   });
 }
--- a/browser/devtools/framework/Target.jsm
+++ b/browser/devtools/framework/Target.jsm
@@ -270,35 +270,41 @@ TabTarget.prototype = {
 
       this._client = new DebuggerClient(DebuggerServer.connectPipe());
       // A local TabTarget will never perform chrome debugging.
       this._chrome = false;
     }
 
     this._setupRemoteListeners();
 
-    if (this.isRemote) {
-      // In the remote debugging case, the protocol connection will have been
-      // already initialized in the connection screen code.
-      this._remote.resolve(null);
-    } else {
+    let attachTab = () => {
+      this._client.attachTab(this._form.actor, (aResponse, aTabClient) => {
+        if (!aTabClient) {
+          this._remote.reject("Unable to attach to the tab");
+          return;
+        }
+        this.threadActor = aResponse.threadActor;
+        this._remote.resolve(null);
+      });
+    };
+
+    if (this.isLocalTab) {
       this._client.connect((aType, aTraits) => {
         this._client.listTabs(aResponse => {
           this._form = aResponse.tabs[aResponse.selected];
-
-          this._client.attachTab(this._form.actor, (aResponse, aTabClient) => {
-            if (!aTabClient) {
-              this._remote.reject("Unable to attach to the tab");
-              return;
-            }
-            this.threadActor = aResponse.threadActor;
-            this._remote.resolve(null);
-          });
+          attachTab();
         });
       });
+    } else if (!this.chrome) {
+      // In the remote debugging case, the protocol connection will have been
+      // already initialized in the connection screen code.
+      attachTab();
+    } else {
+      // Remote chrome debugging doesn't need anything at this point.
+      this._remote.resolve(null);
     }
 
     return this._remote.promise;
   },
 
   /**
    * Listen to the different events.
    */
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -510,17 +510,18 @@ NetworkEventsHandler.prototype = {
  * Localization convenience methods.
  */
 let L10N = new ViewHelpers.L10N(NET_STRINGS_URI);
 
 /**
  * Shortcuts for accessing various network monitor preferences.
  */
 let Prefs = new ViewHelpers.Prefs("devtools.netmonitor", {
-  networkDetailsWidth: ["Int", "panes-network-details-width"]
+  networkDetailsWidth: ["Int", "panes-network-details-width"],
+  networkDetailsHeight: ["Int", "panes-network-details-height"]
 });
 
 /**
  * Convenient way of emitting events from the panel window.
  */
 EventEmitter.decorate(this);
 
 /**
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -2,16 +2,17 @@
 /* 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";
 
 const EPSILON = 0.001;
 const REQUESTS_REFRESH_RATE = 50; // ms
+const REQUESTS_HEADERS_SAFE_BOUNDS = 30; // px
 const REQUESTS_WATERFALL_SAFE_BOUNDS = 100; // px
 const REQUESTS_WATERFALL_BACKGROUND_PATTERN = [5, 250, 1000, 2000]; // ms
 const DEFAULT_HTTP_VERSION = "HTTP/1.1";
 const HEADERS_SIZE_DECIMALS = 3;
 const CONTENT_SIZE_DECIMALS = 2;
 const CONTENT_MIME_TYPE_ABBREVIATIONS = {
   "ecmascript": "js",
   "javascript": "js",
@@ -97,26 +98,28 @@ let NetMonitorView = {
 
     this._detailsPane = $("#details-pane");
     this._detailsPaneToggleButton = $("#details-pane-toggle");
 
     this._collapsePaneString = L10N.getStr("collapseDetailsPane");
     this._expandPaneString = L10N.getStr("expandDetailsPane");
 
     this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth);
+    this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight);
     this.toggleDetailsPane({ visible: false });
   },
 
   /**
    * Destroys the UI for all the displayed panes.
    */
   _destroyPanes: function DV__destroyPanes() {
     dumpn("Destroying the NetMonitorView panes");
 
     Prefs.networkDetailsWidth = this._detailsPane.getAttribute("width");
+    Prefs.networkDetailsHeight = this._detailsPane.getAttribute("height");
 
     this._detailsPane = null;
     this._detailsPaneToggleButton = null;
   },
 
   /**
    * Gets the visibility state of the network details pane.
    * @return boolean
@@ -587,16 +590,37 @@ create({ constructor: RequestsMenuView, 
    *        True if this container's width was changed.
    */
   _flushWaterfallViews: function NVRM__flushWaterfallViews(aReset) {
     // To avoid expensive operations like getBoundingClientRect(), the
     // waterfalls width is cached. However, in certain scenarios like when
     // the window is resized, this needs to be invalidated.
     if (aReset) {
       this._cachedWaterfallWidth = 0;
+
+      let table = $("#network-table");
+      let toolbar = $("#requests-menu-toolbar");
+      let columns = [
+        [".requests-menu-waterfall", "waterfall-overflows"],
+        [".requests-menu-size", "size-overflows"],
+        [".requests-menu-type", "type-overflows"],
+        [".requests-menu-domain", "domain-overflows"]
+      ];
+
+      // Flush headers.
+      columns.forEach(([, attribute]) => table.removeAttribute(attribute));
+      let availableWidth = toolbar.getBoundingClientRect().width;
+
+      // Hide overflowing columns.
+      columns.forEach(([className, attribute]) => {
+        let bounds = $(".requests-menu-header" + className).getBoundingClientRect();
+        if (bounds.right > availableWidth - REQUESTS_HEADERS_SAFE_BOUNDS) {
+          table.setAttribute(attribute, "");
+        }
+      });
     }
 
     // Determine the scaling to be applied to all the waterfalls so that
     // everything is visible at once. One millisecond == one unscaled pixel.
     let availableWidth = this._waterfallWidth - REQUESTS_WATERFALL_SAFE_BOUNDS;
     let longestWidth = this._lastRequestEndedMillis - this._firstRequestStartedMillis;
     let scale = Math.min(Math.max(availableWidth / longestWidth, EPSILON), 1);
 
--- a/browser/devtools/netmonitor/netmonitor.css
+++ b/browser/devtools/netmonitor/netmonitor.css
@@ -8,13 +8,41 @@
 }
 
 #timings-summary-blocked {
   display: none; /* This doesn't work yet. */
 }
 
 /* Responsive sidebar */
 @media (max-width: 700px) {
+  #toolbar-spacer,
   #details-pane-toggle,
+  #details-pane[pane-collapsed],
   .requests-menu-waterfall {
     display: none;
   }
 }
+
+@media (min-width: 701px) {
+  #network-table[waterfall-overflows] .requests-menu-waterfall {
+    display: none;
+  }
+
+  #network-table[size-overflows] .requests-menu-size {
+    display: none;
+  }
+
+  #network-table[type-overflows] .requests-menu-type {
+    display: none;
+  }
+
+  #network-table[domain-overflows] .requests-menu-domain {
+    display: none;
+  }
+
+  #network-table[type-overflows] .requests-menu-domain {
+    -moz-box-flex: 1;
+  }
+
+  #network-table[domain-overflows] .requests-menu-file {
+    -moz-box-flex: 1;
+  }
+}
--- a/browser/devtools/netmonitor/netmonitor.xul
+++ b/browser/devtools/netmonitor/netmonitor.xul
@@ -41,17 +41,17 @@
         <label id="requests-menu-size-label"
                class="plain requests-menu-header requests-menu-size"
                value="&netmonitorUI.toolbar.size;"
                crop="end"/>
         <label id="requests-menu-waterfall-label"
                class="plain requests-menu-header requests-menu-waterfall"
                value="&netmonitorUI.toolbar.waterfall;"
                crop="end"/>
-        <spacer flex="1"/>
+        <spacer id="toolbar-spacer" flex="1"/>
         <toolbarbutton id="details-pane-toggle"
                        class="devtools-toolbarbutton"
                        tooltiptext="&netmonitorUI.panesButton.tooltip;"
                        tabindex="0"/>
       </toolbar>
       <label class="plain requests-menu-empty-notice"
              value="&netmonitorUI.emptyNotice;"/>
       <vbox id="requests-menu-contents" flex="1">
--- a/browser/devtools/netmonitor/test/browser_net_prefs-reload.js
+++ b/browser/devtools/netmonitor/test/browser_net_prefs-reload.js
@@ -4,23 +4,30 @@
 /**
  * Tests if the prefs that should survive across tool reloads work.
  */
 
 function test() {
   initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
     info("Starting test... ");
 
+    // This test reopens the network monitor a bunch of times, for different
+    // hosts (bottom, side, window). This seems to be slow on debug builds.
+    requestLongerTimeout(2);
+
     let prefsToCheck = {
       networkDetailsWidth: {
         newValue: ~~(Math.random() * 200 + 100),
-        validate: () =>
-          ~~aMonitor._view._detailsPane.getAttribute("width"),
-        modifyFrontend: (aValue) =>
-          aMonitor._view._detailsPane.setAttribute("width", aValue)
+        validate: ($) => ~~$("#details-pane").getAttribute("width"),
+        modifyFrontend: ($, aValue) => $("#details-pane").setAttribute("width", aValue)
+      },
+      networkDetailsHeight: {
+        newValue: ~~(Math.random() * 300 + 100),
+        validate: ($) => ~~$("#details-pane").getAttribute("height"),
+        modifyFrontend: ($, aValue) => $("#details-pane").setAttribute("height", aValue)
       },
       /* add more prefs here... */
     };
 
     function storeFirstPrefValues() {
       info("Caching initial pref values.");
 
       for (let name in prefsToCheck) {
@@ -34,97 +41,177 @@ function test() {
 
       for (let name in prefsToCheck) {
         let currentValue = aMonitor.panelWin.Prefs[name];
         let firstValue = prefsToCheck[name].firstValue;
         let validate = prefsToCheck[name].validate;
 
         is(currentValue, firstValue,
           "Pref " + name + " should be equal to first value: " + firstValue);
-        is(currentValue, validate(),
+        is(currentValue, validate(aMonitor.panelWin.$),
           "Pref " + name + " should validate: " + currentValue);
       }
     }
 
     function modifyFrontend() {
       info("Modifying UI elements to the specified new values.");
 
       for (let name in prefsToCheck) {
         let currentValue = aMonitor.panelWin.Prefs[name];
         let firstValue = prefsToCheck[name].firstValue;
         let newValue = prefsToCheck[name].newValue;
         let validate = prefsToCheck[name].validate;
         let modifyFrontend = prefsToCheck[name].modifyFrontend;
 
-        modifyFrontend(newValue);
+        modifyFrontend(aMonitor.panelWin.$, newValue);
         info("Modified UI element affecting " + name + " to: " + newValue);
 
         is(currentValue, firstValue,
           "Pref " + name + " should still be equal to first value: " + firstValue);
         isnot(currentValue, newValue,
           "Pref " + name + " should't yet be equal to second value: " + newValue);
-        is(newValue, validate(),
+        is(newValue, validate(aMonitor.panelWin.$),
           "The UI element affecting " + name + " should validate: " + newValue);
       }
     }
 
     function validateNewPrefValues() {
       info("Invalidating old pref values to the modified UI elements.");
 
       for (let name in prefsToCheck) {
         let currentValue = aMonitor.panelWin.Prefs[name];
         let firstValue = prefsToCheck[name].firstValue;
         let newValue = prefsToCheck[name].newValue;
         let validate = prefsToCheck[name].validate;
-        let modifyFrontend = prefsToCheck[name].modifyFrontend;
 
         isnot(currentValue, firstValue,
           "Pref " + name + " should't be equal to first value: " + firstValue);
         is(currentValue, newValue,
           "Pref " + name + " should now be equal to second value: " + newValue);
-        is(newValue, validate(),
+        is(newValue, validate(aMonitor.panelWin.$),
           "The UI element affecting " + name + " should validate: " + newValue);
       }
     }
 
     function resetFrontend() {
       info("Resetting UI elements to the cached initial pref values.");
 
       for (let name in prefsToCheck) {
         let currentValue = aMonitor.panelWin.Prefs[name];
         let firstValue = prefsToCheck[name].firstValue;
         let newValue = prefsToCheck[name].newValue;
         let validate = prefsToCheck[name].validate;
         let modifyFrontend = prefsToCheck[name].modifyFrontend;
 
-        modifyFrontend(firstValue);
+        modifyFrontend(aMonitor.panelWin.$, firstValue);
         info("Modified UI element affecting " + name + " to: " + firstValue);
 
         isnot(currentValue, firstValue,
           "Pref " + name + " should't yet be equal to first value: " + firstValue);
         is(currentValue, newValue,
           "Pref " + name + " should still be equal to second value: " + newValue);
-        is(firstValue, validate(),
+        is(firstValue, validate(aMonitor.panelWin.$),
           "The UI element affecting " + name + " should validate: " + firstValue);
       }
     }
 
-    storeFirstPrefValues();
+    function testBottom() {
+      info("Testing prefs reload for a bottom host.");
+      storeFirstPrefValues();
+
+      // Validate and modify while toolbox is on the bottom.
+      validateFirstPrefValues();
+      modifyFrontend();
+
+      return restartNetMonitor(aMonitor)
+        .then(([,, aNewMonitor]) => {
+          aMonitor = aNewMonitor;
+
+          // Revalidate and reset frontend while toolbox is on the bottom.
+          validateNewPrefValues();
+          resetFrontend();
+
+          return restartNetMonitor(aMonitor);
+        })
+        .then(([,, aNewMonitor]) => {
+          aMonitor = aNewMonitor;
 
-    // Validate and modify.
-    validateFirstPrefValues();
-    modifyFrontend();
-    restartNetMonitor(aMonitor).then(([,, aNewMonitor]) => {
-      aMonitor = aNewMonitor;
+          // Revalidate.
+          validateFirstPrefValues();
+        });
+    }
+
+    function testSide() {
+      info("Moving toolbox to the side...");
+
+      return aMonitor._toolbox.switchHost(Toolbox.HostType.SIDE)
+        .then(() => {
+          info("Testing prefs reload for a side host.");
+          storeFirstPrefValues();
+
+          // Validate and modify frontend while toolbox is on the side.
+          validateFirstPrefValues();
+          modifyFrontend();
+
+          return restartNetMonitor(aMonitor);
+        })
+        .then(([,, aNewMonitor]) => {
+          aMonitor = aNewMonitor;
+
+          // Revalidate and reset frontend while toolbox is on the side.
+          validateNewPrefValues();
+          resetFrontend();
 
-      // Revalidate and reset.
-      validateNewPrefValues();
-      resetFrontend();
-      restartNetMonitor(aMonitor).then(([,, aNewMonitor]) => {
-        aMonitor = aNewMonitor;
+          return restartNetMonitor(aMonitor);
+        })
+        .then(([,, aNewMonitor]) => {
+          aMonitor = aNewMonitor;
+
+          // Revalidate.
+          validateFirstPrefValues();
+        });
+    }
+
+    function testWindow() {
+      info("Moving toolbox into a window...");
+
+      return aMonitor._toolbox.switchHost(Toolbox.HostType.WINDOW)
+        .then(() => {
+          info("Testing prefs reload for a window host.");
+          storeFirstPrefValues();
+
+          // Validate and modify frontend while toolbox is in a window.
+          validateFirstPrefValues();
+          modifyFrontend();
+
+          return restartNetMonitor(aMonitor);
+        })
+        .then(([,, aNewMonitor]) => {
+          aMonitor = aNewMonitor;
 
-        // Revalidate and finish.
-        validateFirstPrefValues();
-        teardown(aMonitor).then(finish);
-      });
-    });
+          // Revalidate and reset frontend while toolbox is in a window.
+          validateNewPrefValues();
+          resetFrontend();
+
+          return restartNetMonitor(aMonitor);
+        })
+        .then(([,, aNewMonitor]) => {
+          aMonitor = aNewMonitor;
+
+          // Revalidate.
+          validateFirstPrefValues();
+        });
+    }
+
+    function cleanupAndFinish() {
+      info("Moving toolbox back to the bottom...");
+
+      aMonitor._toolbox.switchHost(Toolbox.HostType.BOTTOM)
+        .then(() => teardown(aMonitor))
+        .then(finish);
+    }
+
+    testBottom()
+      .then(testSide)
+      .then(testWindow)
+      .then(cleanupAndFinish);
   });
 }
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -2,16 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 let { Promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {});
 let { TargetFactory } = Cu.import("resource:///modules/devtools/Target.jsm", {});
+let { Toolbox } = Cu.import("resource:///modules/devtools/Toolbox.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/";
 
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
 const CONTENT_TYPE_URL = EXAMPLE_URL + "html_content-type-test-page.html";
 const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
--- a/browser/devtools/shared/DeveloperToolbar.jsm
+++ b/browser/devtools/shared/DeveloperToolbar.jsm
@@ -501,17 +501,17 @@ DeveloperToolbar.prototype.destroy = fun
 {
   if (this._lastState == NOTIFICATIONS.HIDE) {
     return;
   }
 
   let tabbrowser = this._chromeWindow.getBrowser();
   tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
   tabbrowser.tabContainer.removeEventListener("TabClose", this, false);
-  tabbrowser.removeEventListener("load", this, true); 
+  tabbrowser.removeEventListener("load", this, true);
   tabbrowser.removeEventListener("beforeunload", this, true);
 
   Array.prototype.forEach.call(tabbrowser.tabs, this._stopErrorsCount, this);
 
   this.display.focusManager.removeMonitoredElement(this.outputPanel._frame);
   this.display.focusManager.removeMonitoredElement(this._element);
 
   this.display.onVisibilityChange.remove(this.outputPanel._visibilityChanged, this.outputPanel);
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -47,16 +47,17 @@ const STR = Services.strings.createBundl
  * @param nsIDOMNode aParentNode
  *        The parent node to hold this view.
  * @param object aFlags [optional]
  *        An object contaning initialization options for this view.
  *        e.g. { lazyEmpty: true, searchEnabled: true ... }
  */
 this.VariablesView = function VariablesView(aParentNode, aFlags = {}) {
   this._store = new Map();
+  this._items = [];
   this._itemsByElement = new WeakMap();
   this._prevHierarchy = new Map();
   this._currHierarchy = new Map();
 
   this._parent = aParentNode;
   this._parent.classList.add("variables-view-container");
   this._appendEmptyNotice();
 
@@ -98,16 +99,17 @@ VariablesView.prototype = {
    *         The newly created Scope instance.
    */
   addScope: function VV_addScope(aName = "") {
     this._removeEmptyNotice();
     this._toggleSearchVisibility(true);
 
     let scope = new Scope(this, aName);
     this._store.set(scope.id, scope);
+    this._items.push(scope);
     this._currHierarchy.set(aName, scope);
     this._itemsByElement.set(scope._target, scope);
     scope.header = !!aName;
     return scope;
   },
 
   /**
    * Removes all items from this container.
@@ -130,16 +132,17 @@ VariablesView.prototype = {
     let list = this._list;
     let firstChild;
 
     while (firstChild = list.firstChild) {
       list.removeChild(firstChild);
     }
 
     this._store.clear();
+    this._items.length = 0;
     this._itemsByElement.clear();
 
     this._appendEmptyNotice();
     this._toggleSearchVisibility(false);
   },
 
   /**
    * Emptying this container and rebuilding it immediately afterwards would
@@ -156,16 +159,17 @@ VariablesView.prototype = {
    * @see VariablesView.empty
    * @see VariablesView.commitHierarchy
    */
   _emptySoon: function VV__emptySoon(aTimeout) {
     let prevList = this._list;
     let currList = this._list = this.document.createElement("scrollbox");
 
     this._store.clear();
+    this._items.length = 0;
     this._itemsByElement.clear();
 
     this._emptyTimeout = this.window.setTimeout(function() {
       this._emptyTimeout = null;
 
       prevList.removeEventListener("keypress", this._onViewKeyPress, false);
       currList.addEventListener("keypress", this._onViewKeyPress, false);
       currList.setAttribute("orient", "vertical");
@@ -524,74 +528,83 @@ VariablesView.prototype = {
       let match = scope._firstMatch;
       if (match) {
         match.expand();
       }
     }
   },
 
   /**
-   * Focuses the first visible variable or property in this container.
+   * Find the first item in the tree of visible items in this container that
+   * matches the predicate. Searches in visual order (the order seen by the
+   * user). Descends into each scope to check the scope and its children.
+   *
+   * @param function aPredicate
+   *        A function that returns true when a match is found.
+   * @return Scope | Variable | Property
+   *         The first visible scope, variable or property, or null if nothing
+   *         is found.
+   */
+  _findInVisibleItems: function VV__findInVisibleItems(aPredicate) {
+    for (let scope of this._items) {
+      let result = scope._findInVisibleItems(aPredicate);
+      if (result) {
+        return result;
+      }
+    }
+    return null;
+  },
+
+  /**
+   * Find the last item in the tree of visible items in this container that
+   * matches the predicate. Searches in reverse visual order (opposite of the
+   * order seen by the user). Descends into each scope to check the scope and
+   * its children.
+   *
+   * @param function aPredicate
+   *        A function that returns true when a match is found.
+   * @return Scope | Variable | Property
+   *         The last visible scope, variable or property, or null if nothing
+   *         is found.
+   */
+  _findInVisibleItemsReverse: function VV__findInVisibleItemsReverse(aPredicate) {
+    for (let i = this._items.length - 1; i >= 0; i--) {
+      let scope = this._items[i];
+      let result = scope._findInVisibleItemsReverse(aPredicate);
+      if (result) {
+        return result;
+      }
+    }
+    return null;
+  },
+
+  /**
+   * Focuses the first visible scope, variable, or property in this container.
    */
   focusFirstVisibleNode: function VV_focusFirstVisibleNode() {
-    let property, variable, scope;
-
-    for (let [, item] of this._currHierarchy) {
-      if (!item.focusable) {
-        continue;
-      }
-      if (item instanceof Property) {
-        property = item;
-        break;
-      } else if (item instanceof Variable) {
-        variable = item;
-        break;
-      } else if (item instanceof Scope) {
-        scope = item;
-        break;
-      }
-    }
-    if (scope) {
-      this._focusItem(scope);
-    } else if (variable) {
-      this._focusItem(variable);
-    } else if (property) {
-      this._focusItem(property);
+    let focusableItem = this._findInVisibleItems(item => item.focusable);
+
+    if (focusableItem) {
+      this._focusItem(focusableItem);
     }
     this._parent.scrollTop = 0;
     this._parent.scrollLeft = 0;
   },
 
   /**
-   * Focuses the last visible variable or property in this container.
+   * Focuses the last visible scope, variable, or property in this container.
    */
   focusLastVisibleNode: function VV_focusLastVisibleNode() {
-    let property, variable, scope;
-
-    for (let [, item] of this._currHierarchy) {
-      if (!item.focusable) {
-        continue;
-      }
-      if (item instanceof Property) {
-        property = item;
-      } else if (item instanceof Variable) {
-        variable = item;
-      } else if (item instanceof Scope) {
-        scope = item;
-      }
+    let focusableItem = this._findInVisibleItemsReverse(item => item.focusable);
+
+    if (focusableItem) {
+      this._focusItem(focusableItem);
     }
-    if (property && (!variable || property.isDescendantOf(variable))) {
-      this._focusItem(property);
-    } else if (variable && (!scope || variable.isDescendantOf(scope))) {
-      this._focusItem(variable);
-    } else if (scope) {
-      this._focusItem(scope);
-      this._parent.scrollTop = this._parent.scrollHeight;
-      this._parent.scrollLeft = 0;
-    }
+    this._parent.scrollTop = this._parent.scrollHeight;
+    this._parent.scrollLeft = 0;
   },
 
   /**
    * Searches for the scope in this container displayed by the specified node.
    *
    * @param nsIDOMNode aNode
    *        The node to search for.
    * @return Scope
@@ -883,16 +896,17 @@ VariablesView.prototype = {
    * @return nsIDOMWindow
    */
   get window() this._window || (this._window = this.document.defaultView),
 
   _document: null,
   _window: null,
 
   _store: null,
+  _items: null,
   _prevHierarchy: null,
   _currHierarchy: null,
   _enumVisible: true,
   _nonEnumVisible: true,
   _emptyTimeout: null,
   _searchTimeout: null,
   _searchFunction: null,
   _parent: null,
@@ -1077,16 +1091,18 @@ function Scope(aView, aName, aFlags = {}
   this.editableNameTooltip = aView.editableNameTooltip;
   this.editButtonTooltip = aView.editButtonTooltip;
   this.deleteButtonTooltip = aView.deleteButtonTooltip;
   this.descriptorTooltip = aView.descriptorTooltip;
   this.contextMenuId = aView.contextMenuId;
   this.separatorStr = aView.separatorStr;
 
   this._store = new Map();
+  this._enumItems = [];
+  this._nonEnumItems = [];
   this._init(aName.trim(), aFlags);
 }
 
 Scope.prototype = {
   /**
    * Adds a variable to contain any inspected properties.
    *
    * @param string aName
@@ -1781,16 +1797,99 @@ Scope.prototype = {
       if (match) {
         return match;
       }
     }
     return null;
   },
 
   /**
+   * Find the first item in the tree of visible items in this item that matches
+   * the predicate. Searches in visual order (the order seen by the user).
+   * Tests itself, then descends into first the enumerable children and then
+   * the non-enumerable children (since they are presented in separate groups).
+   *
+   * @param function aPredicate
+   *        A function that returns true when a match is found.
+   * @return Scope | Variable | Property
+   *         The first visible scope, variable or property, or null if nothing
+   *         is found.
+   */
+  _findInVisibleItems: function S__findInVisibleItems(aPredicate) {
+    if (aPredicate(this)) {
+      return this;
+    }
+
+    if (this._isExpanded) {
+      if (this._variablesView._enumVisible) {
+        for (let item of this._enumItems) {
+          let result = item._findInVisibleItems(aPredicate);
+          if (result) {
+            return result;
+          }
+        }
+      }
+
+      if (this._variablesView._nonEnumVisible) {
+        for (let item of this._nonEnumItems) {
+          let result = item._findInVisibleItems(aPredicate);
+          if (result) {
+            return result;
+          }
+        }
+      }
+    }
+
+    return null;
+  },
+
+  /**
+   * Find the last item in the tree of visible items in this item that matches
+   * the predicate. Searches in reverse visual order (opposite of the order
+   * seen by the user). Descends into first the non-enumerable children, then
+   * the enumerable children (since they are presented in separate groups), and
+   * finally tests itself.
+   *
+   * @param function aPredicate
+   *        A function that returns true when a match is found.
+   * @return Scope | Variable | Property
+   *         The last visible scope, variable or property, or null if nothing
+   *         is found.
+   */
+  _findInVisibleItemsReverse: function S__findInVisibleItemsReverse(aPredicate) {
+    if (this._isExpanded) {
+      if (this._variablesView._nonEnumVisible) {
+        for (let i = this._nonEnumItems.length - 1; i >= 0; i--) {
+          let item = this._nonEnumItems[i];
+          let result = item._findInVisibleItemsReverse(aPredicate);
+          if (result) {
+            return result;
+          }
+        }
+      }
+
+      if (this._variablesView._enumVisible) {
+        for (let i = this._enumItems.length - 1; i >= 0; i--) {
+          let item = this._enumItems[i];
+          let result = item._findInVisibleItemsReverse(aPredicate);
+          if (result) {
+            return result;
+          }
+        }
+      }
+    }
+
+    if (aPredicate(this)) {
+      return this;
+    }
+
+    return null;
+  },
+
+  /**
    * Gets top level variables view instance.
    * @return VariablesView
    */
   get _variablesView() this._topView || (this._topView = (function(self) {
     let parentView = self.ownerView;
     let topView;
 
     while (topView = parentView.ownerView) {
@@ -1849,17 +1948,19 @@ Scope.prototype = {
   _isMatch: true,
   _idString: "",
   _nameString: "",
   _target: null,
   _arrow: null,
   _name: null,
   _title: null,
   _enum: null,
+  _enumItems: null,
   _nonenum: null,
+  _nonEnumItems: null,
   _throbber: null
 };
 
 /**
  * A Variable is a Scope holding Property instances.
  * Iterable via "for (let [name, property] in instance) { }".
  *
  * @param Scope aScope
@@ -2162,18 +2263,20 @@ ViewHelpers.create({ constructor: Variab
    * @param boolean aImmediateFlag
    *        @see Scope.prototype._lazyAppend
    */
   _onInit: function V__onInit(aImmediateFlag) {
     if (this._initialDescriptor.enumerable ||
         this._nameString == "this" ||
         this._nameString == "<exception>") {
       this.ownerView._lazyAppend(aImmediateFlag, true, this._target);
+      this.ownerView._enumItems.push(this);
     } else {
       this.ownerView._lazyAppend(aImmediateFlag, false, this._target);
+      this.ownerView._nonEnumItems.push(this);
     }
   },
 
   /**
    * Creates the necessary nodes for this variable.
    */
   _displayVariable: function V__createVariable() {
     let document = this.document;
@@ -2669,18 +2772,20 @@ ViewHelpers.create({ constructor: Proper
    * be attached to the owner view.
    *
    * @param boolean aImmediateFlag
    *        @see Scope.prototype._lazyAppend
    */
   _onInit: function P__onInit(aImmediateFlag) {
     if (this._initialDescriptor.enumerable) {
       this.ownerView._lazyAppend(aImmediateFlag, true, this._target);
+      this.ownerView._enumItems.push(this);
     } else {
       this.ownerView._lazyAppend(aImmediateFlag, false, this._target);
+      this.ownerView._nonEnumItems.push(this);
     }
   }
 });
 
 /**
  * A generator-iterator over the VariablesView, Scopes, Variables and Properties.
  */
 VariablesView.prototype.__iterator__ =
--- a/browser/devtools/webconsole/HUDService.jsm
+++ b/browser/devtools/webconsole/HUDService.jsm
@@ -658,19 +658,29 @@ var HeadsUpDisplayUICommands = {
 
       if (!DebuggerServer.initialized) {
         DebuggerServer.init();
         DebuggerServer.addBrowserActors();
       }
 
       let client = new DebuggerClient(DebuggerServer.connectPipe());
       client.connect(() =>
-        client.listTabs((aResponse) =>
-          deferred.resolve({ form: aResponse, client: client })
-        ));
+        client.listTabs((aResponse) => {
+          // Add Global Process debugging...
+          let globals = JSON.parse(JSON.stringify(aResponse));
+          delete globals.tabs;
+          delete globals.selected;
+          // ...only if there are appropriate actors (a 'from' property will
+          // always be there).
+          if (Object.keys(globals).length > 1) {
+            deferred.resolve({ form: globals, client: client, chrome: true });
+          } else {
+            deferred.reject("Global console not found!");
+          }
+        }));
 
       return deferred.promise;
     }
 
     let target;
     function getTarget(aConnection)
     {
       let options = {
--- a/browser/devtools/webconsole/webconsole.xul
+++ b/browser/devtools/webconsole/webconsole.xul
@@ -57,18 +57,18 @@
       <menuitem id="saveBodiesContextMenu" type="checkbox" label="&saveBodies.label;"
                 accesskey="&saveBodies.accesskey;"/>
       <menuitem id="menu_openURL" label="&openURL.label;"
                 accesskey="&openURL.accesskey;" command="consoleCmd_openURL"
                 selection="network" selectionType="single"/>
       <menuitem id="menu_copyURL" label="&copyURLCmd.label;"
                 accesskey="&copyURLCmd.accesskey;" command="consoleCmd_copyURL"
                 selection="network" selectionType="single"/>
-      <menuitem id="menu_copy"/>
-      <menuitem id="menu_selectAll"/>
+      <menuitem id="cMenu_copy"/>
+      <menuitem id="cMenu_selectAll"/>
     </menupopup>
   </popupset>
 
   <box class="hud-outer-wrapper devtools-responsive-container" flex="1">
     <vbox class="hud-console-wrapper" flex="1">
       <toolbar class="hud-console-filter-toolbar devtools-toolbar" mode="full">
         <toolbarbutton label="&btnPageNet.label;" type="menu-button"
                        category="net" class="devtools-toolbarbutton webconsole-filter-button"
--- a/browser/themes/linux/devtools/netmonitor.css
+++ b/browser/themes/linux/devtools/netmonitor.css
@@ -88,21 +88,23 @@
 }
 
 .requests-menu-method {
   text-align: center;
   font-weight: 600;
 }
 
 .requests-menu-file {
-  width: 14em;
+  width: 20vw;
+  min-width: 4em;
 }
 
 .requests-menu-domain {
-  width: 14em;
+  width: 14vw;
+  min-width: 10em;
 }
 
 .requests-menu-type {
   text-align: center;
   width: 4em;
 }
 
 .requests-menu-size {
@@ -298,18 +300,49 @@
   transition: transform 0.2s ease-out;
 }
 
 /* Responsive sidebar */
 @media (max-width: 700px) {
   #details-pane {
     max-width: none;
     margin: 0 !important;
-    /* To prevent all the margin hacks to hide the sidebar */
+    /* To prevent all the margin hacks to hide the sidebar. */
+  }
+
+  .requests-menu-status-and-method {
+    width: 14vw;
+  }
+
+  .requests-menu-file,
+  .requests-menu-domain {
+    width: 30vw;
+    min-width: 10em;
+  }
+
+  .requests-menu-type {
+    width: 8vw;
   }
 
   .requests-menu-size {
-    border-width: 0px !important;
+    width: 16vw;
+    border-width: 0 !important;
     box-shadow: none !important;
-    /* !important are required here because Timeline is not visible and thus
-       the right border and box-shadow of Size column should be hidden */
+    /* The "Timeline" header is not visible anymore, and thus the
+       right border and box-shadow of "Size" column should be hidden. */
   }
 }
+
+@media (min-width: 701px) {
+  #network-table[type-overflows] .requests-menu-domain {
+    border-width: 0 !important;
+    box-shadow: none !important;
+    /* The "Type" header is not visible anymore, and thus the
+       right border and box-shadow of "Domain" column should be hidden. */
+  }
+
+  #network-table[domain-overflows] .requests-menu-file {
+    border-width: 0 !important;
+    box-shadow: none !important;
+    /* The "Domain" header is not visible anymore, and thus the
+       right border and box-shadow of "File" column should be hidden. */
+  }
+}
--- a/browser/themes/osx/devtools/netmonitor.css
+++ b/browser/themes/osx/devtools/netmonitor.css
@@ -88,21 +88,23 @@
 }
 
 .requests-menu-method {
   text-align: center;
   font-weight: 600;
 }
 
 .requests-menu-file {
-  width: 16em;
+  width: 20vw;
+  min-width: 4em;
 }
 
 .requests-menu-domain {
-  width: 16em;
+  width: 14vw;
+  min-width: 10em;
 }
 
 .requests-menu-type {
   text-align: center;
   width: 4em;
 }
 
 .requests-menu-size {
@@ -298,18 +300,49 @@
   transition: transform 0.2s ease-out;
 }
 
 /* Responsive sidebar */
 @media (max-width: 700px) {
   #details-pane {
     max-width: none;
     margin: 0 !important;
-    /* To prevent all the margin hacks to hide the sidebar */
+    /* To prevent all the margin hacks to hide the sidebar. */
+  }
+
+  .requests-menu-status-and-method {
+    width: 14vw;
+  }
+
+  .requests-menu-file,
+  .requests-menu-domain {
+    width: 30vw;
+    min-width: 10em;
+  }
+
+  .requests-menu-type {
+    width: 8vw;
   }
 
   .requests-menu-size {
-    border-width: 0px !important;
+    width: 16vw;
+    border-width: 0 !important;
     box-shadow: none !important;
-    /* !important are required here because Timeline is not visible and thus
-       the right border and box-shadow of Size column should be hidden */
+    /* The "Timeline" header is not visible anymore, and thus the
+       right border and box-shadow of "Size" column should be hidden. */
   }
 }
+
+@media (min-width: 701px) {
+  #network-table[type-overflows] .requests-menu-domain {
+    border-width: 0 !important;
+    box-shadow: none !important;
+    /* The "Type" header is not visible anymore, and thus the
+       right border and box-shadow of "Domain" column should be hidden. */
+  }
+
+  #network-table[domain-overflows] .requests-menu-file {
+    border-width: 0 !important;
+    box-shadow: none !important;
+    /* The "Domain" header is not visible anymore, and thus the
+       right border and box-shadow of "File" column should be hidden. */
+  }
+}
--- a/browser/themes/windows/devtools/netmonitor.css
+++ b/browser/themes/windows/devtools/netmonitor.css
@@ -88,21 +88,23 @@
 }
 
 .requests-menu-method {
   text-align: center;
   font-weight: 600;
 }
 
 .requests-menu-file {
-  width: 16em;
+  width: 20vw;
+  min-width: 4em;
 }
 
 .requests-menu-domain {
-  width: 16em;
+  width: 14vw;
+  min-width: 10em;
 }
 
 .requests-menu-type {
   text-align: center;
   width: 4em;
 }
 
 .requests-menu-size {
@@ -298,18 +300,49 @@
   transition: transform 0.2s ease-out;
 }
 
 /* Responsive sidebar */
 @media (max-width: 700px) {
   #details-pane {
     max-width: none;
     margin: 0 !important;
-    /* To prevent all the margin hacks to hide the sidebar */
+    /* To prevent all the margin hacks to hide the sidebar. */
+  }
+
+  .requests-menu-status-and-method {
+    width: 14vw;
+  }
+
+  .requests-menu-file,
+  .requests-menu-domain {
+    width: 30vw;
+    min-width: 10em;
+  }
+
+  .requests-menu-type {
+    width: 8vw;
   }
 
   .requests-menu-size {
-    border-width: 0px !important;
+    width: 16vw;
+    border-width: 0 !important;
     box-shadow: none !important;
-    /* !important are required here because Timeline is not visible and thus
-       the right border and box-shadow of Size column should be hidden */
+    /* The "Timeline" header is not visible anymore, and thus the
+       right border and box-shadow of "Size" column should be hidden. */
   }
 }
+
+@media (min-width: 701px) {
+  #network-table[type-overflows] .requests-menu-domain {
+    border-width: 0 !important;
+    box-shadow: none !important;
+    /* The "Type" header is not visible anymore, and thus the
+       right border and box-shadow of "Domain" column should be hidden. */
+  }
+
+  #network-table[domain-overflows] .requests-menu-file {
+    border-width: 0 !important;
+    box-shadow: none !important;
+    /* The "Domain" header is not visible anymore, and thus the
+       right border and box-shadow of "File" column should be hidden. */
+  }
+}
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -1964,16 +1964,17 @@ this.DOMApplicationRegistry = {
         }).bind(this));
       }).bind(this));
     }
   },
 
   _nextLocalId: function() {
     let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1;
     Services.prefs.setIntPref("dom.mozApps.maxLocalId", id);
+    Services.prefs.savePrefFile(null);
     return id;
   },
 
   _appId: function(aURI) {
     for (let id in this.webapps) {
       if (this.webapps[id].origin == aURI)
         return id;
     }
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -24,17 +24,19 @@
 #include "nsIObserverService.h"
 #include "nsIDOMFile.h"
 #include "nsIFile.h"
 #include "nsIInputStream.h"
 #include "nsIMIMEService.h"
 #include "nsIOutputStream.h"
 #include "nsNetUtil.h"
 
-#define TARGET_FOLDER "/sdcard/downloads/bluetooth/"
+#define TARGET_ROOT   "/sdcard/"
+#define TARGET_SUBDIR "downloads/bluetooth/"
+#define TARGET_FOLDER TARGET_ROOT TARGET_SUBDIR
 
 USING_BLUETOOTH_NAMESPACE
 using namespace mozilla;
 using namespace mozilla::ipc;
 
 class BluetoothOppManagerObserver : public nsIObserver
 {
 public:
@@ -556,30 +558,35 @@ BluetoothOppManager::CreateFile()
 
   /*
    * The function CreateUnique() may create a file with a different file
    * name from the original sFileName. Therefore we have to retrieve
    * the file name again.
    */
   f->GetLeafName(sFileName);
 
+  nsString fullFileName;
+  f->GetPath(fullFileName);
+  MOZ_ASSERT(StringBeginsWith(fullFileName, NS_LITERAL_STRING(TARGET_ROOT)));
+  nsDependentSubstring storagePath = Substring(fullFileName, strlen(TARGET_ROOT));
+
   mDsFile = nullptr;
 
   nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
   if (mimeSvc) {
     nsCString mimeType;
     nsresult rv = mimeSvc->GetTypeFromFile(f, mimeType);
 
     if (NS_SUCCEEDED(rv)) {
       if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("image/"))) {
-        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("pictures"), f);
+        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("pictures"), storagePath);
       } else if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("video/"))) {
-        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("movies"), f);
+        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("movies"), storagePath);
       } else if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("audio/"))) {
-        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("music"), f);
+        mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("music"), storagePath);
       } else {
         NS_WARNING("Couldn't recognize the mimetype of received file.");
       }
     }
   }
 
   NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f);
   if (!mOutputStream) {
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -900,17 +900,18 @@ nsGonkCameraControl::StartRecordingImpl(
    * filename to it.  The filename may include a relative subpath
    * (e.g.) "DCIM/IMG_0001.jpg".
    *
    * The camera app needs to provide the file extension '.3gp' for now.
    * See bug 795202.
    */
   nsCOMPtr<nsIFile> filename = aStartRecording->mFolder;
   filename->AppendRelativePath(aStartRecording->mFilename);
-  mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"), filename);
+  mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"),
+                                     aStartRecording->mFilename);
 
   nsAutoCString nativeFilename;
   filename->GetNativePath(nativeFilename);
   DOM_CAMERA_LOGI("Video filename is '%s'\n", nativeFilename.get());
 
   if (!mVideoFile->IsSafePath()) {
     DOM_CAMERA_LOGE("Invalid video file name\n");
     return NS_ERROR_INVALID_ARG;
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -21,71 +21,82 @@ Cu.import("resource://gre/modules/Timer.
 const DB_NAME = "contacts";
 const DB_VERSION = 8;
 const STORE_NAME = "contacts";
 const SAVED_GETALL_STORE_NAME = "getallcache";
 const CHUNK_SIZE = 20;
 const CHUNK_INTERVAL = 500;
 
 function ContactDispatcher(aContacts, aFullContacts, aCallback, aNewTxn, aClearDispatcher) {
-  this.nextIndex = 0;
+  let nextIndex = 0;
+  let interval;
 
-  this.cancelTimeout = function() {
-    if (this.interval) {
-      clearTimeout(this.interval);
-      this.interval = null;
+  function cancelTimeout() {
+    if (interval) {
+      clearTimeout(interval);
+      interval = null;
     }
-  };
+  }
 
+  let sendChunk;
+  let count = 0;
   if (aFullContacts) {
-    this.sendChunk = function() {
-      if (aContacts.length > 0) {
-        aCallback(aContacts.splice(0, CHUNK_SIZE));
-        this.interval = setTimeout(this.sendChunk, CHUNK_INTERVAL);
-      } else {
-        aCallback(null);
-        this.cancelTimeout();
+    sendChunk = function() {
+      try {
+        if (aContacts.length > 0) {
+          aCallback(aContacts.splice(0, CHUNK_SIZE));
+          interval = setTimeout(sendChunk, CHUNK_INTERVAL);
+        } else {
+          aCallback(null);
+          cancelTimeout();
+          aClearDispatcher();
+        }
+      } catch (e) {
         aClearDispatcher();
       }
-    }.bind(this);
+    }
   } else {
     this.count = 0;
-    this.sendChunk = function() {
-      let chunk = [];
-      aNewTxn("readonly", STORE_NAME, function(txn, store) {
-        for (let i = this.nextIndex; i < Math.min(this.nextIndex+CHUNK_SIZE, aContacts.length); ++i) {
-          store.get(aContacts[i]).onsuccess = function(e) {
-            chunk.push(e.target.result);
-            this.count++;
-            if (this.count == aContacts.length) {
-              aCallback(chunk)
-              aCallback(null);
-              this.cancelTimeout();
-              aClearDispatcher();
-            } else if (chunk.length == CHUNK_SIZE) {
-              aCallback(chunk);
-              chunk.length = 0;
-              this.nextIndex += CHUNK_SIZE;
-              this.interval = setTimeout(this.sendChunk, CHUNK_INTERVAL);
+    sendChunk = function() {
+      try {
+        let chunk = [];
+        aNewTxn("readonly", STORE_NAME, function(txn, store) {
+          for (let i = nextIndex; i < Math.min(nextIndex+CHUNK_SIZE, aContacts.length); ++i) {
+            store.get(aContacts[i]).onsuccess = function(e) {
+              chunk.push(e.target.result);
+              count++;
+              if (count === aContacts.length) {
+                aCallback(chunk)
+                aCallback(null);
+                cancelTimeout();
+                aClearDispatcher();
+              } else if (chunk.length === CHUNK_SIZE) {
+                aCallback(chunk);
+                chunk.length = 0;
+                nextIndex += CHUNK_SIZE;
+                interval = setTimeout(this.sendChunk, CHUNK_INTERVAL);
+              }
             }
-          }.bind(this);
-        }
-      }.bind(this));
-    }.bind(this);
+          }
+        });
+      } catch (e) {
+        aClearDispatcher();
+      }
+    }
   }
 
-  this.sendChunk(0);
-}
+  sendChunk(0);
 
-ContactDispatcher.prototype = {
-  sendNow: function() {
-    this.cancelTimeout();
-    this.interval = setTimeout(this.sendChunk, 0);
-  }
-};
+  return {
+    sendNow: function() {
+      cancelTimeout();
+      interval = setTimeout(sendChunk, 0);
+    }
+  };
+}
 
 this.ContactDB = function ContactDB(aGlobal) {
   if (DEBUG) debug("Constructor");
   this._global = aGlobal;
 }
 
 ContactDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -109,17 +109,22 @@ let ContactService = {
           msg.options.findOptions);
         break;
       case "Contacts:GetAll":
         if (!this.assertPermission(aMessage, "contacts-read")) {
           return null;
         }
         this._db.getAll(
           function(aContacts) {
-            mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contacts: aContacts});
+            try {
+              mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contacts: aContacts});
+            } catch (e) {
+              if (DEBUG) debug("Child is dead, DB should stop sending contacts");
+              throw e;
+            }
           },
           function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); },
           msg.findOptions, msg.cursorId);
         break;
       case "Contacts:GetAll:SendNow":
         // sendNow is a no op if there isn't an existing cursor in the DB, so we
         // don't need to assert the permission again.
         this._db.sendNow(msg.cursorId);
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -15,40 +15,52 @@
 #include "mozilla/StaticPtr.h"
 
 class DeviceStorageFile MOZ_FINAL
   : public nsISupports {
 public:
   nsCOMPtr<nsIFile> mFile;
   nsString mPath;
   nsString mStorageType;
+  nsString mRootDir;
   bool mEditable;
 
-  DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile, const nsAString& aPath);
-  DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile);
+  // Used when the path will be set later via SetPath.
+  DeviceStorageFile(const nsAString& aStorageType);
+  // Used for non-enumeration purposes.
+  DeviceStorageFile(const nsAString& aStorageType, const nsAString& aPath);
+  // Used for enumerations. When you call Enumerate, you can pass in a directory to enumerate
+  // and the results that are returned are relative to that directory, files related to an
+  // enumeration need to know the "root of the enumeration" directory.
+  DeviceStorageFile(const nsAString& aStorageType, const nsAString& aRootDir, const nsAString& aPath);
+
   void SetPath(const nsAString& aPath);
   void SetEditable(bool aEditable);
 
   NS_DECL_ISUPPORTS
 
   // we want to make sure that the names of file can't reach
   // outside of the type of storage the user asked for.
   bool IsSafePath();
+  bool IsSafePath(const nsAString& aPath);
 
   nsresult Remove();
   nsresult Write(nsIInputStream* aInputStream);
   nsresult Write(InfallibleTArray<uint8_t>& bits);
   void CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRTime aSince = 0);
   void collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles, PRTime aSince, nsAString& aRootPath);
 
   static void DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar, const nsAString& aStorageType);
-
+  static void GetRootDirectoryForType(const nsAString& aType,
+                                      const nsAString& aVolName,
+                                      nsIFile** aFile);
 private:
+  void Init(const nsAString& aStorageType);
   void NormalizeFilePath();
-  void AppendRelativePath();
+  void AppendRelativePath(const nsAString& aPath);
 };
 
 /*
   The FileUpdateDispatcher converts file-watcher-notify
   observer events to file-watcher-update events.  This is
   used to be able to broadcast events from one child to
   another child in B2G.  (f.e., if one child decides to add
   a file, we want to be able to able to send a onchange
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -95,24 +95,19 @@ DeviceStorageRequestChild::Recv__delete_
 
     case DeviceStorageResponseValue::TEnumerationResponse:
     {
       EnumerationResponse r = aValue;
       nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
 
       uint32_t count = r.paths().Length();
       for (uint32_t i = 0; i < count; i++) {
-        nsCOMPtr<nsIFile> f;
-        nsresult rv = NS_NewLocalFile(r.paths()[i].fullpath(), false, getter_AddRefs(f));
-        if (NS_FAILED(rv)) {
-          continue;
-        }
-
-        nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(r.paths()[i].type(), f);
-        dsf->SetPath(r.paths()[i].name());
+        nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(r.type(),
+                                                                r.relpath(),
+                                                                r.paths()[i].name());
         cursor->mFiles.AppendElement(dsf);
       }
 
       nsCOMPtr<ContinueCursorEvent> event = new ContinueCursorEvent(cursor);
       event->Continue();
       break;
     }
 
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -29,20 +29,17 @@ DeviceStorageRequestParent::DeviceStorag
 void
 DeviceStorageRequestParent::Dispatch()
 {
   switch (mParams.type()) {
     case DeviceStorageParams::TDeviceStorageAddParams:
     {
       DeviceStorageAddParams p = mParams;
 
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
 
       BlobParent* bp = static_cast<BlobParent*>(p.blobParent());
       nsCOMPtr<nsIDOMBlob> blob = bp->GetBlob();
 
       nsCOMPtr<nsIInputStream> stream;
       blob->GetInternalStream(getter_AddRefs(stream));
 
       nsRefPtr<CancelableRunnable> r = new WriteFileEvent(this, dsf, stream);
@@ -51,94 +48,78 @@ DeviceStorageRequestParent::Dispatch()
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageGetParams:
     {
       DeviceStorageGetParams p = mParams;
-
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
-      dsf->SetPath(p.name());
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.rootDir(), p.relpath());
       nsRefPtr<CancelableRunnable> r = new ReadFileEvent(this, dsf);
 
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageDeleteParams:
     {
       DeviceStorageDeleteParams p = mParams;
 
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
       nsRefPtr<CancelableRunnable> r = new DeleteFileEvent(this, dsf);
 
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageFreeSpaceParams:
     {
       DeviceStorageFreeSpaceParams p = mParams;
 
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
       nsRefPtr<FreeSpaceFileEvent> r = new FreeSpaceFileEvent(this, dsf);
 
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageUsedSpaceParams:
     {
       DeviceStorageUsedSpaceParams p = mParams;
 
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
       nsRefPtr<UsedSpaceFileEvent> r = new UsedSpaceFileEvent(this, dsf);
 
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageAvailableParams:
     {
       DeviceStorageAvailableParams p = mParams;
-      nsRefPtr<PostAvailableResultEvent> r = new PostAvailableResultEvent(this, p.fullpath());
+
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath());
+      nsRefPtr<PostAvailableResultEvent> r = new PostAvailableResultEvent(this, dsf);
       NS_DispatchToMainThread(r);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageEnumerationParams:
     {
       DeviceStorageEnumerationParams p = mParams;
-
-      nsCOMPtr<nsIFile> f;
-      NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f));
-
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), f);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(p.type(), p.relpath(), NS_LITERAL_STRING(""));
       nsRefPtr<CancelableRunnable> r = new EnumerateFileEvent(this, dsf, p.since());
 
       nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
       NS_ASSERTION(target, "Must have stream transport service");
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
     default:
@@ -377,29 +358,33 @@ DeviceStorageRequestParent::PostBlobSucc
   BlobResponse response;
   response.blobParent() = actor;
 
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent,
+                                                                                     const nsAString& aStorageType,
+                                                                                     const nsAString& aRelPath,
                                                                                      InfallibleTArray<DeviceStorageFileValue>& aPaths)
   : CancelableRunnable(aParent)
+  , mStorageType(aStorageType)
+  , mRelPath(aRelPath)
   , mPaths(aPaths)
 {
 }
 
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::~PostEnumerationSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  EnumerationResponse response(mPaths);
+  EnumerationResponse response(mStorageType, mRelPath, mPaths);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::WriteFileEvent::WriteFileEvent(DeviceStorageRequestParent* aParent,
                                                            DeviceStorageFile* aFile,
                                                            nsIInputStream* aInputStream)
   : CancelableRunnable(aParent)
@@ -616,23 +601,21 @@ DeviceStorageRequestParent::EnumerateFil
 
   nsTArray<nsRefPtr<DeviceStorageFile> > files;
   mFile->CollectFiles(files, mSince);
 
   InfallibleTArray<DeviceStorageFileValue> values;
 
   uint32_t count = files.Length();
   for (uint32_t i = 0; i < count; i++) {
-    nsString fullpath;
-    files[i]->mFile->GetPath(fullpath);
-    DeviceStorageFileValue dsvf(mFile->mStorageType, files[i]->mPath, fullpath);
+    DeviceStorageFileValue dsvf(files[i]->mPath);
     values.AppendElement(dsvf);
   }
 
-  r = new PostEnumerationSuccessEvent(mParent, values);
+  r = new PostEnumerationSuccessEvent(mParent, mFile->mStorageType, mFile->mRootDir, values);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
 
 
 DeviceStorageRequestParent::PostPathResultEvent::PostPathResultEvent(DeviceStorageRequestParent* aParent,
                                                                      const nsAString& aPath)
   : CancelableRunnable(aParent)
@@ -650,35 +633,35 @@ DeviceStorageRequestParent::PostPathResu
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   SuccessResponse response;
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostAvailableResultEvent::PostAvailableResultEvent(DeviceStorageRequestParent* aParent,
-                                                                               const nsAString &aPath)
+                                                                               DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
-  , mPath(aPath)
+  , mFile(aFile)
 {
 }
 
 DeviceStorageRequestParent::PostAvailableResultEvent::~PostAvailableResultEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::PostAvailableResultEvent::CancelableRun()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsString state;
   state.Assign(NS_LITERAL_STRING("available"));
 #ifdef MOZ_WIDGET_GONK
-  nsresult rv = GetSDCardStatus(mPath, state);
+  nsresult rv = GetSDCardStatus(mFile->mPath, state);
   if (NS_FAILED(rv)) {
     state.Assign(NS_LITERAL_STRING("unavailable"));
   }
 #endif
 
   AvailableStorageResponse response(state);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
--- a/dom/devicestorage/DeviceStorageRequestParent.h
+++ b/dom/devicestorage/DeviceStorageRequestParent.h
@@ -100,20 +100,25 @@ private:
       uint64_t mLastModificationDate;
       nsRefPtr<DeviceStorageFile> mFile;
       nsCString mMimeType;
   };
 
   class PostEnumerationSuccessEvent : public CancelableRunnable
   {
     public:
-      PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent, InfallibleTArray<DeviceStorageFileValue>& aPaths);
+      PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent,
+                                  const nsAString& aStorageType,
+                                  const nsAString& aRelPath,
+                                  InfallibleTArray<DeviceStorageFileValue>& aPaths);
       virtual ~PostEnumerationSuccessEvent();
       virtual nsresult CancelableRun();
     private:
+      const nsString mStorageType;
+      const nsString mRelPath;
       InfallibleTArray<DeviceStorageFileValue> mPaths;
   };
 
   class WriteFileEvent : public CancelableRunnable
   {
     public:
       WriteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, nsIInputStream* aInputStream);
       virtual ~WriteFileEvent();
@@ -206,21 +211,21 @@ private:
       virtual nsresult CancelableRun();
     private:
       int64_t mUsedSpace;
  };
 
  class PostAvailableResultEvent : public CancelableRunnable
  {
     public:
-      PostAvailableResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath);
+      PostAvailableResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
       virtual ~PostAvailableResultEvent();
       virtual nsresult CancelableRun();
     private:
-      nsString mPath;
+      nsRefPtr<DeviceStorageFile> mFile;
  };
 
 protected:
   bool AddRunnable(CancelableRunnable* aRunnable) {
     MutexAutoLock lock(mMutex);
     if (mActorDestoryed)
       return false;
 
--- a/dom/devicestorage/PDeviceStorageRequest.ipdl
+++ b/dom/devicestorage/PDeviceStorageRequest.ipdl
@@ -22,23 +22,23 @@ struct SuccessResponse
 
 struct BlobResponse
 {
   PBlob blob;
 };
 
 struct DeviceStorageFileValue
 {
-  nsString type;
   nsString name;
-  nsString fullpath;
 };
 
 struct EnumerationResponse
 {
+  nsString type;
+  nsString relpath;
   DeviceStorageFileValue[] paths;
 };
 
 struct FreeSpaceStorageResponse
 {
   int64_t freeBytes;
 };
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -62,16 +62,31 @@
 #define DEVICESTORAGE_SDCARD     "sdcard"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 
 #include "nsDirectoryServiceDefs.h"
 
+class GlobalDirs : public RefCounted<GlobalDirs>
+{
+public:
+#if !defined(MOZ_WIDGET_GONK)
+  nsCOMPtr<nsIFile> pictures;
+  nsCOMPtr<nsIFile> videos;
+  nsCOMPtr<nsIFile> music;
+  nsCOMPtr<nsIFile> apps;
+  nsCOMPtr<nsIFile> sdcard;
+#endif
+  nsCOMPtr<nsIFile> temp;
+};
+
+static StaticRefPtr<GlobalDirs> sDirs;
+
 nsAutoPtr<DeviceStorageTypeChecker> DeviceStorageTypeChecker::sDeviceStorageTypeChecker;
 
 DeviceStorageTypeChecker::DeviceStorageTypeChecker()
 {
 }
 
 DeviceStorageTypeChecker::~DeviceStorageTypeChecker()
 {
@@ -306,45 +321,192 @@ public:
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsCString mType;
 };
 
 DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
-                                     nsIFile* aFile,
+                                     const nsAString& aRootDir,
+                                     const nsAString& aPath)
+  : mPath(aPath)
+  , mStorageType(aStorageType)
+  , mRootDir(aRootDir)
+  , mEditable(false)
+{
+  Init(aStorageType);
+  AppendRelativePath(mRootDir);
+  if (!mPath.EqualsLiteral("")) {
+    AppendRelativePath(mPath);
+  }
+  NormalizeFilePath();
+}
+
+DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
                                      const nsAString& aPath)
   : mPath(aPath)
   , mStorageType(aStorageType)
   , mEditable(false)
 {
-  NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
-  // always take a clone
-  nsCOMPtr<nsIFile> file;
-  aFile->Clone(getter_AddRefs(mFile));
-
-  AppendRelativePath();
+  Init(aStorageType);
+  AppendRelativePath(aPath);
   NormalizeFilePath();
+}
+
+DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType)
+  : mStorageType(aStorageType)
+  , mEditable(false)
+{
+  Init(aStorageType);
+}
+
+void
+DeviceStorageFile::Init(const nsAString& aStorageType)
+{
+  // The hard-coded sdcard below will change as part of bug 858416
+  DeviceStorageFile::GetRootDirectoryForType(aStorageType,
+                                             NS_LITERAL_STRING("sdcard"),
+                                             getter_AddRefs(mFile));
 
   DebugOnly<DeviceStorageTypeChecker*> typeChecker = DeviceStorageTypeChecker::CreateOrGet();
   NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
 }
 
-DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile)
-  : mStorageType(aStorageType)
-  , mEditable(false)
+static void
+InitDirs()
+{
+  if (sDirs) {
+    return;
+  }
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  sDirs = new GlobalDirs;
+  ClearOnShutdown(&sDirs);
+
+  nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+  NS_ASSERTION(dirService, "Must have directory service");
+
+#if !defined(MOZ_WIDGET_GONK)
+
+#if defined (MOZ_WIDGET_COCOA)
+  dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures));
+  dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos));
+  dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music));
+#elif defined (XP_UNIX)
+  dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures));
+  dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos));
+  dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music));
+#elif defined (XP_WIN)
+  dirService->Get(NS_WIN_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures));
+  dirService->Get(NS_WIN_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos));
+  dirService->Get(NS_WIN_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music));
+#endif
+
+  dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->apps));
+  if (sDirs->apps) {
+    sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
+  }
+
+  // Eventually, on desktop, we want to do something smarter -- for example,
+  // detect when an sdcard is inserted, and use that instead of this.
+  dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->sdcard));
+  if (sDirs->sdcard) {
+    sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
+  }
+#endif // !MOZ_WIDGET_GONK
+
+  if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
+    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->temp));
+    if (sDirs->temp) {
+      sDirs->temp->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing"));
+      sDirs->temp->Create(nsIFile::DIRECTORY_TYPE, 0777);
+      sDirs->temp->Normalize();
+    }
+  }
+}
+
+void
+DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const
+                                           nsAString &aVolName,
+                                           nsIFile** aFile)
 {
-  NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile");
-  // always take a clone
-  nsCOMPtr<nsIFile> file;
-  aFile->Clone(getter_AddRefs(mFile));
-
-  DebugOnly<DeviceStorageTypeChecker*> typeChecker = DeviceStorageTypeChecker::CreateOrGet();
-  NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
+  nsCOMPtr<nsIFile> f;
+
+  InitDirs();
+
+#ifdef MOZ_WIDGET_GONK
+  nsString volMountPoint(NS_LITERAL_STRING("/sdcard"));
+  if (!aVolName.EqualsLiteral("")) {
+    nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+    if (vs) {
+      nsresult rv;
+      nsCOMPtr<nsIVolume> vol;
+      rv = vs->GetVolumeByName(aVolName, getter_AddRefs(vol));
+      if (NS_SUCCEEDED(rv)) {
+        vol->GetMountPoint(volMountPoint);
+      }
+    }
+  }
+#endif
+
+  // Picture directory
+  if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
+#ifdef MOZ_WIDGET_GONK
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
+    f = sDirs->pictures;
+#endif
+  }
+
+  // Video directory
+  else if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
+#ifdef MOZ_WIDGET_GONK
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
+    f = sDirs->videos;
+#endif
+  }
+
+  // Music directory
+  else if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
+#ifdef MOZ_WIDGET_GONK
+    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
+    f = sDirs->music;
+#endif
+  }
+
+  // Apps directory
+  else if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) {
+#ifdef MOZ_WIDGET_GONK
+    NS_NewLocalFile(NS_LITERAL_STRING("/data"), false, getter_AddRefs(f));
+#else
+    f = sDirs->apps;
+#endif
+  }
+
+   // default SDCard
+   else if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
+#ifdef MOZ_WIDGET_GONK
+     NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
+#else
+     f = sDirs->sdcard;
+#endif
+  }
+
+  // in testing, we default all device storage types to a temp directory
+  if (f && mozilla::Preferences::GetBool("device.storage.testing", false)) {
+    f = sDirs->temp;
+  }
+
+  if (f) {
+    f->Clone(aFile);
+  } else {
+    *aFile = nullptr;
+  }
 }
 
 void
 DeviceStorageFile::SetPath(const nsAString& aPath) {
   mPath.Assign(aPath);
   NormalizeFilePath();
 }
 
@@ -353,30 +515,36 @@ DeviceStorageFile::SetEditable(bool aEdi
   mEditable = aEditable;
 }
 
 // we want to make sure that the names of file can't reach
 // outside of the type of storage the user asked for.
 bool
 DeviceStorageFile::IsSafePath()
 {
+  return IsSafePath(mRootDir) && IsSafePath(mPath);
+}
+
+bool
+DeviceStorageFile::IsSafePath(const nsAString& aPath)
+{
   nsAString::const_iterator start, end;
-  mPath.BeginReading(start);
-  mPath.EndReading(end);
+  aPath.BeginReading(start);
+  aPath.EndReading(end);
 
   // if the path is a '~' or starts with '~/', return false.
   NS_NAMED_LITERAL_STRING(tilde, "~");
   NS_NAMED_LITERAL_STRING(tildeSlash, "~/");
-  if (mPath.Equals(tilde) ||
-      StringBeginsWith(mPath, tildeSlash)) {
+  if (aPath.Equals(tilde) ||
+      StringBeginsWith(aPath, tildeSlash)) {
     NS_WARNING("Path name starts with tilde!");
     return false;
    }
   // split on /.  if any token is "", ., or .., return false.
-  NS_ConvertUTF16toUTF8 cname(mPath);
+  NS_ConvertUTF16toUTF8 cname(aPath);
   char* buffer = cname.BeginWriting();
   const char* token;
 
   while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
     if (PL_strcmp(token, "") == 0 ||
         PL_strcmp(token, ".") == 0 ||
         PL_strcmp(token, "..") == 0 ) {
       return false;
@@ -393,33 +561,33 @@ DeviceStorageFile::NormalizeFilePath() {
   for (; cur < end; ++cur) {
     if (PRUnichar('\\') == *cur)
       *cur = PRUnichar('/');
   }
 #endif
 }
 
 void
-DeviceStorageFile::AppendRelativePath() {
+DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
 #if defined(XP_WIN)
   // replace forward slashes with backslashes,
   // since nsLocalFileWin chokes on them
   nsString temp;
-  temp.Assign(mPath);
+  temp.Assign(aPath);
 
   PRUnichar* cur = temp.BeginWriting();
   PRUnichar* end = temp.EndWriting();
 
   for (; cur < end; ++cur) {
     if (PRUnichar('/') == *cur)
       *cur = PRUnichar('\\');
   }
   mFile->AppendRelativePath(temp);
 #else
-  mFile->AppendRelativePath(mPath);
+  mFile->AppendRelativePath(aPath);
 #endif
 }
 
 nsresult
 DeviceStorageFile::Write(nsIInputStream* aInputStream)
 {
   if (!aInputStream) {
     return NS_ERROR_FAILURE;
@@ -531,17 +699,16 @@ void
 DeviceStorageFile::CollectFiles(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
                                 PRTime aSince)
 {
   nsString rootPath;
   nsresult rv = mFile->GetPath(rootPath);
   if (NS_FAILED(rv)) {
     return;
   }
-
   return collectFilesInternal(aFiles, aSince, rootPath);
 }
 
 void
 DeviceStorageFile::collectFilesInternal(nsTArray<nsRefPtr<DeviceStorageFile> > &aFiles,
                                         PRTime aSince,
                                         nsAString& aRootPath)
 {
@@ -580,22 +747,20 @@ DeviceStorageFile::collectFilesInternal(
       NS_ERROR("collectFiles returned a path that does not belong!");
       continue;
     }
 
     nsAString::size_type len = aRootPath.Length() + 1; // +1 for the trailing /
     nsDependentSubstring newPath = Substring(fullpath, len);
 
     if (isDir) {
-      DeviceStorageFile dsf(mStorageType, f);
-      dsf.SetPath(newPath);
+      DeviceStorageFile dsf(mStorageType, mRootDir, newPath);
       dsf.collectFilesInternal(aFiles, aSince, aRootPath);
     } else if (isFile) {
-      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, f);
-      dsf->SetPath(newPath);
+      nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDir, newPath);
       aFiles.AppendElement(dsf);
     }
   }
 }
 
 void
 DeviceStorageFile::DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar, const nsAString& aStorageType)
 {
@@ -701,115 +866,40 @@ static void
 UnregisterForSDCardChanges(nsIObserver* aObserver)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED);
 }
 #endif
 
 void
-nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType, const nsAString& aVolName)
+nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType,
+                                            const nsAString& aVolName)
 {
   nsCOMPtr<nsIFile> f;
-  nsCOMPtr<nsIProperties> dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
-  NS_ASSERTION(dirService, "Must have directory service");
+  DeviceStorageFile::GetRootDirectoryForType(aType,
+                                             aVolName,
+                                             getter_AddRefs(f));
 
   mVolumeName = NS_LITERAL_STRING("");
 #ifdef MOZ_WIDGET_GONK
   nsString volMountPoint(NS_LITERAL_STRING("/sdcard"));
   if (!aVolName.EqualsLiteral("")) {
     nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
     if (vs) {
       nsresult rv;
       nsCOMPtr<nsIVolume> vol;
       rv = vs->GetVolumeByName(aVolName, getter_AddRefs(vol));
       if (NS_SUCCEEDED(rv)) {
         vol->GetMountPoint(volMountPoint);
         mVolumeName = aVolName;
       }
     }
   }
-#endif
-
-  // Picture directory
-  if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
-#ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
-#elif defined (MOZ_WIDGET_COCOA)
-    dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#elif defined (XP_UNIX)
-    dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#elif defined (XP_WIN)
-    dirService->Get(NS_WIN_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#endif
-  }
-
-  // Video directory
-  else if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
-#ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
-#elif defined (MOZ_WIDGET_COCOA)
-    dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#elif defined (XP_UNIX)
-    dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#elif defined (XP_WIN)
-    dirService->Get(NS_WIN_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#endif
-  }
-
-  // Music directory
-  else if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
-#ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
-#elif defined (MOZ_WIDGET_COCOA)
-    dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#elif defined (XP_UNIX)
-    dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#elif defined (XP_WIN)
-    dirService->Get(NS_WIN_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-#endif
-  }
-
-  // Apps directory
-  else if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) {
-#ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(NS_LITERAL_STRING("/data"), false, getter_AddRefs(f));
-#else
-    dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    if (f) {
-      f->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
-    }
-#endif
-  }
-
-   // default SDCard
-   else if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
-#ifdef MOZ_WIDGET_GONK
-     NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
-#else
-    // Eventually, on desktop, we want to do something smarter -- for example,
-    // detect when an sdcard is inserted, and use that instead of this.
-    dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    if (f) {
-      f->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
-    }
-#endif
-  }
-
-  // in testing, we default all device storage types to a temp directory
-  if (f && mozilla::Preferences::GetBool("device.storage.testing", false)) {
-    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
-    if (f) {
-      f->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing"));
-      f->Create(nsIFile::DIRECTORY_TYPE, 0777);
-      f->Normalize();
-    }
-  }
-
-#ifdef MOZ_WIDGET_GONK
+
   RegisterForSDCardChanges(this);
 #endif
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   obs->AddObserver(this, "file-watcher-update", false);
   mRootDirectory = f;
   mStorageType = aType;
 }
@@ -1021,30 +1111,23 @@ ContinueCursorEvent::Continue()
   nsRefPtr<DeviceStorageFile> file = GetNextFile();
 
   if (!file) {
     // done with enumeration.
     NS_DispatchToMainThread(this);
     return;
   }
 
-  nsString fullpath;
-  nsresult rv = file->mFile->GetPath(fullpath);
-  if (NS_FAILED(rv)) {
-    NS_ASSERTION(false, "GetPath failed to return a valid path");
-    return;
-  }
-
   nsDOMDeviceStorageCursor* cursor = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
   nsString cursorStorageType;
   cursor->GetStorageType(cursorStorageType);
 
   DeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, file);
   child->SetCallback(cursor);
-  DeviceStorageGetParams params(cursorStorageType, file->mPath, fullpath);
+  DeviceStorageGetParams params(cursorStorageType, file->mRootDir, file->mPath);
   ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
   mRequest = nullptr;
 }
 
 NS_IMETHODIMP
 ContinueCursorEvent::Run()
 {
   nsRefPtr<DeviceStorageFile> file = GetNextFile();
@@ -1175,27 +1258,18 @@ nsDOMDeviceStorageCursor::Allow()
 {
   if (!mFile->IsSafePath()) {
     nsCOMPtr<nsIRunnable> r = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
-
-    nsString fullpath;
-    nsresult rv = mFile->mFile->GetPath(fullpath);
-
-    if (NS_FAILED(rv)) {
-      // just do nothing
-      return NS_OK;
-    }
-
     PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(this, mFile);
-    DeviceStorageEnumerationParams params(mFile->mStorageType, fullpath, mSince);
+    DeviceStorageEnumerationParams params(mFile->mStorageType, mFile->mRootDir, mSince);
     ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
     return NS_OK;
   }
 
   nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   NS_ASSERTION(target, "Must have stream transport service");
 
   nsCOMPtr<InitCursorEvent> event = new InitCursorEvent(this, mFile);
@@ -1645,24 +1719,16 @@ public:
   NS_IMETHOD Allow()
   {
     nsCOMPtr<nsIRunnable> r;
 
     if (!mRequest) {
       return NS_ERROR_FAILURE;
     }
 
-    nsString fullpath;
-    nsresult rv = mFile->mFile->GetPath(fullpath);
-
-    if (NS_FAILED(rv)) {
-      // just do nothing
-      return NS_OK;
-    }
-
     switch(mRequestType) {
       case DEVICE_STORAGE_REQUEST_CREATE:
       {
         if (!mBlob) {
           return NS_ERROR_FAILURE;
         }
 
         DeviceStorageTypeChecker* typeChecker = DeviceStorageTypeChecker::CreateOrGet();
@@ -1682,18 +1748,17 @@ public:
           BlobChild* actor = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob);
           if (!actor) {
             return NS_ERROR_FAILURE;
           }
 
           DeviceStorageAddParams params;
           params.blobChild() = actor;
           params.type() = mFile->mStorageType;
-          params.name() = mFile->mPath;
-          params.fullpath() = fullpath;
+          params.relpath() = mFile->mPath;
 
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new WriteFileEvent(mBlob, mFile, mRequest);
         break;
       }
@@ -1709,17 +1774,17 @@ public:
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
           r = new PostErrorEvent(mRequest, POST_ERROR_EVENT_ILLEGAL_TYPE);
           NS_DispatchToMainThread(r);
           return NS_OK;
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageGetParams params(mFile->mStorageType, mFile->mPath, fullpath);
+          DeviceStorageGetParams params(mFile->mStorageType, mFile->mRootDir, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
 
         r = new ReadFileEvent(mFile, mRequest);
         break;
       }
 
@@ -1733,53 +1798,53 @@ public:
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
           r = new PostErrorEvent(mRequest, POST_ERROR_EVENT_ILLEGAL_TYPE);
           NS_DispatchToMainThread(r);
           return NS_OK;
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageDeleteParams params(mFile->mStorageType, fullpath);
+          DeviceStorageDeleteParams params(mFile->mStorageType, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new DeleteFileEvent(mFile, mRequest);
         break;
       }
 
       case DEVICE_STORAGE_REQUEST_FREE_SPACE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageFreeSpaceParams params(mFile->mStorageType, fullpath);
+          DeviceStorageFreeSpaceParams params(mFile->mStorageType, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new FreeSpaceFileEvent(mFile, mRequest);
         break;
       }
 
       case DEVICE_STORAGE_REQUEST_USED_SPACE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageUsedSpaceParams params(mFile->mStorageType, fullpath);
+          DeviceStorageUsedSpaceParams params(mFile->mStorageType, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new UsedSpaceFileEvent(mFile, mRequest);
         break;
       }
 
       case DEVICE_STORAGE_REQUEST_AVAILABLE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile);
-          DeviceStorageAvailableParams params(mFile->mStorageType, fullpath);
+          DeviceStorageAvailableParams params(mFile->mStorageType, mFile->mPath);
           ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new PostAvailableResultEvent(mFile->mPath, mRequest);
         NS_DispatchToMainThread(r);
         return NS_OK;
       }
 
@@ -2019,17 +2084,17 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob 
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*_retval = request);
 
   nsCOMPtr<nsIRunnable> r;
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, aPath);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, aPath);
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
   } else if (!typeChecker->Check(mStorageType, dsf->mFile) ||
       !typeChecker->Check(mStorageType, aBlob)) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
   }
   else {
     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATE,
@@ -2075,17 +2140,17 @@ nsDOMDeviceStorage::GetInternal(const JS
   JSString* jsstr = JS_ValueToString(aCx, aPath);
   nsDependentJSString path;
   if (!path.init(aCx, jsstr)) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, path);
   dsf->SetEditable(aEditable);
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
   } else {
     r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE : DEVICE_STORAGE_REQUEST_READ,
                                  win, mPrincipal, dsf, request);
   }
   NS_DispatchToMainThread(r);
@@ -2108,17 +2173,17 @@ nsDOMDeviceStorage::Delete(const JS::Val
   JSString* jsstr = JS_ValueToString(aCx, aPath);
   nsDependentJSString path;
   if (!path.init(aCx, jsstr)) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
     NS_DispatchToMainThread(r);
     return NS_OK;
   }
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, path);
 
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
   }
   else {
     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE,
                                  win, mPrincipal, dsf, request);
   }
@@ -2132,17 +2197,17 @@ nsDOMDeviceStorage::FreeSpace(nsIDOMDOMR
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*aRetval = request);
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE,
                                                      win,
                                                      mPrincipal,
                                                      dsf,
                                                      request);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
@@ -2153,17 +2218,17 @@ nsDOMDeviceStorage::UsedSpace(nsIDOMDOMR
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*aRetval = request);
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE,
                                                      win,
                                                      mPrincipal,
                                                      dsf,
                                                      request);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
@@ -2174,17 +2239,17 @@ nsDOMDeviceStorage::Available(nsIDOMDOMR
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   NS_ADDREF(*aRetval = request);
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE,
                                                      win,
                                                      mPrincipal,
                                                      dsf,
                                                      request);
   NS_DispatchToMainThread(r);
   return NS_OK;
 }
@@ -2274,17 +2339,17 @@ nsDOMDeviceStorage::EnumerateInternal(co
     }
 
     if (aArgc == 2 && (JSVAL_IS_VOID(aOptions) || aOptions.isNull() || !aOptions.isObject())) {
       return NS_ERROR_FAILURE;
     }
     since = ExtractDateFromOptions(aCx, aOptions);
   }
 
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, path, NS_LITERAL_STRING(""));
   dsf->SetEditable(aEditable);
 
   nsRefPtr<nsDOMDeviceStorageCursor> cursor = new nsDOMDeviceStorageCursor(win, mPrincipal,
                                                                            dsf, since);
   nsRefPtr<DeviceStorageCursorRequest> r = new DeviceStorageCursorRequest(cursor);
 
   NS_ADDREF(*aRetval = cursor);
 
@@ -2448,17 +2513,17 @@ nsDOMDeviceStorage::AddEventListener(con
                                      uint8_t aArgc)
 {
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
                                                      win, mPrincipal, dsf, request, this);
   NS_DispatchToMainThread(r);
   return nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted, aArgc);
 }
 
 void
 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
@@ -2469,17 +2534,17 @@ nsDOMDeviceStorage::AddEventListener(con
 {
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
-  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType, mRootDirectory);
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType);
   nsCOMPtr<nsIRunnable> r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
                                                      win, mPrincipal, dsf, request, this);
   NS_DispatchToMainThread(r);
   nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, aWantsUntrusted, aRv);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1171,20 +1171,17 @@ ContentChild::RecvLastPrivateDocShellDes
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
     return true;
 }
 
 bool
 ContentChild::RecvFilePathUpdate(const nsString& type, const nsString& path, const nsCString& aReason)
 {
-    nsCOMPtr<nsIFile> file;
-    NS_NewLocalFile(path, false, getter_AddRefs(file));
-
-    nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(type, file);
+    nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(type, path);
 
     nsString reason;
     CopyASCIItoUTF16(aReason, reason);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     obs->NotifyObservers(dsf, "file-watcher-update", reason.get());
     return true;
 }
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1526,20 +1526,17 @@ ContentParent::Observe(nsISupports* aSub
     else if (!strcmp(aTopic, "last-pb-context-exited")) {
         unused << SendLastPrivateDocShellDestroyed();
     }
     else if (!strcmp(aTopic, "file-watcher-update")) {
         nsCString creason;
         CopyUTF16toUTF8(aData, creason);
         DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
 
-        nsString path;
-        file->mFile->GetPath(path);
-
-        unused << SendFilePathUpdate(file->mStorageType, path, creason);
+        unused << SendFilePathUpdate(file->mStorageType, file->mPath, creason);
     }
 #ifdef MOZ_WIDGET_GONK
     else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
         nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
         if (!vol) {
             return NS_ERROR_NOT_AVAILABLE;
         }
 
@@ -2344,25 +2341,21 @@ ContentParent::RecvAsyncMessage(const ns
     StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
     ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
                         aMsg, false, &cloneData, nullptr, nullptr);
   }
   return true;
 }
 
 bool
-ContentParent::RecvFilePathUpdateNotify(const nsString& aType, const nsString& aFilePath, const nsCString& aReason)
+ContentParent::RecvFilePathUpdateNotify(const nsString& aType,
+                                        const nsString& aFilePath,
+                                        const nsCString& aReason)
 {
-    nsCOMPtr<nsIFile> file;
-    nsresult rv = NS_NewLocalFile(aFilePath, false, getter_AddRefs(file));
-    if (NS_FAILED(rv)) {
-        // ignore
-        return true;
-    }
-    nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(aType, file);
+    nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(aType, aFilePath);
 
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (!obs) {
         return false;
     }
     obs->NotifyObservers(dsf, "file-watcher-update", NS_ConvertASCIItoUTF16(aReason).get());
     return true;
 }
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -58,56 +58,55 @@ struct FontListEntry {
     int16_t   stretch;
     uint8_t   italic;
     uint8_t   index;
 };
 
 struct DeviceStorageFreeSpaceParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
 };
 
 struct DeviceStorageUsedSpaceParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
 };
 
 struct DeviceStorageAvailableParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
 };
 
 struct DeviceStorageAddParams
 {
   nsString type;
+  nsString relpath;
   PBlob blob;
-  nsString name;
-  nsString fullpath;
 };
 
 struct DeviceStorageGetParams
 {
   nsString type;
-  nsString name;
-  nsString fullpath;
+  nsString rootDir;
+  nsString relpath;
 };
 
 struct DeviceStorageDeleteParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
 };
 
 struct DeviceStorageEnumerationParams
 {
   nsString type;
-  nsString fullpath;
+  nsString relpath;
   uint64_t since;
 };
 
 union DeviceStorageParams
 {
   DeviceStorageAddParams;
   DeviceStorageGetParams;
   DeviceStorageDeleteParams;
--- a/dom/network/src/TCPSocket.js
+++ b/dom/network/src/TCPSocket.js
@@ -370,17 +370,17 @@ TCPSocket.prototype = {
     }
 
     LOG("SSL: " + that.ssl + "\n");
 
     if (this._inChild) {
       that._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"]
                              .createInstance(Ci.nsITCPSocketChild);
       that._socketBridge.open(that, host, port, !!that._ssl,
-                              that._binaryType, this.useWin, this);
+                              that._binaryType, this.useWin, this.useWin || this);
       return that;
     }
 
     let transport = that._transport = this._createTransport(host, port, that._ssl);
     transport.setEventSink(that, Services.tm.currentThread);
     transport.securityCallbacks = new SecurityCallbacks(that);
 
     that._socketInputStream = transport.openInputStream(0, 0, 0);
--- a/dom/network/src/TCPSocketChild.cpp
+++ b/dom/network/src/TCPSocketChild.cpp
@@ -6,16 +6,17 @@
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "nsIDOMTCPSocket.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "jswrapper.h"
 
 using mozilla::net::gNeckoChild;
 
 namespace IPC {
 
 bool
 DeserializeArrayBuffer(JSRawObject aObj,
                        const InfallibleTArray<uint8_t>& aBuffer,
@@ -77,17 +78,20 @@ TCPSocketChild::TCPSocketChild()
 NS_IMETHODIMP
 TCPSocketChild::Open(nsITCPSocketInternal* aSocket, const nsAString& aHost,
                      uint16_t aPort, bool aUseSSL, const nsAString& aBinaryType,
                      nsIDOMWindow* aWindow, const JS::Value& aSocketObj,
                      JSContext* aCx)
 {
   mSocket = aSocket;
   MOZ_ASSERT(aSocketObj.isObject());
-  mSocketObj = &aSocketObj.toObject();
+  mSocketObj = js::CheckedUnwrap(&aSocketObj.toObject());
+  if (!mSocketObj) {
+    return NS_ERROR_FAILURE;
+  }
   AddIPDLReference();
   gNeckoChild->SendPTCPSocketConstructor(this, nsString(aHost), aPort,
                                          aUseSSL, nsString(aBinaryType),
                                          GetTabChildFrom(aWindow));
   return NS_OK;
 }
 
 void
@@ -128,17 +132,18 @@ TCPSocketChild::RecvCallback(const nsStr
     rv = mSocket->CallListenerError(aType, err.message(), err.filename(),
                                    err.lineNumber(), err.columnNumber());
 
   } else if (aData.type() == CallbackData::TSendableData) {
     const SendableData& data = aData.get_SendableData();
 
     if (data.type() == SendableData::TArrayOfuint8_t) {
       JS::Value val;
-      IPC::DeserializeArrayBuffer(mSocketObj, data.get_ArrayOfuint8_t(), &val);
+      bool ok = IPC::DeserializeArrayBuffer(mSocketObj, data.get_ArrayOfuint8_t(), &val);
+      NS_ENSURE_TRUE(ok, true);
       rv = mSocket->CallListenerArrayBuffer(aType, val);
 
     } else if (data.type() == SendableData::TnsString) {
       rv = mSocket->CallListenerData(aType, data.get_nsString());
 
     } else {
       MOZ_NOT_REACHED("Invalid callback data type!");
     }
--- a/dom/system/gonk/nsIVolumeService.idl
+++ b/dom/system/gonk/nsIVolumeService.idl
@@ -7,26 +7,27 @@
 #include "nsIVolumeMountLock.idl"
 
 %{C++
 #include "nsTArray.h"
 #include "nsString.h"
 %}
 [ref] native nsStringTArrayRef(nsTArray<nsString>);
 
-[scriptable, uuid(fc4e6449-922e-463d-880b-bfbab4909114)]
+[scriptable, uuid(7c179fb7-67a0-43a3-9337-294e0360b858)]
 interface nsIVolumeService : nsISupports
 {
     nsIVolume getVolumeByName(in DOMString volName);
     nsIVolume getVolumeByPath(in DOMString path);
+    nsIVolume createOrGetVolumeByPath(in DOMString path);
 
     void BroadcastVolume(in DOMString volName);
 
     nsIVolumeMountLock createMountLock(in DOMString volName);
 
     [noscript] void getVolumeNames(in nsStringTArrayRef aVolNames);
 };
 
 %{C++
 #define NS_VOLUMESERVICE_CID \
-  {0x597403c6, 0x5ba4, 0x4e7b, {0xb3, 0xf4, 0xed, 0x3f, 0x05, 0xf7, 0x75, 0xd8}}
+  {0x7c179fb7, 0x67a0, 0x43a3, {0x93, 0x37, 0x29, 0x4e, 0x03, 0x60, 0xb8, 0x58}}
 #define NS_VOLUMESERVICE_CONTRACTID "@mozilla.org/telephony/volume-service;1"
 %}
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -49,18 +49,18 @@ StaticRefPtr<nsVolumeService> nsVolumeSe
 already_AddRefed<nsVolumeService>
 nsVolumeService::GetSingleton()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!sSingleton) {
     sSingleton = new nsVolumeService();
   }
-  NS_ADDREF(sSingleton.get());
-  return sSingleton.get();
+  nsRefPtr<nsVolumeService> volumeService = sSingleton.get();
+  return volumeService.forget();
 }
 
 // static
 void
 nsVolumeService::Shutdown()
 {
   if (!sSingleton) {
     return;
@@ -81,16 +81,17 @@ nsVolumeService::Shutdown()
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(ShutdownVolumeServiceIOThread));
 
   sSingleton = nullptr;
 }
 
 nsVolumeService::nsVolumeService()
+  : mArrayMonitor("nsVolumeServiceArray")
 {
   sSingleton = this;
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     // For child processes, we keep a cache of the volume state.
     nsCOMPtr<nsIObserverService> obs = GetObserverService();
     obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false /*weak*/);
 
@@ -113,45 +114,59 @@ nsVolumeService::nsVolumeService()
   pmService->AddWakeLockListener(this);
 }
 
 nsVolumeService::~nsVolumeService()
 {
 }
 
 // Callback for nsIDOMMozWakeLockListener
-NS_IMETHODIMP nsVolumeService::Callback(const nsAString& aTopic, const nsAString& aState)
+NS_IMETHODIMP
+nsVolumeService::Callback(const nsAString& aTopic, const nsAString& aState)
 {
   CheckMountLock(aTopic, aState);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolumeService::BroadcastVolume(const nsAString& aVolName)
+NS_IMETHODIMP
+nsVolumeService::BroadcastVolume(const nsAString& aVolName)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 
   if (aVolName.EqualsLiteral("")) {
+    nsVolume::Array volumeArray;
+    {
+      // Copy the array since we don't want to call BroadcastVolume
+      // while we're holding the lock.
+      MonitorAutoLock autoLock(mArrayMonitor);
+      volumeArray = mVolumeArray;
+    }
+
     // We treat being passed the empty string as "broadcast all volumes"
-    nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
+    nsVolume::Array::size_type numVolumes = volumeArray.Length();
     nsVolume::Array::index_type volIndex;
     for (volIndex = 0; volIndex < numVolumes; volIndex++) {
-      const nsString &volName(mVolumeArray[volIndex]->Name());
+      const nsString& volName(volumeArray[volIndex]->Name());
       if (!volName.EqualsLiteral("")) {
         // Note: The volume service is the only entity that should be able to
         // modify the array of volumes. So we shouldn't have any issues with
         // the array being modified under our feet (Since we're the volume
         // service the array can't change until after we finish iterating the
         // the loop).
         nsresult rv = BroadcastVolume(volName);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
     return NS_OK;
   }
-  nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
+  nsRefPtr<nsVolume> vol;
+  {
+    MonitorAutoLock autoLock(mArrayMonitor);
+    vol = FindVolumeByName(aVolName);
+  }
   if (!vol) {
     ERR("BroadcastVolume: Unable to locate volume '%s'",
         NS_LossyConvertUTF16toASCII(aVolName).get());
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsCOMPtr<nsIObserverService> obs = GetObserverService();
   NS_ENSURE_TRUE(obs, NS_NOINTERFACE);
@@ -159,26 +174,29 @@ NS_IMETHODIMP nsVolumeService::Broadcast
   DBG("nsVolumeService::BroadcastVolume for '%s'", vol->NameStr().get());
   NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
   obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
   return NS_OK;
 }
 
 NS_IMETHODIMP nsVolumeService::GetVolumeByName(const nsAString& aVolName, nsIVolume **aResult)
 {
+  MonitorAutoLock autoLock(mArrayMonitor);
+
   nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
   if (!vol) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  NS_ADDREF(*aResult = vol);
+  vol.forget(aResult);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult)
+NS_IMETHODIMP
+nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult)
 {
   nsCString utf8Path = NS_ConvertUTF16toUTF8(aPath);
   char realPathBuf[PATH_MAX];
 
   if (!realpath(utf8Path.get(), realPathBuf)) {
     ERR("GetVolumeByPath: realpath on '%s' failed: %d", utf8Path.get(), errno);
     return NSRESULT_FOR_ERRNO();
   }
@@ -188,131 +206,163 @@ NS_IMETHODIMP nsVolumeService::GetVolume
   // what realpath does), we basically check if aPath starts with the mount
   // point, but we don't want to have /mnt/sdcard match /mnt/sdcardfoo but we
   // do want it to match /mnt/sdcard/foo
   // So we add a trailing slash to the mount point and the pathname passed in
   // prior to doing the comparison.
 
   strlcat(realPathBuf, "/", sizeof(realPathBuf));
 
+  MonitorAutoLock autoLock(mArrayMonitor);
+
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
     nsAutoCString volMountPointSlash = NS_ConvertUTF16toUTF8(vol->MountPoint());
     volMountPointSlash.Append(NS_LITERAL_CSTRING("/"));
     nsDependentCSubstring testStr(realPathBuf, volMountPointSlash.Length());
     if (volMountPointSlash.Equals(testStr)) {
-      NS_ADDREF(*aResult = vol);
+      vol.forget(aResult);
       return NS_OK;
     }
   }
+  return NS_ERROR_FILE_NOT_FOUND;
+}
 
-  // In order to support queries by DeviceStorage and the updater, we will fabricate
-  // a volume from the pathname, so that the caller can determine the volume size
-  nsRefPtr<nsVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
-                                        aPath, nsIVolume::STATE_MOUNTED,
-                                        -1 /*generation*/);
-  NS_ADDREF(*aResult = vol);
+NS_IMETHODIMP
+nsVolumeService::CreateOrGetVolumeByPath(const nsAString& aPath, nsIVolume** aResult)
+{
+  nsresult rv = GetVolumeByPath(aPath, aResult);
+  if (rv == NS_OK) {
+    return NS_OK;
+  }
+
+  // In order to support queries by the updater, we will fabricate a volume
+  // from the pathname, so that the caller can determine the volume size.
+  nsCOMPtr<nsIVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
+                                         aPath, nsIVolume::STATE_MOUNTED,
+                                         -1 /*generation*/);
+  vol.forget(aResult);
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolumeService::GetVolumeNames(nsTArray<nsString>& aVolNames)
+NS_IMETHODIMP
+nsVolumeService::GetVolumeNames(nsTArray<nsString>& aVolNames)
 {
+  MonitorAutoLock autoLock(mArrayMonitor);
+
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
     aVolNames.AppendElement(vol->Name());
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
+NS_IMETHODIMP
+nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
 {
-  nsRefPtr<nsVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
+  nsCOMPtr<nsIVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
   if (!mountLock) {
     return NS_ERROR_NOT_AVAILABLE;
   }
-  NS_ADDREF(*aResult = mountLock);
+  mountLock.forget(aResult);
   return NS_OK;
 }
 
-void nsVolumeService::CheckMountLock(const nsAString& aMountLockName,
-                                     const nsAString& aMountLockState)
+void
+nsVolumeService::CheckMountLock(const nsAString& aMountLockName,
+                                const nsAString& aMountLockState)
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   MOZ_ASSERT(NS_IsMainThread());
 
+  nsRefPtr<nsVolume> vol = FindVolumeByMountLockName(aMountLockName);
+  if (vol) {
+    vol->UpdateMountLock(aMountLockState);
+  }
+}
+
+already_AddRefed<nsVolume>
+nsVolumeService::FindVolumeByMountLockName(const nsAString& aMountLockName)
+{
+  MonitorAutoLock autoLock(mArrayMonitor);
+
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
     nsString mountLockName;
     vol->GetMountLockName(mountLockName);
     if (mountLockName.Equals(aMountLockName)) {
-      vol->UpdateMountLock(aMountLockState);
-      return;
+      return vol.forget();
     }
   }
+  return nullptr;
 }
 
-already_AddRefed<nsVolume> nsVolumeService::FindVolumeByName(const nsAString& aName)
+already_AddRefed<nsVolume>
+nsVolumeService::FindVolumeByName(const nsAString& aName)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  mArrayMonitor.AssertCurrentThreadOwns();
 
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
     if (vol->Name().Equals(aName)) {
       return vol.forget();
     }
   }
-  return NULL;
+  return nullptr;
 }
 
 //static
-already_AddRefed<nsVolume> nsVolumeService::FindAddVolumeByName(const nsAString& aName)
+already_AddRefed<nsVolume>
+nsVolumeService::CreateOrFindVolumeByName(const nsAString& aName)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  MonitorAutoLock autoLock(mArrayMonitor);
 
   nsRefPtr<nsVolume> vol;
   vol = FindVolumeByName(aName);
   if (vol) {
     return vol.forget();
   }
   // Volume not found - add a new one
   vol = new nsVolume(aName);
   mVolumeArray.AppendElement(vol);
   return vol.forget();
 }
 
-NS_IMETHODIMP nsVolumeService::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
+NS_IMETHODIMP
+nsVolumeService::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData)
 {
   if (strcmp(aTopic, NS_VOLUME_STATE_CHANGED) != 0) {
     return NS_OK;
   }
   MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
   nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
   if (!vol) {
     return NS_OK;
   }
   UpdateVolume(vol);
   return NS_OK;
 }
 
-void nsVolumeService::UpdateVolume(nsIVolume* aVolume)
+void
+nsVolumeService::UpdateVolume(nsIVolume* aVolume)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString volName;
   aVolume->GetName(volName);
-  nsRefPtr<nsVolume> vol = FindAddVolumeByName(volName);
+  nsRefPtr<nsVolume> vol = CreateOrFindVolumeByName(volName);
   if (vol->Equals(aVolume)) {
     // Nothing has really changed. Don't bother telling anybody.
     return;
   }
   vol->Set(aVolume);
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     // Only the parent broadcasts the state changes
     return;
@@ -342,27 +392,28 @@ public:
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
     DBG("UpdateVolumeRunnable::Run '%s' state %s gen %d locked %d",
         mVolume->NameStr().get(), mVolume->StateStr(),
         mVolume->MountGeneration(), (int)mVolume->IsMountLocked());
 
     mVolumeService->UpdateVolume(mVolume);
-    mVolumeService = NULL;
-    mVolume = NULL;
+    mVolumeService = nullptr;
+    mVolume = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsVolumeService> mVolumeService;
   nsRefPtr<nsVolume>        mVolume;
 };
 
-void nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
+void
+nsVolumeService::UpdateVolumeIOThread(const Volume* aVolume)
 {
   DBG("UpdateVolumeIOThread: Volume '%s' state %s mount '%s' gen %d locked %d",
       aVolume->NameStr(), aVolume->StateStr(), aVolume->MountPoint().get(),
       aVolume->MountGeneration(), (int)aVolume->IsMountLocked());
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   NS_DispatchToMainThread(new UpdateVolumeRunnable(this, aVolume));
 }
 
--- a/dom/system/gonk/nsVolumeService.h
+++ b/dom/system/gonk/nsVolumeService.h
@@ -1,15 +1,16 @@
 /* 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 mozilla_system_nsvolumeservice_h__
 #define mozilla_system_nsvolumeservice_h__
 
+#include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIObserver.h"
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #include "nsVolume.h"
@@ -37,26 +38,29 @@ public:
   NS_DECL_NSIDOMMOZWAKELOCKLISTENER
 
   nsVolumeService();
 
   static already_AddRefed<nsVolumeService> GetSingleton();
   //static nsVolumeService* GetSingleton();
   static void Shutdown();
 
-  void CheckMountLock(const nsAString& aMountLockName,
-                      const nsAString& aMountLockState);
-  already_AddRefed<nsVolume> FindVolumeByName(const nsAString& aName);
-  already_AddRefed<nsVolume> FindAddVolumeByName(const nsAString& aName);
   void UpdateVolume(nsIVolume* aVolume);
   void UpdateVolumeIOThread(const Volume* aVolume);
 
 private:
   ~nsVolumeService();
 
+  void CheckMountLock(const nsAString& aMountLockName,
+                      const nsAString& aMountLockState);
+  already_AddRefed<nsVolume> FindVolumeByMountLockName(const nsAString& aMountLockName);
+  already_AddRefed<nsVolume> FindVolumeByName(const nsAString& aName);
+  already_AddRefed<nsVolume> CreateOrFindVolumeByName(const nsAString& aName);
+
+  Monitor mArrayMonitor;
   nsVolume::Array mVolumeArray;
 
   static StaticRefPtr<nsVolumeService> sSingleton;
 };
 
 } // system
 } // mozilla
 
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -592,35 +592,41 @@ ThreadActor.prototype = {
    * take that into account.
    *
    * @param object aLocation
    *        The location of the breakpoint as specified in the protocol.
    */
   _setBreakpoint: function TA__setBreakpoint(aLocation) {
     let breakpoints = this._breakpointStore[aLocation.url];
 
+    // Get or create the breakpoint actor for the given location
     let actor;
     if (breakpoints[aLocation.line].actor) {
       actor = breakpoints[aLocation.line].actor;
     } else {
       actor = breakpoints[aLocation.line].actor = new BreakpointActor(this, {
         url: aLocation.url,
         line: aLocation.line
       });
       this._hooks.addToParentPool(actor);
     }
 
+    // Find all scripts matching the given location
     let scripts = this.dbg.findScripts(aLocation);
     if (scripts.length == 0) {
       return {
         error: "noScript",
         actor: actor.actorID
       };
     }
 
+   /**
+     * For each script, if the given line has at least one entry point, set
+     * breakpoint on the bytecode offet for each of them.
+     */
     let found = false;
     for (let script of scripts) {
       let offsets = script.getLineOffsets(aLocation.line);
       if (offsets.length > 0) {
         for (let offset of offsets) {
           script.setBreakpoint(offset, actor);
         }
         actor.addScript(script, this);
@@ -628,65 +634,93 @@ ThreadActor.prototype = {
       }
     }
     if (found) {
       return {
         actor: actor.actorID
       };
     }
 
+   /**
+     * If we get here, no breakpoint was set. This is because the given line
+     * has no entry points, for example because it is empty. As a fallback
+     * strategy, we try to set the breakpoint on the smallest line greater
+     * than or equal to the given line that as at least one entry point.
+     */
+
+    // Find all innermost scripts matching the given location
     let scripts = this.dbg.findScripts({
       url: aLocation.url,
       line: aLocation.line,
       innermost: true
     });
 
+    /**
+     * For each innermost script, look for the smallest line greater than or
+     * equal to the given line that has one or more entry points. If found, set
+     * a breakpoint on the bytecode offset for each of its entry points.
+     */
     let actualLocation;
     let found = false;
     for (let script of scripts) {
       let offsets = script.getAllOffsets();
       for (let line = aLocation.line; line < offsets.length; ++line) {
         if (offsets[line]) {
           for (let offset of offsets[line]) {
             script.setBreakpoint(offset, actor);
           }
           actor.addScript(script, this);
           if (!actualLocation) {
             actualLocation = {
               url: aLocation.url,
               line: line,
-              column: aLocation.column
+              column: 0
             };
           }
           found = true;
           break;
         }
       }
     }
     if (found) {
       if (breakpoints[actualLocation.line] &&
           breakpoints[actualLocation.line].actor) {
+        /**
+         * We already have a breakpoint actor for the actual location, so
+         * actor we created earlier is now redundant. Delete it, update the
+         * breakpoint store, and return the actor for the actual location.
+         */
         actor.onDelete();
         delete breakpoints[aLocation.line];
         return {
           actor: breakpoints[actualLocation.line].actor.actorID,
           actualLocation: actualLocation
         };
       } else {
+        /**
+         * We don't have a breakpoint actor for the actual location yet.
+         * Instead or creating a new actor, reuse the actor we created earlier,
+         * and update the breakpoint store.
+         */
         actor.location = actualLocation;
         breakpoints[actualLocation.line] = breakpoints[aLocation.line];
+        delete breakpoints[aLocation.line];
+        // WARNING: This overwrites aLocation.line
         breakpoints[actualLocation.line].line = actualLocation.line;
-        delete breakpoints[aLocation.line];
         return {
           actor: actor.actorID,
           actualLocation: actualLocation
         };
       }
     }
 
+    /**
+     * If we get here, no line matching the given line was found, so just
+     * epically.
+     */
     return {
       error: "noCodeAtLineColumn",
       actor: actor.actorID
     };
   },
 
   /**
    * Get the script and source lists from the debugger.
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -97,17 +97,17 @@ function initialize(event) {
     return;
   }
 
   // Default to the last selected category
   var view = gCategories.node.value;
 
   // Allow passing in a view through the window arguments
   if ("arguments" in window && window.arguments.length > 0 &&
-      "view" in window.arguments[0]) {
+      window.arguments[0] !== null && "view" in window.arguments[0]) {
     view = window.arguments[0].view;
   }
 
   gViewController.loadInitialView(view);
 }
 
 function notifyInitialized() {
   if (!gIsInitializing)