Merge m-c to inbound
authorWes Kocher <wkocher@mozilla.com>
Tue, 21 Jan 2014 17:34:46 -0800
changeset 164561 9ab3440df7b8c2639ddbcbc7af0e071553ee2093
parent 164542 03bdec48d0ac18016bf0e59b0357d8d45c0db903 (current diff)
parent 164560 8f4ecbf938cd15199f017b5b21ca3c3e167802a3 (diff)
child 164562 554475555e21c65fb329e7b2ec200d962aec7d0c
push id38744
push userkwierso@gmail.com
push dateWed, 22 Jan 2014 01:36:02 +0000
treeherdermozilla-inbound@554475555e21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.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
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -183,19 +183,16 @@ pref("privacy.item.history", true);
 pref("privacy.item.formdata", true);
 pref("privacy.item.downloads", true);
 pref("privacy.item.passwords", true);
 pref("privacy.item.sessions", true);
 pref("privacy.item.geolocation", true);
 pref("privacy.item.siteSettings", true);
 pref("privacy.item.syncAccount", true);
 
-// URL to the Learn More link XXX this is the firefox one.  Bug 495578 fixes this.
-pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/geolocation/");
-
 // base url for the wifi geolocation network provider
 pref("geo.wifi.uri", "https://maps.googleapis.com/maps/api/browserlocation/json");
 
 // enable geo
 pref("geo.enabled", true);
 
 // content sink control -- controls responsiveness during page load
 // see https://bugzilla.mozilla.org/show_bug.cgi?id=481566#c9
@@ -337,17 +334,16 @@ pref("browser.safebrowsing.provider.0.ge
 // HTML report pages
 pref("browser.safebrowsing.provider.0.reportGenericURL", "http://{moz:locale}.phish-generic.mozilla.com/?hl={moz:locale}");
 pref("browser.safebrowsing.provider.0.reportErrorURL", "http://{moz:locale}.phish-error.mozilla.com/?hl={moz:locale}");
 pref("browser.safebrowsing.provider.0.reportPhishURL", "http://{moz:locale}.phish-report.mozilla.com/?hl={moz:locale}");
 pref("browser.safebrowsing.provider.0.reportMalwareURL", "http://{moz:locale}.malware-report.mozilla.com/?hl={moz:locale}");
 pref("browser.safebrowsing.provider.0.reportMalwareErrorURL", "http://{moz:locale}.malware-error.mozilla.com/?hl={moz:locale}");
 
 // FAQ URLs
-pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/%APP%/geolocation/");
 
 // Name of the about: page contributed by safebrowsing to handle display of error
 // pages on phishing/malware hits.  (bug 399233)
 pref("urlclassifier.alternate_error_page", "blocked");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
 
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e2f73049f8d52fb06cb9b5d923c1280557aa9238"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="fce1a137746dbd354bca1918f02f96d51c40bad2">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e2f73049f8d52fb06cb9b5d923c1280557aa9238"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e2f73049f8d52fb06cb9b5d923c1280557aa9238"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "a43f6c119d80637dd3e36975e8f38b7f19b5c95c", 
+    "revision": "98abf6bda1c12b45389ec6bc2acd3e3e901e3163", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e2f73049f8d52fb06cb9b5d923c1280557aa9238"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -5,17 +5,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -7,17 +7,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e2f73049f8d52fb06cb9b5d923c1280557aa9238"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e2f73049f8d52fb06cb9b5d923c1280557aa9238"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="fce1a137746dbd354bca1918f02f96d51c40bad2">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e2f73049f8d52fb06cb9b5d923c1280557aa9238"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -6,17 +6,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e0b7c709ddc21c407ee3360d3203e9eb84535b66"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8d18b1e2fd06c84a879f99f6e8ca1f104eeacb13"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96d2d00165f4561fbde62d1062706eab74b3a01f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e2f73049f8d52fb06cb9b5d923c1280557aa9238"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -994,17 +994,17 @@ let BookmarkingUI = {
     if (event.target != event.currentTarget)
       return;
 
     let widget = CustomizableUI.getWidget("bookmarks-menu-button")
                                .forWindow(window);
     if (widget.overflowed) {
       // Don't open a popup in the overflow popup, rather just open the Library.
       event.preventDefault();
-      widget.node.removeAttribute("noautoclose");
+      widget.node.removeAttribute("closemenu");
       PlacesCommandHook.showPlacesOrganizer("BookmarksMenu");
       return;
     }
 
     if (!this._popupNeedsUpdate)
       return;
     this._popupNeedsUpdate = false;
 
@@ -1167,28 +1167,28 @@ let BookmarkingUI = {
 
     // Handle special case when the button is in the panel.
     let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
     let widget = widgetGroup.forWindow(window);
     if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
       let view = document.getElementById("PanelUI-bookmarks");
       view.addEventListener("ViewShowing", this.onPanelMenuViewShowing);
       view.addEventListener("ViewHiding", this.onPanelMenuViewHiding);
-      widget.node.setAttribute("noautoclose", "true");
+      widget.node.setAttribute("closemenu", "none");
       PanelUI.showSubView("PanelUI-bookmarks", widget.node,
                           CustomizableUI.AREA_PANEL);
       return;
     }
     else if (widget.overflowed) {
       // Allow to close the panel if the page is already bookmarked, cause
       // we are going to open the edit bookmark panel.
       if (this._itemIds.length > 0)
-        widget.node.removeAttribute("noautoclose");
+        widget.node.removeAttribute("closemenu");
       else
-        widget.node.setAttribute("noautoclose", "true");
+        widget.node.setAttribute("closemenu", "none");
     }
 
     // Ignore clicks on the star if we are updating its state.
     if (!this._pendingStmt) {
       PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
     }
   },
 
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -1166,17 +1166,17 @@ SocialStatus = {
     // if we're a slice in the hamburger, use that panel instead
     let widgetGroup = CustomizableUI.getWidget(aToolbarButton.getAttribute("id"));
     let widget = widgetGroup.forWindow(window);
     let panel, showingEvent, hidingEvent;
     let inMenuPanel = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
     if (inMenuPanel) {
       panel = document.getElementById("PanelUI-socialapi");
       this._attachNotificatonPanel(panel, aToolbarButton, provider);
-      widget.node.setAttribute("noautoclose", "true");
+      widget.node.setAttribute("closemenu", "none");
       showingEvent = "ViewShowing";
       hidingEvent = "ViewHiding";
     } else {
       panel = document.getElementById("social-notification-panel");
       this._attachNotificatonPanel(panel, aToolbarButton, provider);
       showingEvent = "popupshown";
       hidingEvent = "popuphidden";
     }
--- a/browser/base/content/socialmarks.xml
+++ b/browser/base/content/socialmarks.xml
@@ -18,17 +18,17 @@
       <field name="inMenuPanel">false</field>
 
       <property name="panel">
         <getter>
           let widgetGroup = CustomizableUI.getWidget(this.getAttribute("id"));
           let widget = widgetGroup.forWindow(window);
           this.inMenuPanel = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
           if (this.inMenuPanel) {
-            widget.node.setAttribute("noautoclose", "true");
+            widget.node.setAttribute("closemenu", "none");
             return document.getElementById("PanelUI-socialapi");
           }
           return document.getAnonymousElementByAttribute(this, "anonid", "panel");
         </getter>
       </property>
 
       <property name="content">
         <getter><![CDATA[
--- a/browser/components/customizableui/content/customizeMode.inc.xul
+++ b/browser/components/customizableui/content/customizeMode.inc.xul
@@ -2,18 +2,27 @@
    - 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/. -->
 
 <hbox id="customization-container" flex="1" hidden="true">
   <vbox flex="1" id="customization-palette-container">
     <label id="customization-header">
       &customizeMode.menuAndToolbars.header;
     </label>
+    <hbox id="customization-empty" hidden="true">
+      <label>&customizeMode.menuAndToolbars.empty;</label>
+      <label onclick="BrowserOpenAddonsMgr('addons://discovery/');"
+             onkeypress="BrowserOpenAddonsMgr('addons://discovery/');"
+             id="customization-more-tools"
+             class="text-link">
+        &customizeMode.menuAndToolbars.emptyLink;
+      </label>
+    </hbox>
     <vbox id="customization-palette" flex="1"/>
-    <spacer flex="1"/>
+    <spacer id="customization-spacer" flex="1"/>
     <hbox>
       <button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars;" class="customizationmode-button" type="menu">
         <menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>
       </button>
       <spacer flex="1"/>
       <button id="customization-reset-button" oncommand="gCustomizeMode.reset();" label="&customizeMode.restoreDefaults;" class="customizationmode-button"/>
     </hbox>
   </vbox>
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -325,19 +325,25 @@ const PanelUI = {
     }
   },
 
   /**
    * This function can be used as a command event listener for subviews
    * so that the panel knows if and when to close itself.
    */
   onCommandHandler: function(aEvent) {
-    if (!aEvent.originalTarget.hasAttribute("noautoclose")) {
-      PanelUI.hide();
+    let closemenu = aEvent.originalTarget.getAttribute("closemenu");
+    if (closemenu == "none") {
+      return;
     }
+    if (closemenu == "single") {
+      this.showMainView();
+      return;
+    }
+    this.hide();
   },
 
   /**
    * Open a dialog window that allow the user to customize listed character sets.
    */
   onCharsetCustomizeCommand: function() {
     this.hide();
     window.openDialog("chrome://global/content/customizeCharset.xul",
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -1225,30 +1225,31 @@ let CustomizableUIInternal = {
   maybeAutoHidePanel: function(aEvent) {
     if (aEvent.type == "keypress") {
       if (aEvent.keyCode != aEvent.DOM_VK_ENTER &&
           aEvent.keyCode != aEvent.DOM_VK_RETURN) {
         return;
       }
       // If the user hit enter/return, we don't check preventDefault - it makes sense
       // that this was prevented, but we probably still want to close the panel.
-      // If consumers don't want this to happen, they should specify noautoclose.
+      // If consumers don't want this to happen, they should specify the closemenu
+      // attribute.
 
     } else if (aEvent.type != "command") { // mouse events:
       if (aEvent.defaultPrevented || aEvent.button != 0) {
         return;
       }
       let isInteractive = this._isOnInteractiveElement(aEvent);
       LOG("maybeAutoHidePanel: interactive ? " + isInteractive);
       if (isInteractive) {
         return;
       }
     }
 
-    if (aEvent.target.getAttribute("noautoclose") == "true" ||
+    if (aEvent.target.getAttribute("closemenu") == "none" ||
         aEvent.target.getAttribute("widget-type") == "view") {
       return;
     }
 
     // If we get here, we can actually hide the popup:
     this.hidePanelForNode(aEvent.target);
   },
 
--- a/browser/components/customizableui/src/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/src/CustomizableWidgets.jsm
@@ -39,22 +39,22 @@ function setAttributes(aNode, aAttrs) {
     } else {
       if (name == "label" || name == "tooltiptext")
         value = CustomizableUI.getLocalizedProperty({id: aAttrs.id}, name);
       aNode.setAttribute(name, value);
     }
   }
 }
 
-function updateCombinedWidgetStyle(aNode, aArea, aModifyAutoclose) {
+function updateCombinedWidgetStyle(aNode, aArea, aModifyCloseMenu) {
   let inPanel = (aArea == CustomizableUI.AREA_PANEL);
   let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
   let attrs = {class: cls};
-  if (aModifyAutoclose) {
-    attrs.noautoclose = inPanel ? true : null;
+  if (aModifyCloseMenu) {
+    attrs.closemenu = inPanel ? "none" : null;
   }
   for (let i = 0, l = aNode.childNodes.length; i < l; ++i) {
     if (aNode.childNodes[i].localName == "separator")
       continue;
     setAttributes(aNode.childNodes[i], attrs);
   }
 }
 
@@ -306,38 +306,38 @@ const CustomizableWidgets = [{
     id: "zoom-controls",
     type: "custom",
     defaultArea: CustomizableUI.AREA_PANEL,
     onBuild: function(aDocument) {
       const kPanelId = "PanelUI-popup";
       let areaType = CustomizableUI.getAreaType(this.currentArea);
       let inPanel = areaType == CustomizableUI.TYPE_MENU_PANEL;
       let inToolbar = areaType == CustomizableUI.TYPE_TOOLBAR;
-      let noautoclose = inPanel ? "true" : null;
+      let closeMenu = inPanel ? "none" : null;
       let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
 
       if (!this.currentArea)
         cls = null;
 
       let buttons = [{
         id: "zoom-out-button",
-        noautoclose: noautoclose,
+        closemenu: closeMenu,
         command: "cmd_fullZoomReduce",
         class: cls,
         label: true,
         tooltiptext: true
       }, {
         id: "zoom-reset-button",
-        noautoclose: noautoclose,
+        closemenu: closeMenu,
         command: "cmd_fullZoomReset",
         class: cls,
         tooltiptext: true
       }, {
         id: "zoom-in-button",
-        noautoclose: noautoclose,
+        closemenu: closeMenu,
         command: "cmd_fullZoomEnlarge",
         class: cls,
         label: true,
         tooltiptext: true
       }];
 
       let node = aDocument.createElementNS(kNSXUL, "toolbaritem");
       node.setAttribute("id", "zoom-controls");
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -43,16 +43,18 @@ function CustomizeMode(aWindow) {
   this.document = aWindow.document;
   this.browser = aWindow.gBrowser;
 
   // There are two palettes - there's the palette that can be overlayed with
   // toolbar items in browser.xul. This is invisible, and never seen by the
   // user. Then there's the visible palette, which gets populated and displayed
   // to the user when in customizing mode.
   this.visiblePalette = this.document.getElementById(kPaletteId);
+  this.paletteEmptyNotice = this.document.getElementById("customization-empty");
+  this.paletteSpacer = this.document.getElementById("customization-spacer");
 };
 
 CustomizeMode.prototype = {
   _changed: false,
   _transitioning: false,
   window: null,
   document: null,
   // areas is used to cache the customizable areas when in customization mode.
@@ -223,16 +225,18 @@ CustomizeMode.prototype = {
 
       CustomizableUI.addListener(this);
       window.PanelUI.endBatchUpdate();
       this._customizing = true;
       this._transitioning = false;
 
       // Show the palette now that the transition has finished.
       this.visiblePalette.hidden = false;
+      this.paletteSpacer.hidden = true;
+      this._updateEmptyPaletteNotice();
 
       this._handler.isEnteringCustomizeMode = false;
       this.dispatchToolboxEvent("customizationready");
       if (!this._wantToBeInCustomizeMode) {
         this.exit();
       }
     }.bind(this)).then(null, function(e) {
       ERROR(e);
@@ -268,17 +272,19 @@ CustomizeMode.prototype = {
 
     this._removePanelCustomizationPlaceholders();
 
     let window = this.window;
     let document = this.document;
     let documentElement = document.documentElement;
 
     // Hide the palette before starting the transition for increased perf.
+    this.paletteSpacer.hidden = false;
     this.visiblePalette.hidden = true;
+    this.paletteEmptyNotice.hidden = true;
 
     this._transitioning = true;
 
     Task.spawn(function() {
       yield this.depopulatePalette();
 
       yield this._doTransition(false);
 
@@ -908,19 +914,25 @@ CustomizeMode.prototype = {
         this.visiblePalette.appendChild(this.makePaletteItem(widget, "palette"));
       }
     }
   },
 
   _onUIChange: function() {
     this._changed = true;
     this._updateResetButton();
+    this._updateEmptyPaletteNotice();
     this.dispatchToolboxEvent("customizationchange");
   },
 
+  _updateEmptyPaletteNotice: function() {
+    let paletteItems = this.visiblePalette.getElementsByTagName("toolbarpaletteitem");
+    this.paletteEmptyNotice.hidden = !!paletteItems.length;
+  },
+
   _updateResetButton: function() {
     let btn = this.document.getElementById("customization-reset-button");
     btn.disabled = CustomizableUI.inDefaultState;
   },
 
   handleEvent: function(aEvent) {
     switch(aEvent.type) {
       case "toolbarvisibilitychange":
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -32,16 +32,17 @@ skip-if = os == "mac"
 # Because of the specific widths, this test is fragile and has been disabled.
 # NB: it was designed for mac only, but started randomly failing there.
 skip-if = true
 
 [browser_914863_disabled_help_quit_buttons.js]
 [browser_918049_skipintoolbarset_dnd.js]
 [browser_923857_customize_mode_event_wrapping_during_reset.js]
 [browser_927717_customize_drag_empty_toolbar.js]
+[browser_932928_show_notice_when_palette_empty.js]
 
 [browser_934113_menubar_removable.js]
 # Because this test is about the menubar, it can't be run on mac
 skip-if = os == "mac"
 
 [browser_946320_tabs_from_other_computers.js]
 skip-if = os == "linux"
 
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_932928_show_notice_when_palette_empty.js
@@ -0,0 +1,35 @@
+/* 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";
+
+// There should be an advert to get more addons when the palette is empty.
+add_task(function() {
+  yield startCustomizing();
+  let visiblePalette = document.getElementById("customization-palette");
+  let emptyPaletteNotice = document.getElementById("customization-empty");
+  is(emptyPaletteNotice.hidden, true, "The empty palette notice should not be shown when there are items in the palette.");
+
+  while (visiblePalette.childElementCount) {
+    gCustomizeMode.addToToolbar(visiblePalette.children[0]);
+  }
+  is(visiblePalette.childElementCount, 0, "There shouldn't be any items remaining in the visible palette.");
+  is(emptyPaletteNotice.hidden, false, "The empty palette notice should be shown when there are no items in the palette.");
+
+  yield endCustomizing();
+  yield startCustomizing();
+  visiblePalette = document.getElementById("customization-palette");
+  emptyPaletteNotice = document.getElementById("customization-empty");
+  is(emptyPaletteNotice.hidden, false,
+     "The empty palette notice should be shown when there are no items in the palette and cust. mode is re-entered.");
+
+  gCustomizeMode.removeFromArea(document.getElementById("wrapper-home-button"));
+  is(emptyPaletteNotice.hidden, true,
+     "The empty palette notice should not be shown when there is at least one item in the palette.");
+});
+
+add_task(function asyncCleanup() {
+  yield endCustomizing();
+  yield resetCustomization();
+});
--- a/browser/devtools/scratchpad/test/browser_scratchpad_wrong_window_focus.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_wrong_window_focus.js
@@ -1,13 +1,14 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 /* Bug 661762 */
 
+
 function test()
 {
   waitForExplicitFinish();
 
   // To test for this bug we open a Scratchpad window, save its
   // reference and then open another one. This way the first window
   // loses its focus.
   //
@@ -20,32 +21,25 @@ function test()
   // is currently active (it should be the older one).
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
     gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
 
     openScratchpad(function () {
       let sw = gScratchpadWindow;
+      let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
       openScratchpad(function () {
-        function onWebConsoleOpen(subj) {
-          Services.obs.removeObserver(onWebConsoleOpen,
-            "web-console-created");
-          subj.QueryInterface(Ci.nsISupportsString);
-
-          let hud = HUDService.getHudReferenceById(subj.data);
+        let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
+        gDevTools.showToolbox(target, "webconsole").then((toolbox) => {
+          let hud = toolbox.getCurrentPanel().hud;
           hud.jsterm.clearOutput(true);
-          executeSoon(testFocus.bind(null, sw, hud));
-        }
-
-        Services.obs.
-          addObserver(onWebConsoleOpen, "web-console-created", false);
-
-        HUDService.toggleWebConsole();
+          testFocus(sw, hud);
+        });
       });
     });
   }, true);
 
   content.location = "data:text/html;charset=utf8,<p>test window focus for Scratchpad.";
 }
 
 function testFocus(sw, hud) {
--- a/browser/devtools/webconsole/hudservice.js
+++ b/browser/devtools/webconsole/hudservice.js
@@ -138,34 +138,16 @@ HUD_SERVICE.prototype =
    * @returns Object
    */
   getHudReferenceById: function HS_getHudReferenceById(aId)
   {
     return this.consoles.get(aId);
   },
 
   /**
-   * Toggle the Web Console for the current tab.
-   *
-   * @return object
-   *         A promise for either the opening of the toolbox that holds the Web
-   *         Console, or a Promise for the closing of the toolbox.
-   */
-  toggleWebConsole: function HS_toggleWebConsole()
-  {
-    let window = this.currentContext();
-    let target = devtools.TargetFactory.forTab(window.gBrowser.selectedTab);
-    let toolbox = gDevTools.getToolbox(target);
-
-    return toolbox && toolbox.currentToolId == "webconsole" ?
-        toolbox.destroy() :
-        gDevTools.showToolbox(target, "webconsole");
-  },
-
-  /**
    * Find if there is a Web Console open for the current tab and return the
    * instance.
    * @return object|null
    *         The WebConsole object or null if the active tab has no open Web
    *         Console.
    */
   getOpenWebConsole: function HS_getOpenWebConsole()
   {
@@ -739,17 +721,17 @@ BrowserConsole.prototype = Heritage.exte
 
     return this._bc_destroyer.promise;
   },
 });
 
 const HUDService = new HUD_SERVICE();
 
 (() => {
-  let methods = ["openWebConsole", "openBrowserConsole", "toggleWebConsole",
+  let methods = ["openWebConsole", "openBrowserConsole",
                  "toggleBrowserConsole", "getOpenWebConsole",
                  "getBrowserConsole", "getHudByWindow", "getHudReferenceById"];
   for (let method of methods) {
     exports[method] = HUDService[method].bind(HUDService);
   }
 
   exports.consoles = HUDService.consoles;
   exports.lastFinishedRequest = HUDService.lastFinishedRequest;
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -110,16 +110,17 @@ support-files =
 [browser_bug_865288_repeat_different_objects.js]
 [browser_bug_865871_variables_view_close_on_esc_key.js]
 [browser_bug_869003_inspect_cross_domain_object.js]
 [browser_bug_871156_ctrlw_close_tab.js]
 [browser_cached_messages.js]
 [browser_console.js]
 [browser_console_addonsdk_loader_exception.js]
 [browser_console_clear_on_reload.js]
+[browser_console_click_focus.js]
 [browser_console_consolejsm_output.js]
 [browser_console_dead_objects.js]
 [browser_console_error_source_click.js]
 [browser_console_filters.js]
 [browser_console_iframe_messages.js]
 [browser_console_keyboard_accessibility.js]
 [browser_console_log_inspectable_object.js]
 [browser_console_native_getters.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_console_click_focus.js
@@ -0,0 +1,39 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+// Tests that the input field is focused when the console is opened.
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
+
+function test() {
+  addTab(TEST_URI);
+  browser.addEventListener("DOMContentLoaded", testInputFocus, false);
+}
+
+function testInputFocus() {
+  browser.removeEventListener("DOMContentLoaded", testInputFocus, false);
+
+  openConsole().then((hud) => {
+    let inputNode = hud.jsterm.inputNode;
+    ok(inputNode.getAttribute("focused"), "input node is focused");
+
+    let lostFocus = () => {
+      inputNode.removeEventListener("blur", lostFocus);
+      info("input node lost focus");
+    }
+
+   	inputNode.addEventListener("blur", lostFocus);
+
+   	browser.ownerDocument.getElementById("urlbar").click();
+
+  	ok(!inputNode.getAttribute("focused"), "input node is not focused");
+
+   	hud.outputNode.click();
+
+   	ok(inputNode.getAttribute("focused"), "input node is focused");
+
+      finishTest();
+  });
+}
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -564,16 +564,29 @@ WebConsoleFrame.prototype = {
     this.jsterm.init();
     this.jsterm.inputNode.focus();
 
     let toolbox = gDevTools.getToolbox(this.owner.target);
     if (toolbox) {
       toolbox.on("webconsole-selected", this._onPanelSelected);
     }
 
+    /*
+     * Focus input line whenever the output area is clicked.
+     * Only focus when the target node (or parent, as in source links) is
+     * not an anchor.
+     */
+    this.outputNode.addEventListener("click", (e) => {
+      if ((e.button == 0) &&
+          (e.target.nodeName.toLowerCase() != "a") &&
+          (e.target.parentNode.nodeName.toLowerCase() != "a")) {
+        this.jsterm.inputNode.focus();
+      }
+    });
+
     // Toggle the timestamp on preference change
     gDevTools.on("pref-changed", this._onToolboxPrefChanged);
     this._onToolboxPrefChanged("pref-changed", {
       pref: PREF_MESSAGE_TIMESTAMP,
       newValue: Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP),
     });
   },
 
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -672,16 +672,18 @@ just addresses the organization to follo
 <!ENTITY social.learnMore.accesskey "l">
 <!ENTITY social.closeNotificationItem.label "Not Now">
 
 
 
 <!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
 <!ENTITY customizeMode.menuAndToolbars.label "Menu and toolbars">
 <!ENTITY customizeMode.menuAndToolbars.header "More Tools to Add to the Menu and Toolbar">
+<!ENTITY customizeMode.menuAndToolbars.empty "Want more tools?">
+<!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
 <!ENTITY customizeMode.restoreDefaults "Restore Defaults">
 <!ENTITY customizeMode.toolbars "Show / Hide Toolbars">
 
 <!ENTITY social.chatBar.commandkey "c">
 <!ENTITY social.chatBar.label "Focus chats">
 <!ENTITY social.chatBar.accesskey "c">
 
 <!ENTITY social.markpageMenu.accesskey "P">
--- a/browser/metro/base/content/Util.js
+++ b/browser/metro/base/content/Util.js
@@ -78,40 +78,54 @@ let Util = {
   },
 
   isTextInput: function isTextInput(aElement) {
     return ((aElement instanceof Ci.nsIDOMHTMLInputElement &&
              aElement.mozIsTextField(false)) ||
             aElement instanceof Ci.nsIDOMHTMLTextAreaElement);
   },
 
+  /**
+   * Checks whether aElement's content can be edited either if it(or any of its
+   * parents) has "contenteditable" attribute set to "true" or aElement's
+   * ownerDocument is in design mode.
+   */
   isEditableContent: function isEditableContent(aElement) {
-    if (!aElement)
-      return false;
-    if (aElement.isContentEditable || aElement.designMode == "on")
-      return true;
-    return false;
+    return !!aElement && (aElement.isContentEditable ||
+                          this.isOwnerDocumentInDesignMode(aElement));
+
   },
 
   isEditable: function isEditable(aElement) {
-    if (!aElement)
+    if (!aElement) {
       return false;
-    if (this.isTextInput(aElement) || this.isEditableContent(aElement))
+    }
+
+    if (this.isTextInput(aElement) || this.isEditableContent(aElement)) {
       return true;
+    }
 
     // If a body element is editable and the body is the child of an
     // iframe or div we can assume this is an advanced HTML editor
     if ((aElement instanceof Ci.nsIDOMHTMLIFrameElement ||
          aElement instanceof Ci.nsIDOMHTMLDivElement) &&
         aElement.contentDocument &&
         this.isEditableContent(aElement.contentDocument.body)) {
       return true;
     }
 
-    return aElement.ownerDocument && aElement.ownerDocument.designMode == "on";
+    return false;
+  },
+
+  /**
+   * Checks whether aElement's owner document has design mode turned on.
+   */
+  isOwnerDocumentInDesignMode: function(aElement) {
+    return !!aElement && !!aElement.ownerDocument &&
+           aElement.ownerDocument.designMode == "on";
   },
 
   isMultilineInput: function isMultilineInput(aElement) {
     return (aElement instanceof Ci.nsIDOMHTMLTextAreaElement);
   },
 
   isLink: function isLink(aElement) {
     return ((aElement instanceof Ci.nsIDOMHTMLAnchorElement && aElement.href) ||
--- a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
+++ b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js
@@ -100,33 +100,35 @@ var ContextMenuHandler = {
   /******************************************************
    * ContextCommand handlers
    */
 
   _onSelectAll: function _onSelectAll() {
     if (Util.isTextInput(this._target)) {
       // select all text in the input control
       this._target.select();
+    } else if (Util.isEditableContent(this._target)) {
+      this._target.ownerDocument.execCommand("selectAll", false);
     } else {
       // select the entire document
       content.getSelection().selectAllChildren(content.document);
     }
     this.reset();
   },
 
   _onPaste: function _onPaste() {
     // paste text if this is an input control
     if (Util.isTextInput(this._target)) {
       let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement);
       if (edit) {
         edit.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
       } else {
         Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
       }
-    } else if (this._target.isContentEditable) {
+    } else if (Util.isEditableContent(this._target)) {
       try {
         this._target.ownerDocument.execCommand("paste",
                                                false,
                                                Ci.nsIClipboard.kGlobalClipboard);
       } catch (ex) {
         dump("ContextMenuHandler: exception pasting into contentEditable: " + ex.message + "\n");
       }
     }
@@ -140,17 +142,17 @@ var ContextMenuHandler = {
   _onCut: function _onCut() {
     if (Util.isTextInput(this._target)) {
       let edit = this._target.QueryInterface(Ci.nsIDOMNSEditableElement);
       if (edit) {
         edit.editor.cut();
       } else {
         Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
       }
-    } else if (this._target.isContentEditable) {
+    } else if (Util.isEditableContent(this._target)) {
       try {
         this._target.ownerDocument.execCommand("cut", false);
       } catch (ex) {
         dump("ContextMenuHandler: exception cutting from contentEditable: " + ex.message + "\n");
       }
     }
     this.reset();
   },
@@ -254,17 +256,19 @@ var ContextMenuHandler = {
           linkUrl = state.linkURL;
           state.linkTitle = popupNode.textContent || popupNode.title;
           state.linkProtocol = this._getProtocol(this._getURI(state.linkURL));
           // mark as text so we can pickup on selection below
           isText = true;
           break;
         }
         // is the target contentEditable (not just inheriting contentEditable)
-        else if (elem.contentEditable == "true") {
+        // or the entire document in designer mode.
+        else if (elem.contentEditable == "true" ||
+                 Util.isOwnerDocumentInDesignMode(elem)) {
           this._target = elem;
           isEditableText = true;
           isText = true;
           uniqueStateTypes.add("input-text");
 
           if (elem.textContent.length) {
             uniqueStateTypes.add("selectable");
           } else {
--- a/browser/metro/base/tests/mochitest/browser_context_menu_tests.js
+++ b/browser/metro/base/tests/mochitest/browser_context_menu_tests.js
@@ -704,12 +704,90 @@ gTests.push({
 
 gTests.push({
   desc: "bug 856264 - touch - context menu should reopen on other links",
   setUp: reopenSetUp,
   tearDown: reopenTearDown,
   run: getReopenTest(sendContextMenuClickToElement, sendTap)
 });
 
+gTests.push({
+  desc: "Bug 947505 - Right-click in a designMode document should display a " +
+        "context menu",
+  run: function test() {
+    info(chromeRoot + "browser_context_menu_tests_02.html");
+    yield addTab(chromeRoot + "browser_context_menu_tests_02.html");
+
+    purgeEventQueue();
+    emptyClipboard();
+    ContextUI.dismiss();
+
+    yield waitForCondition(() => !ContextUI.navbarVisible);
+
+    let tabWindow = Browser.selectedTab.browser.contentWindow;
+    let testSpan = tabWindow.document.getElementById("text1");
+
+    // Case #1: Document isn't in design mode and nothing is selected.
+    tabWindow.document.designMode = "off";
+
+    // Simulate right mouse click to reproduce the same step as noted in the
+    // appropriate bug. It's valid for non-touch case only.
+    synthesizeNativeMouseRDown(Browser.selectedTab.browser, 10, 10);
+    synthesizeNativeMouseRUp(Browser.selectedTab.browser, 10, 10);
+
+    yield waitForCondition(() => ContextUI.navbarVisible);
+
+    ok(ContextUI.navbarVisible, "Navbar is visible on context menu action.");
+    ok(ContextUI.tabbarVisible, "Tabbar is visible on context menu action.");
+
+    ContextUI.dismiss();
+    yield waitForCondition(() => !ContextUI.navbarVisible);
+
+    // Case #2: Document isn't in design mode and text is selected.
+    tabWindow.getSelection().selectAllChildren(testSpan);
+
+    let promise = waitForEvent(tabWindow.document, "popupshown");
+    sendContextMenuClickToSelection(tabWindow);
+    yield promise;
+
+    checkContextUIMenuItemVisibility(["context-copy", "context-search"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+
+    // Case #3: Document is in design mode and nothing is selected.
+    tabWindow.document.designMode = "on";
+    tabWindow.getSelection().removeAllRanges();
+
+    promise = waitForEvent(tabWindow.document, "popupshown");
+    sendContextMenuClickToElement(tabWindow, testSpan);
+    yield promise;
+
+    checkContextUIMenuItemVisibility(["context-select-all", "context-select"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+
+    // Case #4: Document is in design mode and text is selected.
+    tabWindow.getSelection().selectAllChildren(testSpan);
+
+    promise = waitForEvent(tabWindow.document, "popupshown");
+    sendContextMenuClickToSelection(tabWindow);
+    yield promise;
+
+    checkContextUIMenuItemVisibility(["context-cut", "context-copy",
+                                      "context-select-all", "context-select",
+                                      "context-search"]);
+
+    promise = waitForEvent(document, "popuphidden");
+    ContextMenuUI.hide();
+    yield promise;
+
+    Browser.closeTab(Browser.selectedTab, { forceClose: true });
+  }
+});
+
 function test() {
   setDevPixelEqualToPx();
   runTests();
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3955,17 +3955,17 @@ window > chatbox {
   border-bottom-left-radius: @toolbarbuttonCornerRadius@;
   border-bottom-right-radius: @toolbarbuttonCornerRadius@;
 }
 
 /* Customization mode */
 
 %include ../shared/customizableui/customizeMode.inc.css
 
-#main-window[customizing] #titlebar {
+#main-window[customize-entered] #titlebar {
   padding-top: 0;
 }
 
 #main-window:-moz-any([customize-entering],[customize-entered]) #tab-view-deck {
   padding: 0 2em 2em;
 }
 
 #main-window[customize-entered] #tab-view-deck {
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -4,16 +4,17 @@
 
 #include "base/basictypes.h"
 #include "mozilla/Assertions.h"
 #include "DOMCameraPreview.h"
 #include "CameraRecorderProfiles.h"
 #include "CameraControlImpl.h"
 #include "CameraCommon.h"
 #include "nsGlobalWindow.h"
+#include "DeviceStorageFileDescriptor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::idl;
 
 CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId)
   : mCameraId(aCameraId)
   , mCameraThread(aCameraThread)
@@ -445,22 +446,19 @@ CameraControlImpl::TakePicture(const Cam
     cancel = true;
   }
 
   nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, cancel, aSize, aRotation, aFileFormat, aPosition, aDateTime, onSuccess, onError);
   return mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
-CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
+CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor* aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
 {
-  nsCOMPtr<nsIFile> clone;
-  aFolder->Clone(getter_AddRefs(clone));
-
-  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, *aOptions, clone, aFilename, onSuccess, onError, mWindowId);
+  nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, *aOptions, aFileDescriptor, onSuccess, onError, mWindowId);
   return mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL);
 }
 
 nsresult
 CameraControlImpl::StopRecording()
 {
   nsCOMPtr<nsIRunnable> stopRecordingTask = new StopRecordingTask(this);
   return mCameraThread->Dispatch(stopRecordingTask, NS_DISPATCH_NORMAL);
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -9,16 +9,20 @@
 #include "nsDOMFile.h"
 #include "nsProxyRelease.h"
 #include "DictionaryHelpers.h"
 #include "nsIDOMDeviceStorage.h"
 #include "DOMCameraManager.h"
 #include "DOMCameraPreview.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
+#include "DeviceStorage.h"
+#include "DeviceStorageFileDescriptor.h"
+
+class DeviceStorageFileDescriptor;
 
 namespace mozilla {
 
 class GetPreviewStreamTask;
 class StartPreviewTask;
 class StopPreviewTask;
 class AutoFocusTask;
 class TakePictureTask;
@@ -49,17 +53,17 @@ class CameraControlImpl : public ICamera
 public:
   CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId);
 
   nsresult GetPreviewStream(idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StartPreview(DOMCameraPreview* aDOMPreview);
   void StopPreview();
   nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult TakePicture(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
-  nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
+  nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aDSFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult StopRecording();
   nsresult GetPreviewStreamVideoMode(idl::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
   nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError);
 
   nsresult Set(uint32_t aKey, const nsAString& aValue);
   nsresult Get(uint32_t aKey, nsAString& aValue);
   nsresult Set(uint32_t aKey, double aValue);
   nsresult Get(uint32_t aKey, double* aValue);
@@ -447,21 +451,25 @@ protected:
   nsMainThreadPtrHandle<nsICameraStartRecordingCallback> mOnSuccessCb;
   uint64_t mWindowId;
 };
 
 // Start video recording.
 class StartRecordingTask : public nsRunnable
 {
 public:
-  StartRecordingTask(CameraControlImpl* aCameraControl, idl::CameraStartRecordingOptions aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
+  StartRecordingTask(CameraControlImpl* aCameraControl,
+                     idl::CameraStartRecordingOptions aOptions,
+                     DeviceStorageFileDescriptor *aDSFileDescriptor,
+                     nsICameraStartRecordingCallback* onSuccess,
+                     nsICameraErrorCallback* onError,
+                     uint64_t aWindowId)
     : mCameraControl(aCameraControl)
     , mOptions(aOptions)
-    , mFolder(aFolder)
-    , mFilename(aFilename)
+    , mDSFileDescriptor(aDSFileDescriptor)
     , mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraStartRecordingCallback>(onSuccess))
     , mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
     , mWindowId(aWindowId)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   virtual ~StartRecordingTask()
@@ -486,18 +494,17 @@ public:
     if (NS_FAILED(rv)) {
       DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv);
     }
     return rv;
   }
 
   nsRefPtr<CameraControlImpl> mCameraControl;
   idl::CameraStartRecordingOptions mOptions;
-  nsCOMPtr<nsIFile> mFolder;
-  nsString mFilename;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
   nsMainThreadPtrHandle<nsICameraStartRecordingCallback> mOnSuccessCb;
   nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
   uint64_t mWindowId;
 };
 
 // Stop video recording.
 class StopRecordingTask : public nsRunnable
 {
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 #include "nsCOMPtr.h"
 #include "nsDOMClassInfo.h"
 #include "nsHashPropertyBag.h"
 #include "nsThread.h"
 #include "DeviceStorage.h"
+#include "DeviceStorageFileDescriptor.h"
 #include "mozilla/dom/CameraControlBinding.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/MediaManager.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "nsIAppsService.h"
 #include "nsIObserverService.h"
 #include "nsIDOMDeviceStorage.h"
@@ -344,23 +345,22 @@ nsDOMCameraControl::StartRecording(JSCon
                                    JS::Handle<JS::Value> aOptions,
                                    nsDOMDeviceStorage& storageArea,
                                    const nsAString& filename,
                                    nsICameraStartRecordingCallback* onSuccess,
                                    const Optional<nsICameraErrorCallback*>& onError,
                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(onSuccess, "no onSuccess handler passed");
-  mozilla::idl::CameraStartRecordingOptions options;
 
   // Default values, until the dictionary parser can handle them.
-  options.rotation = 0;
-  options.maxFileSizeBytes = 0;
-  options.maxVideoLengthMs = 0;
-  aRv = options.Init(aCx, aOptions.address());
+  mOptions.rotation = 0;
+  mOptions.maxFileSizeBytes = 0;
+  mOptions.maxVideoLengthMs = 0;
+  aRv = mOptions.Init(aCx, aOptions.address());
   if (aRv.Failed()) {
     return;
   }
 
   aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
 
   #ifdef MOZ_B2G
   if (!mAudioChannelAgent) {
@@ -370,23 +370,63 @@ nsDOMCameraControl::StartRecording(JSCon
       mAudioChannelAgent->Init(AUDIO_CHANNEL_CONTENT, nullptr);
       // Video recording doesn't output any sound, so it's not necessary to check canPlay.
       int32_t canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
     }
   }
   #endif
 
-  nsCOMPtr<nsIFile> folder;
-  aRv = storageArea.GetRootDirectoryForFile(filename, getter_AddRefs(folder));
+  nsCOMPtr<nsIDOMDOMRequest> request;
+  mDSFileDescriptor = new DeviceStorageFileDescriptor();
+  aRv = storageArea.CreateFileDescriptor(filename, mDSFileDescriptor.get(),
+                                         getter_AddRefs(request));
   if (aRv.Failed()) {
     return;
   }
-  aRv = mCameraControl->StartRecording(&options, folder, filename, onSuccess,
-                                       onError.WasPassed() ? onError.Value() : nullptr);
+
+  mOnSuccessCb = onSuccess;
+  mOnErrorCb = onError.WasPassed() ? onError.Value() : nullptr;
+
+  request->AddEventListener(NS_LITERAL_STRING("success"), this, false);
+  request->AddEventListener(NS_LITERAL_STRING("error"), this, false);
+}
+
+NS_IMETHODIMP
+nsDOMCameraControl::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsString  eventType;
+  aEvent->GetType(eventType);
+  ErrorResult rv;
+
+  if ((eventType.EqualsLiteral("success")) &&
+      mDSFileDescriptor->mFileDescriptor.IsValid()) {
+
+    rv = mCameraControl->StartRecording(&mOptions,
+                                        mDSFileDescriptor.get(),
+                                        mOnSuccessCb.get(),
+                                        mOnErrorCb.get());
+    if (!rv.Failed()) {
+      return rv.ErrorCode();
+    }
+
+    // An error happened. Fall through and call the error callback.
+  }
+
+  // We're already be on the main thread, so go ahead and call the
+  // error callback directly.
+
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mOnErrorCb &&
+      nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID())) {
+    mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE"));
+  }
+
+  return NS_OK;
 }
 
 void
 nsDOMCameraControl::StopRecording(ErrorResult& aRv)
 {
   aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
 
   #ifdef MOZ_B2G
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -3,41 +3,44 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef DOM_CAMERA_DOMCAMERACONTROL_H
 #define DOM_CAMERA_DOMCAMERACONTROL_H
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsIDOMEventListener.h"
 #include "DictionaryHelpers.h"
 #include "ICameraControl.h"
 #include "DOMCameraPreview.h"
 #include "nsIDOMCameraManager.h"
 #include "CameraCommon.h"
 #include "AudioChannelAgent.h"
 #include "nsProxyRelease.h"
 #include "nsHashPropertyBag.h"
+#include "DeviceStorage.h"
 
 class nsDOMDeviceStorage;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 class CameraPictureOptions;
 template<typename T> class Optional;
 }
 class ErrorResult;
 
 // Main camera control.
-class nsDOMCameraControl MOZ_FINAL : public nsISupports,
+class nsDOMCameraControl MOZ_FINAL : public nsIDOMEventListener,
                                      public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSIDOMEVENTLISTENER
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMCameraControl)
 
   nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread,
                      nsICameraGetCameraCallback* onSuccess,
                      nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow);
   nsresult Result(nsresult aResult,
                   const nsMainThreadPtrHandle<nsICameraGetCameraCallback>& onSuccess,
                   const nsMainThreadPtrHandle<nsICameraErrorCallback>& onError,
@@ -102,16 +105,21 @@ protected:
 
 private:
   nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;
   nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
   nsresult NotifyRecordingStatusChange(const nsString& aMsg);
 
+  mozilla::idl::CameraStartRecordingOptions mOptions;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
+  nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
+  nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
+
 protected:
   /* additional members */
   nsRefPtr<ICameraControl>        mCameraControl; // non-DOM camera control
   nsCOMPtr<nsICameraCapabilities> mDOMCapabilities;
   // An agent used to join audio channel service.
   nsCOMPtr<nsIAudioChannelAgent>  mAudioChannelAgent;
   nsCOMPtr<nsPIDOMWindow> mWindow;
 };
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -37,16 +37,17 @@
 #include "nsIVolumeService.h"
 #include "DOMCameraManager.h"
 #include "GonkCameraHwMgr.h"
 #include "DOMCameraCapabilities.h"
 #include "DOMCameraControl.h"
 #include "GonkRecorderProfiles.h"
 #include "GonkCameraControl.h"
 #include "CameraCommon.h"
+#include "DeviceStorageFileDescriptor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace android;
 using mozilla::gfx::IntSize;
 
 /**
@@ -1018,17 +1019,17 @@ nsGonkCameraControl::TakePictureImpl(Tak
     SetParameter(CameraParameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aTakePicture->mPosition.timestamp).get());
   }
 
   // Add the non-GPS timestamp.  The EXIF date/time field is formatted as
   // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
   // is meant to be stored as a local time.  Since we are given seconds from
   // Epoch GMT, we use localtime_r() to handle the conversion.
   time_t time = aTakePicture->mDateTime;
-  if (time != aTakePicture->mDateTime) {
+  if ((uint64_t)time != aTakePicture->mDateTime) {
     DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aTakePicture->mDateTime);
   } else {
     struct tm t;
     if (localtime_r(&time, &t)) {
       char dateTime[20];
       if (strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
         DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
         // Not every platform defines a CameraParameters::KEY_EXIF_DATETIME;
@@ -1092,52 +1093,34 @@ nsGonkCameraControl::StartRecordingImpl(
   /**
    * Get the base path from device storage and append the app-specified
    * 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);
-
-  nsString fullpath;
-  filename->GetPath(fullpath);
-
-  nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(vs, NS_ERROR_FAILURE);
-
-  nsCOMPtr<nsIVolume> vol;
-  nsresult rv = vs->GetVolumeByPath(fullpath, getter_AddRefs(vol));
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
-
-  nsString volName;
-  vol->GetName(volName);
-
-  mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"),
-                                     volName,
-                                     aStartRecording->mFilename);
-
-  nsAutoCString nativeFilename;
-  filename->GetNativePath(nativeFilename);
-  DOM_CAMERA_LOGI("Video filename is '%s'\n", nativeFilename.get());
+  nsRefPtr<DeviceStorageFileDescriptor> dsfd = aStartRecording->mDSFileDescriptor;
+  NS_ENSURE_TRUE(dsfd, NS_ERROR_FAILURE);
+  nsAutoString fullPath;
+  mVideoFile = dsfd->mDSFile;
+  mVideoFile->GetFullPath(fullPath);
+  DOM_CAMERA_LOGI("Video filename is '%s'\n",
+                  NS_LossyConvertUTF16toASCII(fullPath).get());
 
   if (!mVideoFile->IsSafePath()) {
     DOM_CAMERA_LOGE("Invalid video file name\n");
     return NS_ERROR_INVALID_ARG;
   }
 
-  ScopedClose fd(open(nativeFilename.get(), O_RDWR | O_CREAT, 0644));
-  if (fd < 0) {
-    DOM_CAMERA_LOGE("Couldn't create file '%s': (%d) %s\n", nativeFilename.get(), errno, strerror(errno));
-    return NS_ERROR_FAILURE;
-  }
-
-  rv = SetupRecording(fd, aStartRecording->mOptions.rotation, aStartRecording->mOptions.maxFileSizeBytes, aStartRecording->mOptions.maxVideoLengthMs);
+  nsresult rv;
+  rv = SetupRecording(dsfd->mFileDescriptor.PlatformHandle(),
+                      aStartRecording->mOptions.rotation,
+                      aStartRecording->mOptions.maxFileSizeBytes,
+                      aStartRecording->mOptions.maxVideoLengthMs);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mRecorder->start() != OK) {
     DOM_CAMERA_LOGE("mRecorder->start() failed\n");
     // important: we MUST destroy the recorder if start() fails!
     mRecorder = nullptr;
     return NS_ERROR_FAILURE;
   }
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -5,32 +5,34 @@
 #ifndef DOM_CAMERA_ICAMERACONTROL_H
 #define DOM_CAMERA_ICAMERACONTROL_H
 
 #include "nsIFile.h"
 #include "nsIDOMCameraManager.h"
 #include "DictionaryHelpers.h"
 #include "CameraCommon.h"
 
+class DeviceStorageFileDescriptor;
+
 namespace mozilla {
 
 class DOMCameraPreview;
 class RecorderProfileManager;
 
 class ICameraControl
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl)
 
   virtual nsresult GetPreviewStream(idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult StartPreview(DOMCameraPreview* aDOMPreview) = 0;
   virtual void StopPreview() = 0;
   virtual nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult TakePicture(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
-  virtual nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
+  virtual nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult StopRecording() = 0;
   virtual nsresult GetPreviewStreamVideoMode(idl::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
   virtual nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
 
   virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0;
   virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0;
   virtual nsresult Set(uint32_t aKey, double aValue) = 0;
   virtual nsresult Get(uint32_t aKey, double* aValue) = 0;
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -9,33 +9,37 @@
 
 #include "nsIDOMDeviceStorage.h"
 #include "nsIFile.h"
 #include "nsIPrincipal.h"
 #include "nsIObserver.h"
 #include "nsDOMEventTargetHelper.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
-#include "DOMRequest.h"
+#include "mozilla/dom/DOMRequest.h"
 
 #define DEVICESTORAGE_PICTURES   "pictures"
 #define DEVICESTORAGE_VIDEOS     "videos"
 #define DEVICESTORAGE_MUSIC      "music"
 #define DEVICESTORAGE_APPS       "apps"
 #define DEVICESTORAGE_SDCARD     "sdcard"
 #define DEVICESTORAGE_CRASHES    "crashes"
 
+class DeviceStorageFile;
 class nsIInputStream;
 
 namespace mozilla {
 namespace dom {
 class DeviceStorageEnumerationParameters;
 class DOMCursor;
 class DOMRequest;
 } // namespace dom
+namespace ipc {
+class FileDescriptor;
+}
 } // namespace mozilla
 
 class DeviceStorageFile MOZ_FINAL
   : public nsISupports {
 public:
   nsCOMPtr<nsIFile> mFile;
   nsString mStorageType;
   nsString mStorageName;
@@ -97,16 +101,17 @@ public:
   void GetStatus(nsAString& aStatus);
   void DoFormat(nsAString& aStatus);
   static void GetRootDirectoryForType(const nsAString& aStorageType,
                                       const nsAString& aStorageName,
                                       nsIFile** aFile);
 
   nsresult CalculateSizeAndModifiedDate();
   nsresult CalculateMimeType();
+  nsresult CreateFileDescriptor(mozilla::ipc::FileDescriptor& aFileDescriptor);
 
 private:
   void Init();
   void NormalizeFilePath();
   void AppendRelativePath(const nsAString& aPath);
   void AccumDirectoryUsage(nsIFile* aFile,
                            uint64_t* aPicturesSoFar,
                            uint64_t* aVideosSoFar,
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageFileDescriptor.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et 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/. */
+
+#ifndef DeviceStorageFileDescriptor_h
+#define DeviceStorageFileDescriptor_h
+
+#include "mozilla/ipc/FileDescriptor.h"
+
+class DeviceStorageFileDescriptor MOZ_FINAL
+  : public mozilla::RefCounted<DeviceStorageFileDescriptor>
+{
+public:
+  nsRefPtr<DeviceStorageFile> mDSFile;
+  mozilla::ipc::FileDescriptor mFileDescriptor;
+};
+
+#endif
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -1,35 +1,52 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "DeviceStorageRequestChild.h"
+#include "DeviceStorageFileDescriptor.h"
 #include "nsDeviceStorage.h"
 #include "nsDOMFile.h"
 #include "mozilla/dom/ipc/Blob.h"
 
 namespace mozilla {
 namespace dom {
 namespace devicestorage {
 
 DeviceStorageRequestChild::DeviceStorageRequestChild()
   : mCallback(nullptr)
 {
   MOZ_COUNT_CTOR(DeviceStorageRequestChild);
 }
 
 DeviceStorageRequestChild::DeviceStorageRequestChild(DOMRequest* aRequest,
-                                                     DeviceStorageFile* aFile)
+                                                     DeviceStorageFile* aDSFile)
   : mRequest(aRequest)
-  , mFile(aFile)
+  , mDSFile(aDSFile)
   , mCallback(nullptr)
 {
+  MOZ_ASSERT(aRequest);
+  MOZ_ASSERT(aDSFile);
+  MOZ_COUNT_CTOR(DeviceStorageRequestChild);
+}
+
+DeviceStorageRequestChild::DeviceStorageRequestChild(DOMRequest* aRequest,
+                                                     DeviceStorageFile* aDSFile,
+                                                     DeviceStorageFileDescriptor* aDSFileDescriptor)
+  : mRequest(aRequest)
+  , mDSFile(aDSFile)
+  , mDSFileDescriptor(aDSFileDescriptor)
+  , mCallback(nullptr)
+{
+  MOZ_ASSERT(aRequest);
+  MOZ_ASSERT(aDSFile);
+  MOZ_ASSERT(aDSFileDescriptor);
   MOZ_COUNT_CTOR(DeviceStorageRequestChild);
 }
 
 DeviceStorageRequestChild::~DeviceStorageRequestChild() {
   MOZ_COUNT_DTOR(DeviceStorageRequestChild);
 }
 
 bool
@@ -48,24 +65,40 @@ DeviceStorageRequestChild::
       ErrorResponse r = aValue;
       mRequest->FireError(r.error());
       break;
     }
 
     case DeviceStorageResponseValue::TSuccessResponse:
     {
       nsString fullPath;
-      mFile->GetFullPath(fullPath);
+      mDSFile->GetFullPath(fullPath);
       AutoJSContext cx;
       JS::Rooted<JS::Value> result(cx,
         StringToJsval(mRequest->GetOwner(), fullPath));
       mRequest->FireSuccess(result);
       break;
     }
 
+    case DeviceStorageResponseValue::TFileDescriptorResponse:
+    {
+      FileDescriptorResponse r = aValue;
+
+      nsString fullPath;
+      mDSFile->GetFullPath(fullPath);
+      AutoJSContext cx;
+      JS::Rooted<JS::Value> result(cx,
+        StringToJsval(mRequest->GetOwner(), fullPath));
+
+      mDSFileDescriptor->mDSFile = mDSFile;
+      mDSFileDescriptor->mFileDescriptor = r.fileDescriptor();
+      mRequest->FireSuccess(result);
+      break;
+    }
+
     case DeviceStorageResponseValue::TBlobResponse:
     {
       BlobResponse r = aValue;
       BlobChild* actor = static_cast<BlobChild*>(r.blobChild());
       nsCOMPtr<nsIDOMBlob> blob = actor->GetBlob();
 
       nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
       AutoJSContext cx;
--- a/dom/devicestorage/DeviceStorageRequestChild.h
+++ b/dom/devicestorage/DeviceStorageRequestChild.h
@@ -2,44 +2,50 @@
 /* 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_dom_devicestorage_DeviceStorageRequestChild_h
 #define mozilla_dom_devicestorage_DeviceStorageRequestChild_h
 
 #include "mozilla/dom/devicestorage/PDeviceStorageRequestChild.h"
-#include "DOMRequest.h"
 
 class DeviceStorageFile;
+class DeviceStorageFileDescriptor;
 
 namespace mozilla {
 namespace dom {
+
+class DOMRequest;
+
 namespace devicestorage {
 
 class DeviceStorageRequestChildCallback
 {
   public:
     virtual void RequestComplete() = 0;
 };
 
 class DeviceStorageRequestChild : public PDeviceStorageRequestChild
 {
 public:
   DeviceStorageRequestChild();
   DeviceStorageRequestChild(DOMRequest* aRequest, DeviceStorageFile* aFile);
+  DeviceStorageRequestChild(DOMRequest* aRequest, DeviceStorageFile* aFile,
+                            DeviceStorageFileDescriptor* aFileDescrptor);
   ~DeviceStorageRequestChild();
 
   void SetCallback(class DeviceStorageRequestChildCallback *aCallback);
 
   virtual bool Recv__delete__(const DeviceStorageResponseValue& value);
 
 private:
   nsRefPtr<DOMRequest> mRequest;
-  nsRefPtr<DeviceStorageFile> mFile;
+  nsRefPtr<DeviceStorageFile> mDSFile;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
 
   DeviceStorageRequestChildCallback* mCallback;
 };
 
 } // namespace devicestorage
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/devicestorage/DeviceStorageRequestParent.cpp
+++ b/dom/devicestorage/DeviceStorageRequestParent.cpp
@@ -24,17 +24,17 @@ DeviceStorageRequestParent::DeviceStorag
   : mParams(aParams)
   , mMutex("DeviceStorageRequestParent::mMutex")
   , mActorDestoryed(false)
 {
   MOZ_COUNT_CTOR(DeviceStorageRequestParent);
 
   DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
     = DeviceStorageUsedSpaceCache::CreateOrGet();
-  NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+  MOZ_ASSERT(usedSpaceCache);
 }
 
 void
 DeviceStorageRequestParent::Dispatch()
 {
   switch (mParams.type()) {
     case DeviceStorageParams::TDeviceStorageAddParams:
     {
@@ -48,71 +48,87 @@ DeviceStorageRequestParent::Dispatch()
 
       nsCOMPtr<nsIInputStream> stream;
       blob->GetInternalStream(getter_AddRefs(stream));
 
       nsRefPtr<CancelableRunnable> r = new WriteFileEvent(this, dsf, stream);
 
       nsCOMPtr<nsIEventTarget> target
         = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-      NS_ASSERTION(target, "Must have stream transport service");
+      MOZ_ASSERT(target);
+      target->Dispatch(r, NS_DISPATCH_NORMAL);
+      break;
+    }
+
+    case DeviceStorageParams::TDeviceStorageCreateFdParams:
+    {
+      DeviceStorageCreateFdParams p = mParams;
+
+      nsRefPtr<DeviceStorageFile> dsf =
+        new DeviceStorageFile(p.type(), p.storageName(), p.relpath());
+
+      nsRefPtr<CancelableRunnable> r = new CreateFdEvent(this, dsf);
+
+      nsCOMPtr<nsIEventTarget> target
+        = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageGetParams:
     {
       DeviceStorageGetParams p = mParams;
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName(),
                               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");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageDeleteParams:
     {
       DeviceStorageDeleteParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName(), 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");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageFreeSpaceParams:
     {
       DeviceStorageFreeSpaceParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName());
       nsRefPtr<FreeSpaceFileEvent> r = new FreeSpaceFileEvent(this, dsf);
 
       nsCOMPtr<nsIEventTarget> target
         = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-      NS_ASSERTION(target, "Must have stream transport service");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageUsedSpaceParams:
     {
       DeviceStorageUsedSpaceCache* usedSpaceCache
         = DeviceStorageUsedSpaceCache::CreateOrGet();
-      NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+      MOZ_ASSERT(usedSpaceCache);
 
       DeviceStorageUsedSpaceParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName());
       nsRefPtr<UsedSpaceFileEvent> r = new UsedSpaceFileEvent(this, dsf);
 
       usedSpaceCache->Dispatch(r);
@@ -122,44 +138,46 @@ DeviceStorageRequestParent::Dispatch()
     case DeviceStorageParams::TDeviceStorageAvailableParams:
     {
       DeviceStorageAvailableParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName());
       nsRefPtr<PostAvailableResultEvent> r
         = new PostAvailableResultEvent(this, dsf);
-      NS_DispatchToMainThread(r);
+      DebugOnly<nsresult> rv = NS_DispatchToMainThread(r);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageFormatParams:
     {
       DeviceStorageFormatParams p = mParams;
 
       nsRefPtr<DeviceStorageFile> dsf =
         new DeviceStorageFile(p.type(), p.storageName());
       nsRefPtr<PostFormatResultEvent> r
         = new PostFormatResultEvent(this, dsf);
-      NS_DispatchToMainThread(r);
+      DebugOnly<nsresult> rv = NS_DispatchToMainThread(r);
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
       break;
     }
 
     case DeviceStorageParams::TDeviceStorageEnumerationParams:
     {
       DeviceStorageEnumerationParams p = mParams;
       nsRefPtr<DeviceStorageFile> dsf
         = new DeviceStorageFile(p.type(), p.storageName(),
                                 p.rootdir(), 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");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
       break;
     }
     default:
     {
       NS_RUNTIMEABORT("not reached");
       break;
     }
@@ -182,16 +200,24 @@ DeviceStorageRequestParent::EnsureRequir
     case DeviceStorageParams::TDeviceStorageAddParams:
     {
       DeviceStorageAddParams p = mParams;
       type = p.type();
       requestType = DEVICE_STORAGE_REQUEST_CREATE;
       break;
     }
 
+    case DeviceStorageParams::TDeviceStorageCreateFdParams:
+    {
+      DeviceStorageCreateFdParams p = mParams;
+      type = p.type();
+      requestType = DEVICE_STORAGE_REQUEST_CREATEFD;
+      break;
+    }
+
     case DeviceStorageParams::TDeviceStorageGetParams:
     {
       DeviceStorageGetParams p = mParams;
       type = p.type();
       requestType = DEVICE_STORAGE_REQUEST_READ;
       break;
     }
 
@@ -307,17 +333,17 @@ DeviceStorageRequestParent::PostFreeSpac
 {
 }
 
 DeviceStorageRequestParent::PostFreeSpaceResultEvent::
   ~PostFreeSpaceResultEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostFreeSpaceResultEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   FreeSpaceStorageResponse response(mFreeSpace);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostUsedSpaceResultEvent::
   PostUsedSpaceResultEvent(DeviceStorageRequestParent* aParent,
@@ -329,17 +355,17 @@ DeviceStorageRequestParent::PostUsedSpac
 {
 }
 
 DeviceStorageRequestParent::PostUsedSpaceResultEvent::
   ~PostUsedSpaceResultEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostUsedSpaceResultEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   UsedSpaceStorageResponse response(mUsedSpace);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostErrorEvent::
   PostErrorEvent(DeviceStorageRequestParent* aParent, const char* aError)
@@ -347,34 +373,34 @@ DeviceStorageRequestParent::PostErrorEve
 {
   CopyASCIItoUTF16(aError, mError);
 }
 
 DeviceStorageRequestParent::PostErrorEvent::~PostErrorEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostErrorEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   ErrorResponse response(mError);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostSuccessEvent::
   PostSuccessEvent(DeviceStorageRequestParent* aParent)
   : CancelableRunnable(aParent)
 {
 }
 
 DeviceStorageRequestParent::PostSuccessEvent::~PostSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostSuccessEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   SuccessResponse response;
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
 DeviceStorageRequestParent::PostBlobSuccessEvent::
   PostBlobSuccessEvent(DeviceStorageRequestParent* aParent,
@@ -389,17 +415,17 @@ DeviceStorageRequestParent::PostBlobSucc
   , mMimeType(aMimeType)
 {
 }
 
 DeviceStorageRequestParent::PostBlobSuccessEvent::~PostBlobSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostBlobSuccessEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsString mime;
   CopyASCIItoUTF16(mMimeType, mime);
 
   nsString fullPath;
   mFile->GetFullPath(fullPath);
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(fullPath, mime, mLength, 
                                                 mFile->mFile,
@@ -432,23 +458,64 @@ DeviceStorageRequestParent::PostEnumerat
 {
 }
 
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::
   ~PostEnumerationSuccessEvent() {}
 
 nsresult
 DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   EnumerationResponse response(mStorageType, mRelPath, mPaths);
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
+DeviceStorageRequestParent::CreateFdEvent::
+  CreateFdEvent(DeviceStorageRequestParent* aParent,
+                 DeviceStorageFile* aFile)
+  : CancelableRunnable(aParent)
+  , mFile(aFile)
+{
+}
+
+DeviceStorageRequestParent::CreateFdEvent::~CreateFdEvent()
+{
+}
+
+nsresult
+DeviceStorageRequestParent::CreateFdEvent::CancelableRun()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+
+  nsRefPtr<nsRunnable> r;
+
+  bool check = false;
+  mFile->mFile->Exists(&check);
+  if (check) {
+    nsCOMPtr<PostErrorEvent> event
+      = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS);
+    return NS_DispatchToMainThread(event);
+  }
+
+  FileDescriptor fileDescriptor;
+  nsresult rv = mFile->CreateFileDescriptor(fileDescriptor);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("CreateFileDescriptor failed");
+    mFile->Dump("CreateFileDescriptor failed");
+    r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
+  }
+  else {
+    r = new PostFileDescriptorResultEvent(mParent, fileDescriptor);
+  }
+
+  return NS_DispatchToMainThread(r);
+}
+
 DeviceStorageRequestParent::WriteFileEvent::
   WriteFileEvent(DeviceStorageRequestParent* aParent,
                  DeviceStorageFile* aFile,
                  nsIInputStream* aInputStream)
   : CancelableRunnable(aParent)
   , mFile(aFile)
   , mInputStream(aInputStream)
 {
@@ -456,80 +523,75 @@ DeviceStorageRequestParent::WriteFileEve
 
 DeviceStorageRequestParent::WriteFileEvent::~WriteFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::WriteFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   nsRefPtr<nsRunnable> r;
 
   if (!mInputStream) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   bool check = false;
   mFile->mFile->Exists(&check);
   if (check) {
     nsCOMPtr<PostErrorEvent> event
       = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_EXISTS);
-    NS_DispatchToMainThread(event);
-    return NS_OK;
+    return NS_DispatchToMainThread(event);
   }
 
   nsresult rv = mFile->Write(mInputStream);
 
   if (NS_FAILED(rv)) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
   }
   else {
     r = new PostPathResultEvent(mParent, mFile->mPath);
   }
 
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
-
 DeviceStorageRequestParent::DeleteFileEvent::
   DeleteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
 }
 
 DeviceStorageRequestParent::DeleteFileEvent::~DeleteFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::DeleteFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   mFile->Remove();
 
   nsRefPtr<nsRunnable> r;
 
   bool check = false;
   mFile->mFile->Exists(&check);
   if (check) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
   }
   else {
     r = new PostPathResultEvent(mParent, mFile->mPath);
   }
 
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 DeviceStorageRequestParent::FreeSpaceFileEvent::
   FreeSpaceFileEvent(DeviceStorageRequestParent* aParent,
                      DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
@@ -537,27 +599,26 @@ DeviceStorageRequestParent::FreeSpaceFil
 
 DeviceStorageRequestParent::FreeSpaceFileEvent::~FreeSpaceFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::FreeSpaceFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   int64_t freeSpace = 0;
   if (mFile) {
     mFile->GetDiskFreeSpace(&freeSpace);
   }
 
   nsCOMPtr<nsIRunnable> r;
   r = new PostFreeSpaceResultEvent(mParent, static_cast<uint64_t>(freeSpace));
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 DeviceStorageRequestParent::UsedSpaceFileEvent::
   UsedSpaceFileEvent(DeviceStorageRequestParent* aParent,
                      DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
@@ -565,17 +626,17 @@ DeviceStorageRequestParent::UsedSpaceFil
 
 DeviceStorageRequestParent::UsedSpaceFileEvent::~UsedSpaceFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::UsedSpaceFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
   mFile->AccumDiskUsage(&picturesUsage, &videosUsage,
                         &musicUsage, &totalUsage);
   nsCOMPtr<nsIRunnable> r;
   if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
     r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType,
                                      picturesUsage);
@@ -583,18 +644,17 @@ DeviceStorageRequestParent::UsedSpaceFil
   else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
     r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, videosUsage);
   }
   else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
     r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, musicUsage);
   } else {
     r = new PostUsedSpaceResultEvent(mParent, mFile->mStorageType, totalUsage);
   }
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 DeviceStorageRequestParent::ReadFileEvent::
   ReadFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
   nsCOMPtr<nsIMIMEService> mimeService
@@ -609,48 +669,44 @@ DeviceStorageRequestParent::ReadFileEven
 
 DeviceStorageRequestParent::ReadFileEvent::~ReadFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::ReadFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<nsIRunnable> r;
   bool check = false;
   mFile->mFile->Exists(&check);
 
   if (!check) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   int64_t fileSize;
   nsresult rv = mFile->mFile->GetFileSize(&fileSize);
   if (NS_FAILED(rv)) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   PRTime modDate;
   rv = mFile->mFile->GetLastModifiedTime(&modDate);
   if (NS_FAILED(rv)) {
     r = new PostErrorEvent(mParent, POST_ERROR_EVENT_UNKNOWN);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   r = new PostBlobSuccessEvent(mParent, mFile, static_cast<uint64_t>(fileSize),
                                mMimeType, modDate);
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 DeviceStorageRequestParent::EnumerateFileEvent::
   EnumerateFileEvent(DeviceStorageRequestParent* aParent,
                      DeviceStorageFile* aFile,
                      uint64_t aSince)
   : CancelableRunnable(aParent)
   , mFile(aFile)
@@ -660,44 +716,42 @@ DeviceStorageRequestParent::EnumerateFil
 
 DeviceStorageRequestParent::EnumerateFileEvent::~EnumerateFileEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<nsIRunnable> r;
   if (mFile->mFile) {
     bool check = false;
     mFile->mFile->Exists(&check);
     if (!check) {
       r = new PostErrorEvent(mParent, POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
-      NS_DispatchToMainThread(r);
-      return NS_OK;
+      return NS_DispatchToMainThread(r);
     }
   }
 
   nsTArray<nsRefPtr<DeviceStorageFile> > files;
   mFile->CollectFiles(files, mSince);
 
   InfallibleTArray<DeviceStorageFileValue> values;
 
   uint32_t count = files.Length();
   for (uint32_t i = 0; i < count; i++) {
     DeviceStorageFileValue dsvf(files[i]->mStorageName, files[i]->mPath);
     values.AppendElement(dsvf);
   }
 
   r = new PostEnumerationSuccessEvent(mParent, mFile->mStorageType,
                                       mFile->mRootDir, values);
-  NS_DispatchToMainThread(r);
-  return NS_OK;
+  return NS_DispatchToMainThread(r);
 }
 
 
 DeviceStorageRequestParent::PostPathResultEvent::
   PostPathResultEvent(DeviceStorageRequestParent* aParent,
                       const nsAString& aPath)
   : CancelableRunnable(aParent)
   , mPath(aPath)
@@ -706,40 +760,63 @@ DeviceStorageRequestParent::PostPathResu
 
 DeviceStorageRequestParent::PostPathResultEvent::~PostPathResultEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::PostPathResultEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   SuccessResponse response;
   unused << mParent->Send__delete__(mParent, response);
   return NS_OK;
 }
 
+DeviceStorageRequestParent::PostFileDescriptorResultEvent::
+  PostFileDescriptorResultEvent(DeviceStorageRequestParent* aParent,
+                                const FileDescriptor& aFileDescriptor)
+  : CancelableRunnable(aParent)
+  , mFileDescriptor(aFileDescriptor)
+{
+}
+
+DeviceStorageRequestParent::PostFileDescriptorResultEvent::
+  ~PostFileDescriptorResultEvent()
+{
+}
+
+nsresult
+DeviceStorageRequestParent::PostFileDescriptorResultEvent::CancelableRun()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  FileDescriptorResponse response(mFileDescriptor);
+  unused << mParent->Send__delete__(mParent, response);
+  return NS_OK;
+}
+
 DeviceStorageRequestParent::PostAvailableResultEvent::
   PostAvailableResultEvent(DeviceStorageRequestParent* aParent,
                            DeviceStorageFile* aFile)
   : CancelableRunnable(aParent)
   , mFile(aFile)
 {
 }
 
 DeviceStorageRequestParent::PostAvailableResultEvent::
   ~PostAvailableResultEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::PostAvailableResultEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsString state = NS_LITERAL_STRING("unavailable");
   if (mFile) {
     mFile->GetStatus(state);
   }
 
   AvailableStorageResponse response(state);
   unused << mParent->Send__delete__(mParent, response);
@@ -757,17 +834,17 @@ DeviceStorageRequestParent::PostFormatRe
 DeviceStorageRequestParent::PostFormatResultEvent::
   ~PostFormatResultEvent()
 {
 }
 
 nsresult
 DeviceStorageRequestParent::PostFormatResultEvent::CancelableRun()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread());
 
   nsString state = NS_LITERAL_STRING("unavailable");
   if (mFile) {
     mFile->DoFormat(state);
   }
 
   FormatStorageResponse response(state);
   unused << mParent->Send__delete__(mParent, response);
--- a/dom/devicestorage/DeviceStorageRequestParent.h
+++ b/dom/devicestorage/DeviceStorageRequestParent.h
@@ -114,16 +114,26 @@ private:
       virtual ~PostEnumerationSuccessEvent();
       virtual nsresult CancelableRun();
     private:
       const nsString mStorageType;
       const nsString mRelPath;
       InfallibleTArray<DeviceStorageFileValue> mPaths;
   };
 
+  class CreateFdEvent : public CancelableRunnable
+  {
+    public:
+      CreateFdEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile);
+      virtual ~CreateFdEvent();
+      virtual nsresult CancelableRun();
+    private:
+      nsRefPtr<DeviceStorageFile> mFile;
+  };
+
   class WriteFileEvent : public CancelableRunnable
   {
     public:
       WriteFileEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile, nsIInputStream* aInputStream);
       virtual ~WriteFileEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
@@ -188,16 +198,28 @@ private:
       PostPathResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath);
       virtual ~PostPathResultEvent();
       virtual nsresult CancelableRun();
     private:
       nsRefPtr<DeviceStorageFile> mFile;
       nsString mPath;
   };
 
+  class PostFileDescriptorResultEvent : public CancelableRunnable
+  {
+    public:
+      PostFileDescriptorResultEvent(DeviceStorageRequestParent* aParent,
+                                    const FileDescriptor& aFileDescriptor);
+      virtual ~PostFileDescriptorResultEvent();
+      virtual nsresult CancelableRun();
+    private:
+      nsRefPtr<DeviceStorageFile> mFile;
+      FileDescriptor mFileDescriptor;
+  };
+
  class PostFreeSpaceResultEvent : public CancelableRunnable
  {
     public:
       PostFreeSpaceResultEvent(DeviceStorageRequestParent* aParent,
                                uint64_t aFreeSpace);
       virtual ~PostFreeSpaceResultEvent();
       virtual nsresult CancelableRun();
     private:
--- a/dom/devicestorage/PDeviceStorageRequest.ipdl
+++ b/dom/devicestorage/PDeviceStorageRequest.ipdl
@@ -15,16 +15,21 @@ struct ErrorResponse
 {
   nsString error;
 };
 
 struct SuccessResponse
 {
 };
 
+struct FileDescriptorResponse
+{
+  FileDescriptor fileDescriptor;
+};
+
 struct BlobResponse
 {
   PBlob blob;
 };
 
 struct DeviceStorageFileValue
 {
   nsString storageName;
@@ -57,16 +62,17 @@ struct FormatStorageResponse
 {
   nsString mountState;
 };
 
 union DeviceStorageResponseValue
 {
   ErrorResponse;
   SuccessResponse;
+  FileDescriptorResponse;
   BlobResponse;
   EnumerationResponse;
   FreeSpaceStorageResponse;
   UsedSpaceStorageResponse;
   AvailableStorageResponse;
   FormatStorageResponse;
 };
 
--- a/dom/devicestorage/moz.build
+++ b/dom/devicestorage/moz.build
@@ -3,16 +3,17 @@
 # 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/.
 
 TEST_DIRS += ['test', 'ipc']
 
 EXPORTS += [
     'DeviceStorage.h',
+    'DeviceStorageFileDescriptor.h',
     'nsDeviceStorage.h',
 ]
 
 EXPORTS.mozilla.dom.devicestorage += [
     'DeviceStorageRequestChild.h',
     'DeviceStorageRequestParent.h',
 ]
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -33,27 +33,29 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPrincipal.h"
 #include "nsJSUtils.h"
 #include "DictionaryHelpers.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsXULAppAPI.h"
 #include "TabChild.h"
+#include "DeviceStorageFileDescriptor.h"
 #include "DeviceStorageRequestChild.h"
 #include "nsIDOMDeviceStorageChangeEvent.h"
 #include "nsCRT.h"
 #include "nsIObserverService.h"
 #include "GeneratedEvents.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIPermissionManager.h"
 #include "nsIStringBundle.h"
 #include "nsIDocument.h"
 #include <algorithm>
+#include "private/pprio.h"
 
 #include "mozilla/dom/DeviceStorageBinding.h"
 
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
@@ -62,16 +64,17 @@
 
 #define DEVICESTORAGE_PROPERTIES \
   "chrome://global/content/devicestorage.properties"
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
+using namespace mozilla::ipc;
 
 #include "nsDirectoryServiceDefs.h"
 
 StaticAutoPtr<DeviceStorageUsedSpaceCache>
   DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
 
 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
 {
@@ -244,17 +247,17 @@ DeviceStorageTypeChecker::InitFromBundle
     NS_ConvertASCIItoUTF16(DEVICESTORAGE_VIDEOS).get(),
     getter_Copies(mVideosExtensions));
 }
 
 
 bool
 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIDOMBlob* aBlob)
 {
-  NS_ASSERTION(aBlob, "Calling Check without a blob");
+  MOZ_ASSERT(aBlob);
 
   nsString mimeType;
   if (NS_FAILED(aBlob->GetType(mimeType))) {
     return false;
   }
 
   if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
     return StringBeginsWith(mimeType, NS_LITERAL_STRING("image/"));
@@ -276,17 +279,17 @@ DeviceStorageTypeChecker::Check(const ns
   }
 
   return false;
 }
 
 bool
 DeviceStorageTypeChecker::Check(const nsAString& aType, nsIFile* aFile)
 {
-  NS_ASSERTION(aFile, "Calling Check without a file");
+  MOZ_ASSERT(aFile);
 
   if (aType.EqualsLiteral(DEVICESTORAGE_APPS) ||
       aType.EqualsLiteral(DEVICESTORAGE_SDCARD) ||
       aType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
     // Apps, crashes and sdcard have no restrictions on what file extensions used.
     return true;
   }
 
@@ -316,17 +319,17 @@ DeviceStorageTypeChecker::Check(const ns
   }
 
   return false;
 }
 
 void
 DeviceStorageTypeChecker::GetTypeFromFile(nsIFile* aFile, nsAString& aType)
 {
-  NS_ASSERTION(aFile, "Calling Check without a file");
+  MOZ_ASSERT(aFile);
 
   nsString path;
   aFile->GetPath(path);
 
   GetTypeFromFileName(path, aType);
 }
 
 void
@@ -389,16 +392,17 @@ DeviceStorageTypeChecker::GetAccessForRe
       aAccessResult.AssignLiteral("read");
       break;
     case DEVICE_STORAGE_REQUEST_WRITE:
     case DEVICE_STORAGE_REQUEST_DELETE:
     case DEVICE_STORAGE_REQUEST_FORMAT:
       aAccessResult.AssignLiteral("write");
       break;
     case DEVICE_STORAGE_REQUEST_CREATE:
+    case DEVICE_STORAGE_REQUEST_CREATEFD:
       aAccessResult.AssignLiteral("create");
       break;
     default:
       aAccessResult.AssignLiteral("undefined");
   }
   return NS_OK;
 }
 
@@ -478,17 +482,17 @@ public:
     nsString data;
     CopyASCIItoUTF16(mType, data);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 
     obs->NotifyObservers(mFile, "file-watcher-notify", data.get());
 
     DeviceStorageUsedSpaceCache* usedSpaceCache
       = DeviceStorageUsedSpaceCache::CreateOrGet();
-    NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+    MOZ_ASSERT(usedSpaceCache);
     usedSpaceCache->Invalidate(mFile->mStorageName);
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsCString mType;
 };
@@ -569,17 +573,17 @@ void
 DeviceStorageFile::Init()
 {
   DeviceStorageFile::GetRootDirectoryForType(mStorageType,
                                              mStorageName,
                                              getter_AddRefs(mFile));
 
   DebugOnly<DeviceStorageTypeChecker*> typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
-  NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
+  MOZ_ASSERT(typeChecker);
 }
 
 // Directories which don't depend on a volume should be calculated once
 // here. Directories which depend on the root directory of a volume
 // should be calculated in DeviceStorageFile::GetRootDirectoryForType.
 static void
 InitDirs()
 {
@@ -587,17 +591,17 @@ InitDirs()
     return;
   }
   MOZ_ASSERT(NS_IsMainThread());
   sDirs = new GlobalDirs;
   ClearOnShutdown(&sDirs);
 
   nsCOMPtr<nsIProperties> dirService
     = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
-  NS_ASSERTION(dirService, "Must have directory service");
+  MOZ_ASSERT(dirService);
 
 #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,
@@ -784,17 +788,17 @@ DeviceStorageFile::GetRootDirectoryForTy
 //static
 already_AddRefed<DeviceStorageFile>
 DeviceStorageFile::CreateUnique(nsAString& aFileName,
                                 uint32_t aFileType,
                                 uint32_t aFileAttributes)
 {
   DeviceStorageTypeChecker* typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
-  NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
+  MOZ_ASSERT(typeChecker);
 
   nsString storageType;
   typeChecker->GetTypeFromFileName(aFileName, storageType);
 
   nsString storageName;
   nsString storagePath;
   if (!nsDOMDeviceStorage::ParseFullPath(aFileName, storageName, storagePath)) {
     return nullptr;
@@ -919,29 +923,46 @@ DeviceStorageFile::AppendRelativePath(co
   }
   mFile->AppendRelativePath(temp);
 #else
   mFile->AppendRelativePath(aPath);
 #endif
 }
 
 nsresult
+DeviceStorageFile::CreateFileDescriptor(FileDescriptor& aFileDescriptor)
+{
+  PRFileDesc* fd;
+  nsresult rv = mFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE,
+                                        0660, &fd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aFileDescriptor =
+    FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(fd));
+
+  return NS_OK;
+}
+
+nsresult
 DeviceStorageFile::Write(nsIInputStream* aInputStream)
 {
   if (!aInputStream || !mFile) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "created");
-  NS_DispatchToMainThread(iocomplete);
+  rv = NS_DispatchToMainThread(iocomplete);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   uint64_t bufSize = 0;
   aInputStream->Available(&bufSize);
 
   nsCOMPtr<nsIOutputStream> outputStream;
   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
 
   if (!outputStream) {
@@ -962,54 +983,63 @@ DeviceStorageFile::Write(nsIInputStream*
       &wrote);
     if (NS_FAILED(rv)) {
       break;
     }
     bufSize -= wrote;
   }
 
   iocomplete = new IOEventComplete(this, "modified");
-  NS_DispatchToMainThread(iocomplete);
+  rv = NS_DispatchToMainThread(iocomplete);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   bufferedOutputStream->Close();
   outputStream->Close();
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 DeviceStorageFile::Write(InfallibleTArray<uint8_t>& aBits)
 {
   if (!mFile) {
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = mFile->Create(nsIFile::NORMAL_FILE_TYPE, 00600);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "created");
-  NS_DispatchToMainThread(iocomplete);
+  rv = NS_DispatchToMainThread(iocomplete);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   nsCOMPtr<nsIOutputStream> outputStream;
   NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mFile);
 
   if (!outputStream) {
     return NS_ERROR_FAILURE;
   }
 
   uint32_t wrote;
   outputStream->Write((char*) aBits.Elements(), aBits.Length(), &wrote);
   outputStream->Close();
 
   iocomplete = new IOEventComplete(this, "modified");
-  NS_DispatchToMainThread(iocomplete);
+  rv = NS_DispatchToMainThread(iocomplete);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   if (aBits.Length() != wrote) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 nsresult
@@ -1027,23 +1057,22 @@ DeviceStorageFile::Remove()
     return rv;
   }
 
   if (!check) {
     return NS_OK;
   }
 
   rv = mFile->Remove(true);
-  if (NS_FAILED(rv)) {
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<IOEventComplete> iocomplete = new IOEventComplete(this, "deleted");
-  NS_DispatchToMainThread(iocomplete);
-  return NS_OK;
+  return NS_DispatchToMainThread(iocomplete);
 }
 
 nsresult
 DeviceStorageFile::CalculateMimeType()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsAutoCString mimeType;
@@ -1111,17 +1140,17 @@ DeviceStorageFile::collectFilesInternal(
 
   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
 
     PRTime msecs;
     f->GetLastModifiedTime(&msecs);
 
     if (msecs < aSince) {
       continue;
-     }
+    }
 
     bool isDir;
     f->IsDirectory(&isDir);
 
     bool isFile;
     f->IsFile(&isFile);
 
     nsString fullpath;
@@ -1160,17 +1189,17 @@ DeviceStorageFile::AccumDiskUsage(uint64
     return;
   }
 
   uint64_t pictureUsage = 0, videoUsage = 0, musicUsage = 0, totalUsage = 0;
 
   if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) {
     DeviceStorageUsedSpaceCache* usedSpaceCache =
       DeviceStorageUsedSpaceCache::CreateOrGet();
-    NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+    MOZ_ASSERT(usedSpaceCache);
     nsresult rv = usedSpaceCache->AccumUsedSizes(mStorageName,
                                                  aPicturesSoFar, aVideosSoFar,
                                                  aMusicSoFar, aTotalSoFar);
     if (NS_SUCCEEDED(rv)) {
       return;
     }
     AccumDirectoryUsage(mFile, &pictureUsage, &videoUsage,
                         &musicUsage, &totalUsage);
@@ -1202,18 +1231,17 @@ DeviceStorageFile::AccumDirectoryUsage(n
   nsCOMPtr<nsISimpleEnumerator> e;
   rv = aFile->GetDirectoryEntries(getter_AddRefs(e));
 
   if (NS_FAILED(rv) || !e) {
     return;
   }
 
   nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
-  NS_ASSERTION(files,
-               "GetDirectoryEntries must return a nsIDirectoryEnumerator");
+  MOZ_ASSERT(files);
 
   nsCOMPtr<nsIFile> f;
   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(f))) && f) {
     bool isDir;
     rv = f->IsDirectory(&isDir);
     if (NS_FAILED(rv)) {
       continue;
     }
@@ -1240,17 +1268,17 @@ DeviceStorageFile::AccumDirectoryUsage(n
 
       int64_t size;
       rv = f->GetFileSize(&size);
       if (NS_FAILED(rv)) {
         continue;
       }
       DeviceStorageTypeChecker* typeChecker
         = DeviceStorageTypeChecker::CreateOrGet();
-      NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null");
+      MOZ_ASSERT(typeChecker);
       nsString type;
       typeChecker->GetTypeFromFile(f, type);
 
       if (type.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
         *aPicturesSoFar += size;
       }
       else if (type.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
         *aVideosSoFar += size;
@@ -1436,47 +1464,46 @@ InterfaceToJsval(nsPIDOMWindow* aWindow,
 
   return someJsVal;
 }
 
 JS::Value
 nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ASSERTION(aWindow, "Null Window");
+  MOZ_ASSERT(aWindow);
 
   if (!aFile) {
     return JSVAL_NULL;
   }
 
   if (aFile->mEditable) {
     // TODO - needs janv's file handle support.
     return JSVAL_NULL;
   }
 
   nsString fullPath;
   aFile->GetFullPath(fullPath);
 
   // This check is useful to know if somewhere the DeviceStorageFile
   // has not been properly set. Mimetype is not checked because it can be
   // empty.
-  NS_ASSERTION(aFile->mLength != UINT64_MAX, "Size not set");
-  NS_ASSERTION(aFile->mLastModifiedDate != UINT64_MAX,
-               "LastModifiedDate not set");
+  MOZ_ASSERT(aFile->mLength != UINT64_MAX);
+  MOZ_ASSERT(aFile->mLastModifiedDate != UINT64_MAX);
 
   nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(fullPath, aFile->mMimeType,
                                                 aFile->mLength, aFile->mFile,
                                                 aFile->mLastModifiedDate);
   return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
 }
 
 JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  NS_ASSERTION(aWindow, "Null Window");
+  MOZ_ASSERT(aWindow);
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
   if (!sgo) {
     return JSVAL_NULL;
   }
 
   nsIScriptContext *scriptContext = sgo->GetScriptContext();
   if (!scriptContext) {
@@ -1615,25 +1642,27 @@ ContinueCursorEvent::GetNextFile()
 }
 
 ContinueCursorEvent::~ContinueCursorEvent() {}
 
 void
 ContinueCursorEvent::Continue()
 {
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    NS_DispatchToMainThread(this);
+    DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
     return;
   }
 
   nsRefPtr<DeviceStorageFile> file = GetNextFile();
 
   if (!file) {
     // done with enumeration.
-    NS_DispatchToMainThread(this);
+    DebugOnly<nsresult> rv = NS_DispatchToMainThread(this);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
     return;
   }
 
   nsDOMDeviceStorageCursor* cursor
     = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
   nsString cursorStorageType;
   cursor->GetStorageType(cursorStorageType);
 
@@ -1686,18 +1715,17 @@ public:
 
     if (mFile->mFile) {
       bool check;
       mFile->mFile->IsDirectory(&check);
       if (!check) {
         nsCOMPtr<PostErrorEvent> event =
           new PostErrorEvent(mRequest.forget(),
                              POST_ERROR_EVENT_FILE_NOT_ENUMERABLE);
-        NS_DispatchToMainThread(event);
-        return NS_OK;
+        return NS_DispatchToMainThread(event);
       }
     }
 
     nsDOMDeviceStorageCursor* cursor
       = static_cast<nsDOMDeviceStorageCursor*>(mRequest.get());
     mFile->CollectFiles(cursor->mFiles, cursor->mSince);
 
     nsCOMPtr<ContinueCursorEvent> event
@@ -1777,45 +1805,43 @@ nsDOMDeviceStorageCursor::GetElement(nsI
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorageCursor::Cancel()
 {
   nsCOMPtr<PostErrorEvent> event
     = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
-  NS_DispatchToMainThread(event);
-  return NS_OK;
+  return NS_DispatchToMainThread(event);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorageCursor::Allow()
 {
   if (!mFile->IsSafePath()) {
     nsCOMPtr<nsIRunnable> r
       = new PostErrorEvent(this, POST_ERROR_EVENT_PERMISSION_DENIED);
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     PDeviceStorageRequestChild* child
       = new DeviceStorageRequestChild(this, mFile);
     DeviceStorageEnumerationParams params(mFile->mStorageType,
                                           mFile->mStorageName,
                                           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");
+  MOZ_ASSERT(target);
 
   nsCOMPtr<InitCursorEvent> event = new InitCursorEvent(this, mFile);
   target->Dispatch(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 void
 nsDOMDeviceStorageCursor::Continue(ErrorResult& aRv)
@@ -1854,27 +1880,28 @@ void
 nsDOMDeviceStorageCursor::IPDLRelease()
 {
   Release();
 }
 
 void
 nsDOMDeviceStorageCursor::RequestComplete()
 {
-  NS_ASSERTION(!mOkToCallContinue, "mOkToCallContinue must be false");
+  MOZ_ASSERT(!mOkToCallContinue);
   mOkToCallContinue = true;
 }
 
 class PostAvailableResultEvent : public nsRunnable
 {
 public:
   PostAvailableResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
   {
+    MOZ_ASSERT(mRequest);
   }
 
   ~PostAvailableResultEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -1898,16 +1925,17 @@ private:
 
 class PostFormatResultEvent : public nsRunnable
 {
 public:
   PostFormatResultEvent(DeviceStorageFile *aFile, DOMRequest* aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
   {
+    MOZ_ASSERT(mRequest);
   }
 
   ~PostFormatResultEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -1931,29 +1959,35 @@ private:
 
 class PostResultEvent : public nsRunnable
 {
 public:
   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
                   DeviceStorageFile* aFile)
     : mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mRequest);
+  }
 
   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
                   const nsAString & aPath)
     : mPath(aPath)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mRequest);
+  }
 
   PostResultEvent(already_AddRefed<DOMRequest> aRequest,
                   const uint64_t aValue)
     : mValue(aValue)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mRequest);
+  }
 
   ~PostResultEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     AutoJSContext cx;
@@ -1976,78 +2010,131 @@ public:
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsString mPath;
   uint64_t mValue;
   nsRefPtr<DOMRequest> mRequest;
 };
 
+class CreateFdEvent : public nsRunnable
+{
+public:
+  CreateFdEvent(DeviceStorageFileDescriptor* aDSFileDescriptor,
+                already_AddRefed<DOMRequest> aRequest)
+    : mDSFileDescriptor(aDSFileDescriptor)
+    , mRequest(aRequest)
+  {
+    MOZ_ASSERT(mDSFileDescriptor);
+    MOZ_ASSERT(mRequest);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    DeviceStorageFile* dsFile = mDSFileDescriptor->mDSFile;
+    MOZ_ASSERT(dsFile);
+
+    nsString fullPath;
+    dsFile->GetFullPath(fullPath);
+    MOZ_ASSERT(!fullPath.IsEmpty());
+
+    bool check = false;
+    dsFile->mFile->Exists(&check);
+    if (check) {
+      nsCOMPtr<PostErrorEvent> event =
+        new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
+      return NS_DispatchToMainThread(event);
+    }
+
+    nsresult rv = dsFile->CreateFileDescriptor(mDSFileDescriptor->mFileDescriptor);
+
+    if (NS_FAILED(rv)) {
+      dsFile->mFile->Remove(false);
+
+      nsCOMPtr<PostErrorEvent> event =
+        new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
+      return NS_DispatchToMainThread(event);
+    }
+
+    nsCOMPtr<PostResultEvent> event =
+      new PostResultEvent(mRequest.forget(), fullPath);
+    return NS_DispatchToMainThread(event);
+  }
+
+private:
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
+  nsRefPtr<DOMRequest> mRequest;
+};
+
 class WriteFileEvent : public nsRunnable
 {
 public:
   WriteFileEvent(nsIDOMBlob* aBlob,
                  DeviceStorageFile *aFile,
                  already_AddRefed<DOMRequest> aRequest)
     : mBlob(aBlob)
     , mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
 
   ~WriteFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsCOMPtr<nsIInputStream> stream;
     mBlob->GetInternalStream(getter_AddRefs(stream));
 
     bool check = false;
     mFile->mFile->Exists(&check);
     if (check) {
       nsCOMPtr<PostErrorEvent> event =
         new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_FILE_EXISTS);
-      NS_DispatchToMainThread(event);
-      return NS_OK;
+      return NS_DispatchToMainThread(event);
     }
 
     nsresult rv = mFile->Write(stream);
 
     if (NS_FAILED(rv)) {
       mFile->mFile->Remove(false);
 
       nsCOMPtr<PostErrorEvent> event =
         new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
-      NS_DispatchToMainThread(event);
-      return NS_OK;
+      return NS_DispatchToMainThread(event);
     }
 
     nsString fullPath;
     mFile->GetFullPath(fullPath);
     nsCOMPtr<PostResultEvent> event =
       new PostResultEvent(mRequest.forget(), fullPath);
-    NS_DispatchToMainThread(event);
-    return NS_OK;
+    return NS_DispatchToMainThread(event);
   }
 
 private:
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class ReadFileEvent : public nsRunnable
 {
 public:
   ReadFileEvent(DeviceStorageFile* aFile,
                 already_AddRefed<DOMRequest> aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
   {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
     mFile->CalculateMimeType();
   }
 
   ~ReadFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
@@ -2067,33 +2154,35 @@ public:
       if (NS_FAILED(rv)) {
         r = new PostErrorEvent(mRequest.forget(), POST_ERROR_EVENT_UNKNOWN);
       }
     }
 
     if (!r) {
       r = new PostResultEvent(mRequest.forget(), mFile);
     }
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class DeleteFileEvent : public nsRunnable
 {
 public:
   DeleteFileEvent(DeviceStorageFile* aFile,
                   already_AddRefed<DOMRequest> aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
 
   ~DeleteFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
     mFile->Remove();
 
@@ -2104,33 +2193,35 @@ public:
       r = new PostErrorEvent(mRequest.forget(),
                              POST_ERROR_EVENT_FILE_DOES_NOT_EXIST);
     }
     else {
       nsString fullPath;
       mFile->GetFullPath(fullPath);
       r = new PostResultEvent(mRequest.forget(), fullPath);
     }
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class UsedSpaceFileEvent : public nsRunnable
 {
 public:
   UsedSpaceFileEvent(DeviceStorageFile* aFile,
                      already_AddRefed<DOMRequest> aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
 
   ~UsedSpaceFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     uint64_t picturesUsage = 0, videosUsage = 0, musicUsage = 0, totalUsage = 0;
@@ -2143,90 +2234,124 @@ public:
     else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
       r = new PostResultEvent(mRequest.forget(), videosUsage);
     }
     else if (mFile->mStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
       r = new PostResultEvent(mRequest.forget(), musicUsage);
     } else {
       r = new PostResultEvent(mRequest.forget(), totalUsage);
     }
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class FreeSpaceFileEvent : public nsRunnable
 {
 public:
   FreeSpaceFileEvent(DeviceStorageFile* aFile,
                      already_AddRefed<DOMRequest> aRequest)
     : mFile(aFile)
     , mRequest(aRequest)
-  {}
+  {
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
 
   ~FreeSpaceFileEvent() {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     int64_t freeSpace = 0;
     if (mFile) {
       mFile->GetDiskFreeSpace(&freeSpace);
     }
 
     nsCOMPtr<nsIRunnable> r;
     r = new PostResultEvent(mRequest.forget(),
                             static_cast<uint64_t>(freeSpace));
-    NS_DispatchToMainThread(r);
-    return NS_OK;
+    return NS_DispatchToMainThread(r);
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
 };
 
 class DeviceStorageRequest MOZ_FINAL
   : public nsIContentPermissionRequest
   , public nsIRunnable
   , public PCOMContentPermissionRequestChild
 {
 public:
 
-    DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
-                         nsPIDOMWindow *aWindow,
-                         nsIPrincipal *aPrincipal,
-                         DeviceStorageFile *aFile,
-                         DOMRequest* aRequest,
-                         nsDOMDeviceStorage *aDeviceStorage)
-      : mRequestType(aRequestType)
-      , mWindow(aWindow)
-      , mPrincipal(aPrincipal)
-      , mFile(aFile)
-      , mRequest(aRequest)
-      , mDeviceStorage(aDeviceStorage)
-    {}
-
-    DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
-                         nsPIDOMWindow *aWindow,
-                         nsIPrincipal *aPrincipal,
-                         DeviceStorageFile *aFile,
-                         DOMRequest* aRequest,
-                         nsIDOMBlob *aBlob = nullptr)
-      : mRequestType(aRequestType)
-      , mWindow(aWindow)
-      , mPrincipal(aPrincipal)
-      , mFile(aFile)
-      , mRequest(aRequest)
-      , mBlob(aBlob) {}
+  DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
+                       nsPIDOMWindow* aWindow,
+                       nsIPrincipal* aPrincipal,
+                       DeviceStorageFile* aFile,
+                       DOMRequest* aRequest,
+                       nsDOMDeviceStorage* aDeviceStorage)
+    : mRequestType(aRequestType)
+    , mWindow(aWindow)
+    , mPrincipal(aPrincipal)
+    , mFile(aFile)
+    , mRequest(aRequest)
+    , mDeviceStorage(aDeviceStorage)
+  {
+    MOZ_ASSERT(mWindow);
+    MOZ_ASSERT(mPrincipal);
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+    MOZ_ASSERT(mDeviceStorage);
+  }
+
+  DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
+                       nsPIDOMWindow* aWindow,
+                       nsIPrincipal* aPrincipal,
+                       DeviceStorageFile* aFile,
+                       DOMRequest* aRequest,
+                       nsIDOMBlob* aBlob = nullptr)
+    : mRequestType(aRequestType)
+    , mWindow(aWindow)
+    , mPrincipal(aPrincipal)
+    , mFile(aFile)
+    , mRequest(aRequest)
+    , mBlob(aBlob)
+  {
+    MOZ_ASSERT(mWindow);
+    MOZ_ASSERT(mPrincipal);
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+  }
+
+  DeviceStorageRequest(const DeviceStorageRequestType aRequestType,
+                       nsPIDOMWindow* aWindow,
+                       nsIPrincipal* aPrincipal,
+                       DeviceStorageFile* aFile,
+                       DOMRequest* aRequest,
+                       DeviceStorageFileDescriptor* aDSFileDescriptor)
+    : mRequestType(aRequestType)
+    , mWindow(aWindow)
+    , mPrincipal(aPrincipal)
+    , mFile(aFile)
+    , mRequest(aRequest)
+    , mDSFileDescriptor(aDSFileDescriptor)
+  {
+    MOZ_ASSERT(mRequestType == DEVICE_STORAGE_REQUEST_CREATEFD);
+    MOZ_ASSERT(mWindow);
+    MOZ_ASSERT(mPrincipal);
+    MOZ_ASSERT(mFile);
+    MOZ_ASSERT(mRequest);
+    MOZ_ASSERT(mDSFileDescriptor);
+  }
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest,
                                            nsIContentPermissionRequest)
 
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -2316,51 +2441,89 @@ public:
     return NS_OK;
   }
 
   NS_IMETHOD Cancel()
   {
     nsCOMPtr<PostErrorEvent> event
       = new PostErrorEvent(mRequest.forget(),
                            POST_ERROR_EVENT_PERMISSION_DENIED);
-    NS_DispatchToMainThread(event);
-    return NS_OK;
+    return NS_DispatchToMainThread(event);
   }
 
   NS_IMETHOD Allow()
   {
-    nsCOMPtr<nsIRunnable> r;
+    MOZ_ASSERT(NS_IsMainThread());
 
     if (!mRequest) {
       return NS_ERROR_FAILURE;
     }
 
+    nsCOMPtr<nsIRunnable> r;
+
     switch(mRequestType) {
+      case DEVICE_STORAGE_REQUEST_CREATEFD:
+      {
+        if (!mFile->mFile) {
+          return NS_ERROR_FAILURE;
+        }
+
+        DeviceStorageTypeChecker* typeChecker
+          = DeviceStorageTypeChecker::CreateOrGet();
+        if (!typeChecker) {
+          return NS_OK;
+        }
+
+        if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
+          r = new PostErrorEvent(mRequest.forget(),
+                                 POST_ERROR_EVENT_ILLEGAL_TYPE);
+          return NS_DispatchToCurrentThread(r);
+        }
+
+        if (XRE_GetProcessType() != GeckoProcessType_Default) {
+
+          DeviceStorageCreateFdParams params;
+          params.type() = mFile->mStorageType;
+          params.storageName() = mFile->mStorageName;
+          params.relpath() = mFile->mPath;
+
+          mFile->Dump("DeviceStorageCreateFdParams");
+
+          PDeviceStorageRequestChild* child
+            = new DeviceStorageRequestChild(mRequest, mFile,
+                                            mDSFileDescriptor.get());
+          ContentChild::GetSingleton()
+            ->SendPDeviceStorageRequestConstructor(child, params);
+          return NS_OK;
+        }
+        mDSFileDescriptor->mDSFile = mFile;
+        r = new CreateFdEvent(mDSFileDescriptor.get(), mRequest.forget());
+        break;
+      }
+
       case DEVICE_STORAGE_REQUEST_CREATE:
       {
         if (!mBlob || !mFile->mFile) {
           return NS_ERROR_FAILURE;
         }
 
         DeviceStorageTypeChecker* typeChecker
           = DeviceStorageTypeChecker::CreateOrGet();
         if (!typeChecker) {
           return NS_OK;
         }
 
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile) ||
             !typeChecker->Check(mFile->mStorageType, mBlob)) {
           r = new PostErrorEvent(mRequest.forget(),
                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
-          NS_DispatchToMainThread(r);
-          return NS_OK;
+          return NS_DispatchToCurrentThread(r);
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
-
           BlobChild* actor
             = ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlob);
           if (!actor) {
             return NS_ERROR_FAILURE;
           }
 
           DeviceStorageAddParams params;
           params.blobChild() = actor;
@@ -2389,18 +2552,17 @@ public:
           = DeviceStorageTypeChecker::CreateOrGet();
         if (!typeChecker) {
           return NS_OK;
         }
 
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
           r = new PostErrorEvent(mRequest.forget(),
                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
-          NS_DispatchToMainThread(r);
-          return NS_OK;
+          return NS_DispatchToCurrentThread(r);
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child
             = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageGetParams params(mFile->mStorageType,
                                         mFile->mStorageName,
                                         mFile->mRootDir,
@@ -2424,18 +2586,17 @@ public:
           = DeviceStorageTypeChecker::CreateOrGet();
         if (!typeChecker) {
           return NS_OK;
         }
 
         if (!typeChecker->Check(mFile->mStorageType, mFile->mFile)) {
           r = new PostErrorEvent(mRequest.forget(),
                                  POST_ERROR_EVENT_ILLEGAL_TYPE);
-          NS_DispatchToMainThread(r);
-          return NS_OK;
+          return NS_DispatchToCurrentThread(r);
         }
 
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
           PDeviceStorageRequestChild* child
             = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageDeleteParams params(mFile->mStorageType,
                                            mFile->mStorageName,
                                            mFile->mPath);
@@ -2472,17 +2633,17 @@ public:
           ContentChild::GetSingleton()
             ->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         // this needs to be dispatched to only one (1)
         // thread or we will do more work than required.
         DeviceStorageUsedSpaceCache* usedSpaceCache
           = DeviceStorageUsedSpaceCache::CreateOrGet();
-        NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+        MOZ_ASSERT(usedSpaceCache);
         r = new UsedSpaceFileEvent(mFile, mRequest.forget());
         usedSpaceCache->Dispatch(r);
         return NS_OK;
       }
 
       case DEVICE_STORAGE_REQUEST_AVAILABLE:
       {
         if (XRE_GetProcessType() != GeckoProcessType_Default) {
@@ -2490,18 +2651,17 @@ public:
             = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageAvailableParams params(mFile->mStorageType,
                                               mFile->mStorageName);
           ContentChild::GetSingleton()
             ->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new PostAvailableResultEvent(mFile, mRequest);
-        NS_DispatchToMainThread(r);
-        return NS_OK;
+        return NS_DispatchToCurrentThread(r);
       }
 
       case DEVICE_STORAGE_REQUEST_WATCH:
       {
         mDeviceStorage->mAllowedToWatchFile = true;
         return NS_OK;
       }
 
@@ -2512,26 +2672,25 @@ public:
             = new DeviceStorageRequestChild(mRequest, mFile);
           DeviceStorageFormatParams params(mFile->mStorageType,
                                            mFile->mStorageName);
           ContentChild::GetSingleton()
             ->SendPDeviceStorageRequestConstructor(child, params);
           return NS_OK;
         }
         r = new PostFormatResultEvent(mFile, mRequest);
-        NS_DispatchToMainThread(r);
-        return NS_OK;
+        return NS_DispatchToCurrentThread(r);
       }
 
     }
 
     if (r) {
       nsCOMPtr<nsIEventTarget> target
         = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-      NS_ASSERTION(target, "Must have stream transport service");
+      MOZ_ASSERT(target);
       target->Dispatch(r, NS_DISPATCH_NORMAL);
     }
 
     return NS_OK;
   }
 
   bool Recv__delete__(const bool& allow)
   {
@@ -2553,16 +2712,17 @@ private:
   int32_t mRequestType;
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsRefPtr<DeviceStorageFile> mFile;
 
   nsRefPtr<DOMRequest> mRequest;
   nsCOMPtr<nsIDOMBlob> mBlob;
   nsRefPtr<nsDOMDeviceStorage> mDeviceStorage;
+  nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
 };
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeviceStorageRequest)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
 NS_INTERFACE_MAP_END
 
@@ -2598,19 +2758,19 @@ nsDOMDeviceStorage::WrapObject(JSContext
 }
 
 nsresult
 nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType,
                          const nsAString &aVolName)
 {
   DebugOnly<FileUpdateDispatcher*> observer
     = FileUpdateDispatcher::GetSingleton();
-  NS_ASSERTION(observer, "FileUpdateDispatcher is null");
-
-  NS_ASSERTION(aWindow, "Must have a content dom");
+  MOZ_ASSERT(observer);
+
+  MOZ_ASSERT(aWindow);
 
   SetRootDirectoryForType(aType, aVolName);
   if (!mRootDirectory) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   if (!mStorageName.IsEmpty()) {
     RegisterForSDCardChanges(this);
   }
@@ -2794,16 +2954,18 @@ nsDOMDeviceStorage::GetStorage(const nsA
     ds = GetStorageByName(storageName);
   }
   return ds.forget();
 }
 
 already_AddRefed<nsDOMDeviceStorage>
 nsDOMDeviceStorage::GetStorageByName(const nsAString& aStorageName)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsRefPtr<nsDOMDeviceStorage> ds;
 
   if (mStorageName.Equals(aStorageName)) {
     ds = this;
     return ds.forget();
   }
   VolumeNameArray volNames;
   GetOrderedVolumeNames(volNames);
@@ -2909,16 +3071,18 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob 
   request.forget(_retval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::AddNamed(nsIDOMBlob* aBlob, const nsAString& aPath,
                              ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   // if the blob is null here, bail
   if (!aBlob) {
     return nullptr;
   }
 
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
@@ -2928,24 +3092,28 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob*
   DeviceStorageTypeChecker* typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
   if (!typeChecker) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIRunnable> r;
+  nsresult rv;
 
   if (IsFullPath(aPath)) {
     nsString storagePath;
     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
     if (!ds) {
       nsRefPtr<DOMRequest> request = new DOMRequest(win);
       r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
-      NS_DispatchToMainThread(r);
+      rv = NS_DispatchToCurrentThread(r);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+      }
       return request.forget();
     }
     return ds->AddNamed(aBlob, storagePath, aRv);
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
@@ -2956,17 +3124,20 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob*
   } 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,
                                  win, mPrincipal, dsf, request, aBlob);
   }
 
-  NS_DispatchToMainThread(r);
+  rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Get(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = Get(aPath, rv);
@@ -2983,243 +3154,322 @@ nsDOMDeviceStorage::GetEditable(const ns
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::GetInternal(const nsAString& aPath, bool aEditable,
                                 ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   if (IsFullPath(aPath)) {
     nsString storagePath;
     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
     if (!ds) {
       nsCOMPtr<nsIRunnable> r =
         new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
-      NS_DispatchToMainThread(r);
+      nsresult rv = NS_DispatchToCurrentThread(r);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+      }
       return request.forget();
     }
     ds->GetInternal(win, storagePath, request, aEditable);
     return request.forget();
   }
   GetInternal(win, aPath, request, aEditable);
   return request.forget();
 }
 
 void
 nsDOMDeviceStorage::GetInternal(nsPIDOMWindow *aWin,
                                 const nsAString& aPath,
                                 DOMRequest* aRequest,
                                 bool aEditable)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName,
                                                           aPath);
   dsf->SetEditable(aEditable);
 
   nsCOMPtr<nsIRunnable> r;
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
   } else {
     r = new DeviceStorageRequest(aEditable ? DEVICE_STORAGE_REQUEST_WRITE
                                            : DEVICE_STORAGE_REQUEST_READ,
                                  aWin, mPrincipal, dsf, aRequest);
   }
-  NS_DispatchToMainThread(r);
+  DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Delete(const nsAString& aPath, nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = Delete(aPath, rv);
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::Delete(const nsAString& aPath, ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   if (IsFullPath(aPath)) {
     nsString storagePath;
     nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
     if (!ds) {
       nsCOMPtr<nsIRunnable> r =
         new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
-      NS_DispatchToMainThread(r);
+      nsresult rv = NS_DispatchToCurrentThread(r);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(rv);
+      }
       return request.forget();
     }
     ds->DeleteInternal(win, storagePath, request);
     return request.forget();
   }
   DeleteInternal(win, aPath, request);
   return request.forget();
 }
 
 void
 nsDOMDeviceStorage::DeleteInternal(nsPIDOMWindow *aWin,
                                    const nsAString& aPath,
                                    DOMRequest* aRequest)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsIRunnable> r;
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName,
                                                           aPath);
   if (!dsf->IsSafePath()) {
     r = new PostErrorEvent(aRequest, POST_ERROR_EVENT_PERMISSION_DENIED);
   } else {
     r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_DELETE,
                                  aWin, mPrincipal, dsf, aRequest);
   }
-  NS_DispatchToMainThread(r);
+  DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(r);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = FreeSpace(rv);
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::FreeSpace(ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE,
                                win, mPrincipal, dsf, request);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = UsedSpace(rv);
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::UsedSpace(ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   DebugOnly<DeviceStorageUsedSpaceCache*> usedSpaceCache
     = DeviceStorageUsedSpaceCache::CreateOrGet();
-  NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+  MOZ_ASSERT(usedSpaceCache);
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE,
                                win, mPrincipal, dsf, request);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval)
 {
   ErrorResult rv;
   nsRefPtr<DOMRequest> request = Available(rv);
   request.forget(aRetval);
   return rv.ErrorCode();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::Available(ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE,
                                win, mPrincipal, dsf, request);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 already_AddRefed<DOMRequest>
 nsDOMDeviceStorage::Format(ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
 
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FORMAT,
                                win, mPrincipal, dsf, request);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
   return request.forget();
 }
 
 NS_IMETHODIMP
-nsDOMDeviceStorage::GetRootDirectoryForFile(const nsAString& aName,
-                                            nsIFile** aRootDirectory)
+nsDOMDeviceStorage::CreateFileDescriptor(const nsAString& aPath,
+                                         DeviceStorageFileDescriptor* aDSFileDescriptor,
+                                         nsIDOMDOMRequest** aRequest)
 {
-  nsRefPtr<nsDOMDeviceStorage> ds;
-
-  if (IsFullPath(aName)) {
-    nsString storagePath;
-    ds = GetStorage(aName, storagePath);
-  } else {
-    ds = this;
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aDSFileDescriptor);
+
+  nsCOMPtr<nsPIDOMWindow> win = GetOwner();
+  if (!win) {
+    return NS_ERROR_UNEXPECTED;
   }
-  if (!ds || !ds->mRootDirectory) {
+
+  DeviceStorageTypeChecker* typeChecker
+    = DeviceStorageTypeChecker::CreateOrGet();
+  if (!typeChecker) {
     return NS_ERROR_FAILURE;
   }
-  return ds->mRootDirectory->Clone(aRootDirectory);
+
+  nsCOMPtr<nsIRunnable> r;
+  nsresult rv;
+
+  if (IsFullPath(aPath)) {
+    nsString storagePath;
+    nsRefPtr<nsDOMDeviceStorage> ds = GetStorage(aPath, storagePath);
+    if (!ds) {
+      nsRefPtr<DOMRequest> request = new DOMRequest(win);
+      r = new PostErrorEvent(request, POST_ERROR_EVENT_UNKNOWN);
+      rv = NS_DispatchToCurrentThread(r);
+      if (NS_FAILED(rv)) {
+        return rv;
+      }
+      request.forget(aRequest);
+      return NS_OK;
+    }
+    return ds->CreateFileDescriptor(storagePath, aDSFileDescriptor, aRequest);
+  }
+
+  nsRefPtr<DOMRequest> request = new DOMRequest(win);
+
+  nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
+                                                          mStorageName,
+                                                          aPath);
+  if (!dsf->IsSafePath()) {
+    r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED);
+  } else if (!typeChecker->Check(mStorageType, dsf->mFile)) {
+    r = new PostErrorEvent(request, POST_ERROR_EVENT_ILLEGAL_TYPE);
+  } else {
+    r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_CREATEFD,
+                                 win, mPrincipal, dsf, request,
+                                 aDSFileDescriptor);
+  }
+
+  rv = NS_DispatchToCurrentThread(r);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  request.forget(aRequest);
+  return NS_OK;
 }
 
 bool
 nsDOMDeviceStorage::Default()
 {
   nsString defaultStorageName;
   GetDefaultStorageName(mStorageType, defaultStorageName);
   return mStorageName.Equals(defaultStorageName);
@@ -3389,17 +3639,17 @@ nsDOMDeviceStorage::Observe(nsISupports 
     if (!vol) {
       return NS_OK;
     }
     nsString volName;
     vol->GetName(volName);
 
     DeviceStorageUsedSpaceCache* usedSpaceCache
       = DeviceStorageUsedSpaceCache::CreateOrGet();
-    NS_ASSERTION(usedSpaceCache, "DeviceStorageUsedSpaceCache is null");
+    MOZ_ASSERT(usedSpaceCache);
     usedSpaceCache->Invalidate(volName);
 
     if (!volName.Equals(mStorageName)) {
       // Not our volume - we can ignore.
       return NS_OK;
     }
 
     DeviceStorageFile dsf(mStorageType, mStorageName);
@@ -3448,52 +3698,63 @@ nsDOMDeviceStorage::Notify(const char* a
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
                                      nsIDOMEventListener *aListener,
                                      bool aUseCapture,
                                      bool aWantsUntrusted,
                                      uint8_t aArgc)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     return NS_ERROR_UNEXPECTED;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
                                win, mPrincipal, dsf, request, this);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   return nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
                                                   aWantsUntrusted, aArgc);
 }
 
 void
 nsDOMDeviceStorage::AddEventListener(const nsAString & aType,
                                      EventListener *aListener,
                                      bool aUseCapture,
                                      const Nullable<bool>& aWantsUntrusted,
                                      ErrorResult& aRv)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   nsCOMPtr<nsPIDOMWindow> win = GetOwner();
   if (!win) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   nsRefPtr<DOMRequest> request = new DOMRequest(win);
   nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(mStorageType,
                                                           mStorageName);
   nsCOMPtr<nsIRunnable> r
     = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH,
                                win, mPrincipal, dsf, request, this);
-  NS_DispatchToMainThread(r);
+  nsresult rv = NS_DispatchToCurrentThread(r);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
   nsDOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture,
                                            aWantsUntrusted, aRv);
 }
 
 NS_IMETHODIMP
 nsDOMDeviceStorage::AddSystemEventListener(const nsAString & aType,
                                            nsIDOMEventListener *aListener,
                                            bool aUseCapture,
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -47,17 +47,18 @@ enum DeviceStorageRequestType {
     DEVICE_STORAGE_REQUEST_READ,
     DEVICE_STORAGE_REQUEST_WRITE,
     DEVICE_STORAGE_REQUEST_CREATE,
     DEVICE_STORAGE_REQUEST_DELETE,
     DEVICE_STORAGE_REQUEST_WATCH,
     DEVICE_STORAGE_REQUEST_FREE_SPACE,
     DEVICE_STORAGE_REQUEST_USED_SPACE,
     DEVICE_STORAGE_REQUEST_AVAILABLE,
-    DEVICE_STORAGE_REQUEST_FORMAT
+    DEVICE_STORAGE_REQUEST_FORMAT,
+    DEVICE_STORAGE_REQUEST_CREATEFD
 };
 
 class DeviceStorageUsedSpaceCache MOZ_FINAL
 {
 public:
   static DeviceStorageUsedSpaceCache* CreateOrGet();
 
   DeviceStorageUsedSpaceCache();
@@ -85,27 +86,27 @@ public:
       }
     private:
       DeviceStorageUsedSpaceCache* mCache;
       nsString mStorageName;
   };
 
   void Invalidate(const nsAString& aStorageName)
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    NS_ASSERTION(mIOThread, "Null mIOThread!");
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mIOThread);
 
     nsRefPtr<InvalidateRunnable> r = new InvalidateRunnable(this, aStorageName);
     mIOThread->Dispatch(r, NS_DISPATCH_NORMAL);
   }
 
   void Dispatch(nsIRunnable* aRunnable)
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    NS_ASSERTION(mIOThread, "Null mIOThread!");
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mIOThread);
 
     mIOThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
   }
 
   nsresult AccumUsedSizes(const nsAString& aStorageName,
                           uint64_t* aPictureSize, uint64_t* aVideosSize,
                           uint64_t* aMusicSize, uint64_t* aTotalSize);
 
--- a/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
+++ b/dom/interfaces/devicestorage/nsIDOMDeviceStorage.idl
@@ -6,17 +6,22 @@
 #include "nsIDOMEventTarget.idl"
 interface nsIDOMBlob;
 interface nsIDOMDOMRequest;
 interface nsIDOMDOMCursor;
 interface nsIDOMDeviceStorageChangeEvent;
 interface nsIDOMEventListener;
 interface nsIFile;
 
-[scriptable, uuid(7c1b2305-0f14-4c07-8a8a-359eeb850068), builtinclass]
+%{C++
+class DeviceStorageFileDescriptor;
+%}
+[ptr] native DeviceStorageFdPtr(DeviceStorageFileDescriptor);
+
+[scriptable, uuid(8b724547-3c78-4244-969a-f00a1f4ae0c3), builtinclass]
 interface nsIDOMDeviceStorage : nsIDOMEventTarget
 {
     [implicit_jscontext] attribute jsval onchange;
     nsIDOMDOMRequest add(in nsIDOMBlob aBlob);
     nsIDOMDOMRequest addNamed(in nsIDOMBlob aBlob, in DOMString aName);
 
     nsIDOMDOMRequest get([Null(Stringify)] in DOMString aName);
     nsIDOMDOMRequest getEditable([Null(Stringify)] in DOMString aName);
@@ -29,10 +34,13 @@ interface nsIDOMDeviceStorage : nsIDOMEv
     // Note that the storageName is just a name (like sdcard), and doesn't
     // include any path information.
     readonly attribute DOMString storageName;
 
     // Determines if this storage area is the one which will be used by default
     // for storing new files.
     readonly attribute bool default;
 
-    [noscript] nsIFile getRootDirectoryForFile(in DOMString aName);
+    // Note: aFileDescriptor is reference counted, which is why we're using
+    //       a pointer rather than a reference.
+    [noscript] nsIDOMDOMRequest createFileDescriptor(in DOMString aName,
+                                                     in DeviceStorageFdPtr aFileDescriptor);
 };
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -89,16 +89,23 @@ struct DeviceStorageFormatParams
 struct DeviceStorageAddParams
 {
   nsString type;
   nsString storageName;
   nsString relpath;
   PBlob blob;
 };
 
+struct DeviceStorageCreateFdParams
+{
+  nsString type;
+  nsString storageName;
+  nsString relpath;
+};
+
 struct DeviceStorageGetParams
 {
   nsString type;
   nsString storageName;
   nsString rootDir;
   nsString relpath;
 };
 
@@ -115,16 +122,17 @@ struct DeviceStorageEnumerationParams
   nsString storageName;
   nsString rootdir;
   uint64_t since;
 };
 
 union DeviceStorageParams
 {
   DeviceStorageAddParams;
+  DeviceStorageCreateFdParams;
   DeviceStorageGetParams;
   DeviceStorageDeleteParams;
   DeviceStorageEnumerationParams;
   DeviceStorageFreeSpaceParams;
   DeviceStorageUsedSpaceParams;
   DeviceStorageAvailableParams;
   DeviceStorageFormatParams;
 };
--- a/mobile/android/base/FormAssistPopup.java
+++ b/mobile/android/base/FormAssistPopup.java
@@ -46,17 +46,22 @@ public class FormAssistPopup extends Rel
     private TextView mValidationMessageText;
     private ImageView mValidationMessageArrow;
     private ImageView mValidationMessageArrowInverted;
 
     private double mX;
     private double mY;
     private double mW;
     private double mH;
-    private boolean mIsAutoComplete = true;
+
+    private enum PopupType {
+        AUTOCOMPLETE,
+        VALIDATIONMESSAGE;
+    }
+    private PopupType mPopupType;
 
     private static int sAutoCompleteMinWidth = 0;
     private static int sAutoCompleteRowHeight = 0;
     private static int sValidationMessageHeight = 0;
     private static int sValidationTextMarginTop = 0;
     private static RelativeLayout.LayoutParams sValidationTextLayoutNormal;
     private static RelativeLayout.LayoutParams sValidationTextLayoutInverted;
 
@@ -203,17 +208,18 @@ public class FormAssistPopup extends Rel
             mW = rect.getDouble("w");
             mH = rect.getDouble("h");
         } catch (JSONException e) {
             // Bail if we can't get the correct dimensions for the popup.
             Log.e(LOGTAG, "Error getting FormAssistPopup dimensions", e);
             return false;
         }
 
-        mIsAutoComplete = isAutoComplete;
+        mPopupType = (isAutoComplete ?
+                      PopupType.AUTOCOMPLETE : PopupType.VALIDATIONMESSAGE);
         return true;
     }
 
     private void positionAndShowPopup() {
         positionAndShowPopup(GeckoAppShell.getLayerView().getViewportMetrics());
     }
 
     private void positionAndShowPopup(ImmutableViewportMetrics aMetrics) {
@@ -222,19 +228,19 @@ public class FormAssistPopup extends Rel
         // Don't show the form assist popup when using fullscreen VKB
         InputMethodManager imm =
                 (InputMethodManager) GeckoAppShell.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
         if (imm.isFullscreenMode())
             return;
 
         // Hide/show the appropriate popup contents
         if (mAutoCompleteList != null)
-            mAutoCompleteList.setVisibility(mIsAutoComplete ? VISIBLE : GONE);
+            mAutoCompleteList.setVisibility((mPopupType == PopupType.AUTOCOMPLETE) ? VISIBLE : GONE);
         if (mValidationMessage != null)
-            mValidationMessage.setVisibility(mIsAutoComplete ? GONE : VISIBLE);
+            mValidationMessage.setVisibility((mPopupType == PopupType.AUTOCOMPLETE) ? GONE : VISIBLE);
 
         if (sAutoCompleteMinWidth == 0) {
             Resources res = mContext.getResources();
             sAutoCompleteMinWidth = (int) (res.getDimension(R.dimen.autocomplete_min_width));
             sAutoCompleteRowHeight = (int) (res.getDimension(R.dimen.autocomplete_row_height));
             sValidationMessageHeight = (int) (res.getDimension(R.dimen.validation_message_height));
         }
 
@@ -250,38 +256,38 @@ public class FormAssistPopup extends Rel
 
         int popupWidth = RelativeLayout.LayoutParams.FILL_PARENT;
         int popupLeft = left < 0 ? 0 : left;
 
         FloatSize viewport = aMetrics.getSize();
 
         // For autocomplete suggestions, if the input is smaller than the screen-width,
         // shrink the popup's width. Otherwise, keep it as FILL_PARENT.
-        if (mIsAutoComplete && (left + width) < viewport.width) {
+        if ((mPopupType == PopupType.AUTOCOMPLETE) && (left + width) < viewport.width) {
             popupWidth = left < 0 ? left + width : width;
 
             // Ensure the popup has a minimum width.
             if (popupWidth < sAutoCompleteMinWidth) {
                 popupWidth = sAutoCompleteMinWidth;
 
                 // Move the popup to the left if there isn't enough room for it.
                 if ((popupLeft + popupWidth) > viewport.width)
                     popupLeft = (int) (viewport.width - popupWidth);
             }
         }
 
         int popupHeight;
-        if (mIsAutoComplete)
+        if (mPopupType == PopupType.AUTOCOMPLETE)
             popupHeight = sAutoCompleteRowHeight * mAutoCompleteList.getAdapter().getCount();
         else
             popupHeight = sValidationMessageHeight;
 
         int popupTop = top + height;
 
-        if (!mIsAutoComplete) {
+        if (mPopupType == PopupType.VALIDATIONMESSAGE) {
             mValidationMessageText.setLayoutParams(sValidationTextLayoutNormal);
             mValidationMessageArrow.setVisibility(VISIBLE);
             mValidationMessageArrowInverted.setVisibility(GONE);
         }
 
         // If the popup doesn't fit below the input box, shrink its height, or
         // see if we can place it above the input instead.
         if ((popupTop + popupHeight) > viewport.height) {
@@ -294,17 +300,17 @@ public class FormAssistPopup extends Rel
                     // No shrinking needed to fit on top.
                     popupTop = (top - popupHeight);
                 } else {
                     // Shrink to available space on top.
                     popupTop = 0;
                     popupHeight = top;
                 }
 
-                if (!mIsAutoComplete) {
+                if (mPopupType == PopupType.VALIDATIONMESSAGE) {
                     mValidationMessageText.setLayoutParams(sValidationTextLayoutInverted);
                     mValidationMessageArrow.setVisibility(GONE);
                     mValidationMessageArrowInverted.setVisibility(VISIBLE);
                 }
            }
         }
 
         RelativeLayout.LayoutParams layoutParams =
--- a/security/sandbox/linux/seccomp_filter.h
+++ b/security/sandbox/linux/seccomp_filter.h
@@ -153,17 +153,16 @@
   ALLOW_SYSCALL(lstat), \
   ALLOW_SYSCALL(mmap), \
   ALLOW_SYSCALL(openat), \
   ALLOW_SYSCALL(fcntl), \
   ALLOW_SYSCALL(fstat), \
   ALLOW_SYSCALL(readlink), \
   ALLOW_SYSCALL(getsockname), \
   ALLOW_SYSCALL(recvmsg), \
-  ALLOW_SYSCALL(uname), \
   /* duplicate rt_sigaction in SECCOMP_WHITELIST_PROFILING */ \
   ALLOW_SYSCALL(rt_sigaction), \
   ALLOW_SYSCALL(getuid), \
   ALLOW_SYSCALL(geteuid), \
   ALLOW_SYSCALL(mkdir), \
   ALLOW_SYSCALL(getcwd), \
   ALLOW_SYSCALL(readahead), \
   ALLOW_SYSCALL(pread64), \
@@ -263,11 +262,14 @@
   SECCOMP_WHITELIST_B2G_LOW \
   /* Always last and always OK calls */ \
   SECCOMP_WHITELIST_ARCH_LAST \
   /* restart_syscall is called internally, generally when debugging */ \
   ALLOW_SYSCALL(restart_syscall), \
   /* linux desktop is not as performance critical as B2G */ \
   /* we can place desktop syscalls at the end */ \
   SECCOMP_WHITELIST_DESKTOP_LINUX \
+  /* nsSystemInfo uses uname (and we cache an instance, so */ \
+  /* the info remains present even if we block the syscall) */ \
+  ALLOW_SYSCALL(uname), \
   ALLOW_SYSCALL(exit_group), \
   ALLOW_SYSCALL(exit)