Merge b2g-inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 18 Aug 2015 10:41:52 -0400
changeset 258212 d5cf4e7900df6b2351bf3677b49fb70bedf68b99
parent 258185 bf540756452992dad2430e684194b7f922f19204 (current diff)
parent 258211 3ef1a1856a3f3636d280db9dd09912aab5d4332c (diff)
child 258213 d55e24c983aa92e66e3a688ccc476650bb3ed94e
child 258226 2c272af993c23e803f6ea7798a812b0c8abfad4d
child 258282 bf52c68b200138b712009e72c8bef2d7d3906f73
child 258366 89d1e2c56130ae10cea48c16a78f79b6857848e2
push id63843
push userryanvm@gmail.com
push dateTue, 18 Aug 2015 14:58:06 +0000
treeherdermozilla-inbound@d55e24c983aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.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 b2g-inbound to m-c. a=merge
dom/base/test/mochitest.ini
widget/gonk/nsClipboard.cpp
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -7,20 +7,20 @@
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
-  <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
+  <project name="platform_build" path="build" remote="b2g" revision="aacd9b12da7fc3c8f4deaaa8eedfbb158f4fefe7">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -7,20 +7,20 @@
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
-  <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
+  <project name="platform_build" path="build" remote="b2g" revision="aacd9b12da7fc3c8f4deaaa8eedfbb158f4fefe7">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <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="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <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="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- 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
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- 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-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -7,20 +7,20 @@
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
-  <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
+  <project name="platform_build" path="build" remote="b2g" revision="aacd9b12da7fc3c8f4deaaa8eedfbb158f4fefe7">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>
@@ -123,18 +123,18 @@
   <project name="platform/system/security" path="system/security" revision="583374f69f531ba68fc3dcbff1f74893d2a96406"/>
   <project name="platform/system/vold" path="system/vold" revision="d4455b8cf361f8353e8aebac15ffd64b4aedd2b9"/>
   <project name="platform/external/icu4c" path="external/icu4c" remote="aosp" revision="b4c6379528887dc25ca9991a535a8d92a61ad6b6"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="f313503b5c91aaa6fcf962d4ec9bf260e0c00bf1"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="8586f55fe4b015911b48e731b69c592ad82a0807"/>
   <default remote="caf" revision="refs/tags/android-4.4.2_r1" sync-j="4"/>
   <!-- Emulator specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="72ffdf71c68a96309212eb13d63560d66db14c9e"/>
-  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="85f6a2e1e638dbc8b119896e61383e973e764ffd"/>
-  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="4bebbe8d92368befc31e8b4a99da2d29cc26bfbc"/>
+  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="dea95f1a539f037db1b00732048a8722532b4d47"/>
+  <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="ef1a0385c3286dc2403c72fdc8092258c20f70d4"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="f37bd545063039e30a92f2550ae78c0e6e4e2d08"/>
   <project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="0c6a6547cd1fd302fa2b0f6e375654df36bf0ec4"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd4fc430da93fad3123f0775791a919568aa0ca2"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="5f4b68c799927b6e078f987b12722c3a6ccd4a45"/>
   <project name="platform/development" path="development" revision="5968ff4e13e0d696ad8d972281fc27ae5a12829b"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="0951179277915335251c5e11d242e4e1a8c2236f"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <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="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <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="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="2d58f4b9206b50b8fda0d5036da6f0c62608db7c"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d70e4bfdcb65e7514de0f9315b74aea1c811678d"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -7,20 +7,20 @@
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
-  <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
+  <project name="platform_build" path="build" remote="b2g" revision="aacd9b12da7fc3c8f4deaaa8eedfbb158f4fefe7">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "d9d99f32762975a370f1abd34a3512bd6fe29111", 
+        "git_revision": "507ba38fb64b27f87d11f4104dfcc58448e12b1a", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "a1b18f9a9e869b1ae261a064a7dd174d59169fb9", 
+    "revision": "8ca4fec22230c6b6d62301261a6fc76a6fa64814", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- 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/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="05a36844c1046a1eb07d5b1325f85ed741f961ea">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="d9d99f32762975a370f1abd34a3512bd6fe29111"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="507ba38fb64b27f87d11f4104dfcc58448e12b1a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="bfdb6348725a33bdcdc4e17999cb500be6beedb5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a6f9a1245d98c51172c15afecb9ade1a6ca511e2"/>
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -201,17 +201,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBluetooth)
 #endif
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
 #endif
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCameraManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagesManager)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageStores)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedResolveResults)
 #ifdef MOZ_EME
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
 #endif
@@ -323,17 +322,20 @@ Navigator::Invalidate()
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   if (mAudioChannelManager) {
     mAudioChannelManager = nullptr;
   }
 #endif
 
   uint32_t len = mDeviceStorageStores.Length();
   for (uint32_t i = 0; i < len; ++i) {
-    mDeviceStorageStores[i]->Shutdown();
+    nsRefPtr<nsDOMDeviceStorage> ds = do_QueryReferent(mDeviceStorageStores[i]);
+    if (ds) {
+      ds->Shutdown();
+    }
   }
   mDeviceStorageStores.Clear();
 
   if (mTimeManager) {
     mTimeManager = nullptr;
   }
 
   if (mPresentation) {
@@ -956,71 +958,120 @@ Navigator::GetDeviceStorageAreaListener(
       return nullptr;
     }
     mDeviceStorageAreaListener = new DeviceStorageAreaListener(mWindow);
   }
 
   return mDeviceStorageAreaListener;
 }
 
-nsDOMDeviceStorage*
+already_AddRefed<nsDOMDeviceStorage>
+Navigator::FindDeviceStorage(const nsAString& aName, const nsAString& aType)
+{
+  auto i = mDeviceStorageStores.Length();
+  while (i > 0) {
+    --i;
+    nsRefPtr<nsDOMDeviceStorage> storage =
+      do_QueryReferent(mDeviceStorageStores[i]);
+    if (storage) {
+      if (storage->Equals(mWindow, aName, aType)) {
+        return storage.forget();
+      }
+    } else {
+      mDeviceStorageStores.RemoveElementAt(i);
+    }
+  }
+  return nullptr;
+}
+
+already_AddRefed<nsDOMDeviceStorage>
 Navigator::GetDeviceStorage(const nsAString& aType, ErrorResult& aRv)
 {
   if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsRefPtr<nsDOMDeviceStorage> storage;
+  nsString name;
+  nsDOMDeviceStorage::GetDefaultStorageName(aType, name);
+  nsRefPtr<nsDOMDeviceStorage> storage = FindDeviceStorage(name, aType);
+  if (storage) {
+    return storage.forget();
+  }
+
   nsDOMDeviceStorage::CreateDeviceStorageFor(mWindow, aType,
                                              getter_AddRefs(storage));
 
   if (!storage) {
     return nullptr;
   }
 
-  mDeviceStorageStores.AppendElement(storage);
-  return storage;
+  mDeviceStorageStores.AppendElement(
+    do_GetWeakReference(static_cast<DOMEventTargetHelper*>(storage)));
+  return storage.forget();
 }
 
 void
 Navigator::GetDeviceStorages(const nsAString& aType,
                              nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores,
                              ErrorResult& aRv)
 {
   if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  nsDOMDeviceStorage::CreateDeviceStoragesFor(mWindow, aType, aStores);
-
-  mDeviceStorageStores.AppendElements(aStores);
+  nsDOMDeviceStorage::VolumeNameArray volumes;
+  nsDOMDeviceStorage::GetOrderedVolumeNames(aType, volumes);
+  if (volumes.IsEmpty()) {
+    nsRefPtr<nsDOMDeviceStorage> storage = GetDeviceStorage(aType, aRv);
+    if (storage) {
+      aStores.AppendElement(storage.forget());
+    }
+  } else {
+    uint32_t len = volumes.Length();
+    aStores.SetCapacity(len);
+    for (uint32_t i = 0; i < len; ++i) {
+      nsRefPtr<nsDOMDeviceStorage> storage =
+        GetDeviceStorageByNameAndType(volumes[i], aType, aRv);
+      if (aRv.Failed()) {
+        break;
+      }
+
+      if (storage) {
+        aStores.AppendElement(storage.forget());
+      }
+    }
+  }
 }
 
-nsDOMDeviceStorage*
+already_AddRefed<nsDOMDeviceStorage>
 Navigator::GetDeviceStorageByNameAndType(const nsAString& aName,
                                          const nsAString& aType,
                                          ErrorResult& aRv)
 {
   if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  nsRefPtr<nsDOMDeviceStorage> storage;
+  nsRefPtr<nsDOMDeviceStorage> storage = FindDeviceStorage(aName, aType);
+  if (storage) {
+    return storage.forget();
+  }
   nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(mWindow, aName, aType,
                                                        getter_AddRefs(storage));
 
   if (!storage) {
     return nullptr;
   }
 
-  mDeviceStorageStores.AppendElement(storage);
-  return storage;
+  mDeviceStorageStores.AppendElement(
+    do_GetWeakReference(static_cast<DOMEventTargetHelper*>(storage)));
+  return storage.forget();
 }
 
 Geolocation*
 Navigator::GetGeolocation(ErrorResult& aRv)
 {
   if (mGeolocation) {
     return mGeolocation;
   }
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -13,16 +13,17 @@
 #include "nsIDOMNavigator.h"
 #include "nsIMozNavigatorNetwork.h"
 #include "nsAutoPtr.h"
 #include "nsWrapperCache.h"
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
+#include "nsWeakPtr.h"
 #ifdef MOZ_EME
 #include "mozilla/dom/MediaKeySystemAccessManager.h"
 #endif
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsPIDOMWindow;
 class nsIDOMNavigatorSystemMessages;
@@ -215,24 +216,28 @@ public:
   {
     return false;
   }
   void AddIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
   void RemoveIdleObserver(MozIdleObserver& aObserver, ErrorResult& aRv);
   already_AddRefed<WakeLock> RequestWakeLock(const nsAString &aTopic,
                                              ErrorResult& aRv);
   DeviceStorageAreaListener* GetDeviceStorageAreaListener(ErrorResult& aRv);
-  nsDOMDeviceStorage* GetDeviceStorage(const nsAString& aType,
-                                       ErrorResult& aRv);
+
+  already_AddRefed<nsDOMDeviceStorage> GetDeviceStorage(const nsAString& aType,
+                                                        ErrorResult& aRv);
+
   void GetDeviceStorages(const nsAString& aType,
                          nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores,
                          ErrorResult& aRv);
-  nsDOMDeviceStorage* GetDeviceStorageByNameAndType(const nsAString& aName,
-                                                    const nsAString& aType,
-                                                    ErrorResult& aRv);
+
+  already_AddRefed<nsDOMDeviceStorage>
+  GetDeviceStorageByNameAndType(const nsAString& aName, const nsAString& aType,
+                                ErrorResult& aRv);
+
   DesktopNotificationCenter* GetMozNotification(ErrorResult& aRv);
   CellBroadcast* GetMozCellBroadcast(ErrorResult& aRv);
   IccManager* GetMozIccManager(ErrorResult& aRv);
   MobileMessageManager* GetMozMobileMessage();
   Telephony* GetMozTelephony(ErrorResult& aRv);
   Voicemail* GetMozVoicemail(ErrorResult& aRv);
   TVManager* GetTv();
   InputPortManager* GetInputPortManager(ErrorResult& aRv);
@@ -352,16 +357,19 @@ private:
 #endif
 
 private:
   virtual ~Navigator();
 
   bool CheckPermission(const char* type);
   static bool CheckPermission(nsPIDOMWindow* aWindow, const char* aType);
 
+  already_AddRefed<nsDOMDeviceStorage> FindDeviceStorage(const nsAString& aName,
+                                                         const nsAString& aType);
+
   nsRefPtr<nsMimeTypeArray> mMimeTypes;
   nsRefPtr<nsPluginArray> mPlugins;
   nsRefPtr<Permissions> mPermissions;
   nsRefPtr<Geolocation> mGeolocation;
   nsRefPtr<DesktopNotificationCenter> mNotification;
   nsRefPtr<battery::BatteryManager> mBatteryManager;
 #ifdef MOZ_B2G_FM
   nsRefPtr<FMRadio> mFMRadio;
@@ -382,17 +390,17 @@ private:
   nsRefPtr<bluetooth::BluetoothManager> mBluetooth;
 #endif
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   nsRefPtr<system::AudioChannelManager> mAudioChannelManager;
 #endif
   nsRefPtr<nsDOMCameraManager> mCameraManager;
   nsRefPtr<MediaDevices> mMediaDevices;
   nsCOMPtr<nsIDOMNavigatorSystemMessages> mMessagesManager;
-  nsTArray<nsRefPtr<nsDOMDeviceStorage> > mDeviceStorageStores;
+  nsTArray<nsWeakPtr> mDeviceStorageStores;
   nsRefPtr<time::TimeManager> mTimeManager;
   nsRefPtr<ServiceWorkerContainer> mServiceWorkerContainer;
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsRefPtr<DeviceStorageAreaListener> mDeviceStorageAreaListener;
   nsRefPtr<Presentation> mPresentation;
 
   // Hashtable for saving cached objects DoResolve created, so we don't create
   // the object twice if asked for it twice, whether due to use of "delete" or
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -1,29 +1,28 @@
 /* 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 "CameraControlImpl.h"
 #include "base/basictypes.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/unused.h"
-#include "nsPrintfCString.h"
 #include "nsIWeakReferenceUtils.h"
 #include "CameraCommon.h"
 #include "nsGlobalWindow.h"
 #include "DeviceStorageFileDescriptor.h"
 #include "CameraControlListener.h"
 
 using namespace mozilla;
 
 /* static */ StaticRefPtr<nsIThread> CameraControlImpl::sCameraThread;
 
 CameraControlImpl::CameraControlImpl()
-  : mListenerLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "CameraControlImpl.Listeners.Lock"))
+  : mListenerLock("mozilla::camera::CameraControlImpl.Listeners")
   , mPreviewState(CameraControlListener::kPreviewStopped)
   , mHardwareState(CameraControlListener::kHardwareUninitialized)
   , mHardwareStateChangeReason(NS_OK)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   mCurrentConfiguration.mMode = ICameraControl::kUnspecifiedMode;
 
   class Delegate : public nsRunnable
@@ -45,52 +44,31 @@ CameraControlImpl::CameraControlImpl()
   } else {
     nsresult rv = NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread));
     if (NS_FAILED(rv)) {
       MOZ_CRASH("Failed to create new Camera Thread");
     }
     mCameraThread->Dispatch(new Delegate(), NS_DISPATCH_NORMAL);
     sCameraThread = mCameraThread;
   }
-
-  // Care must be taken with the mListenerLock read-write lock to prevent
-  // deadlocks. Currently this is handled by ensuring that any attempts to
-  // acquire the lock for writing (as in Add/RemoveListener()) happen in a
-  // runnable dispatched to the Camera Thread--even if the method is being
-  // called from that thread. This ensures that if a registered listener
-  // (which is invoked with a read-lock) tries to call Add/RemoveListener(),
-  // the lock-for-writing attempt won't happen until the listener has
-  // completed.
-  //
-  // Multiple parallel listeners being invoked are not a problem because
-  // the read-write lock allows multiple simultaneous read-locks.
-  if (!mListenerLock) {
-    MOZ_CRASH("Out of memory getting new PRRWLock");
-  }
 }
 
 CameraControlImpl::~CameraControlImpl()
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
-
-  MOZ_ASSERT(mListenerLock, "mListenerLock missing in ~CameraControlImpl()");
-  if (mListenerLock) {
-    PR_DestroyRWLock(mListenerLock);
-    mListenerLock = nullptr;
-  }
 }
 
 void
 CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aNewState,
                                          nsresult aReason)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread. On Gonk, it may be called from the camera's
   //  local binder thread, should the mediaserver process die.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   if (aNewState == mHardwareState) {
     DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState);
     return;
   }
 
   const char* state[] = { "uninitialized", "closed", "open", "failed" };
   MOZ_ASSERT(aNewState >= 0);
@@ -109,128 +87,128 @@ CameraControlImpl::OnHardwareStateChange
     l->OnHardwareStateChange(mHardwareState, mHardwareStateChangeReason);
   }
 }
 
 void
 CameraControlImpl::OnConfigurationChange()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   DOM_CAMERA_LOGI("OnConfigurationChange : %zu listeners\n", mListeners.Length());
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnConfigurationChange(mCurrentConfiguration);
   }
 }
 
 void
 CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread. On Gonk, it is called from the camera
   //  library's auto focus thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnAutoFocusComplete(aAutoFocusSucceeded);
   }
 }
 
 void
 CameraControlImpl::OnAutoFocusMoving(bool aIsMoving)
 {
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnAutoFocusMoving(aIsMoving);
   }
 }
 
 void
 CameraControlImpl::OnFacesDetected(const nsTArray<Face>& aFaces)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread. On Gonk, it is called from the camera
   //  library's face detection thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnFacesDetected(aFaces);
   }
 }
 
 void
 CameraControlImpl::OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread. On Gonk, it is called from the camera
   //  library's snapshot thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnTakePictureComplete(aData, aLength, aMimeType);
   }
 }
 
 void
 CameraControlImpl::OnPoster(dom::BlobImpl* aBlobImpl)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnPoster(aBlobImpl);
   }
 }
 
 void
 CameraControlImpl::OnShutter()
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread. On Gonk, it is called from the camera driver's
   //  preview thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnShutter();
   }
 }
 
 void
 CameraControlImpl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
                                          int32_t aStatus, int32_t aTrackNumber)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread. On Gonk, it is called from the media encoder
   //  thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnRecorderStateChange(aState, aStatus, aTrackNumber);
   }
 }
 
 void
 CameraControlImpl::OnPreviewStateChange(CameraControlListener::PreviewState aNewState)
 {
   // This callback runs on the Main Thread and the Camera Thread, and
   //  may run on the local binder thread, should the mediaserver
   //  process die.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   if (aNewState == mPreviewState) {
     DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState);
     return;
   }
 
   const char* state[] = { "stopped", "paused", "started" };
   MOZ_ASSERT(aNewState >= 0);
@@ -247,32 +225,32 @@ CameraControlImpl::OnPreviewStateChange(
     l->OnPreviewStateChange(mPreviewState);
   }
 }
 
 void
 CameraControlImpl::OnRateLimitPreview(bool aLimit)
 {
   // This function runs on neither the Main Thread nor the Camera Thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   DOM_CAMERA_LOGI("OnRateLimitPreview: %d\n", aLimit);
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
     l->OnRateLimitPreview(aLimit);
   }
 }
 
 bool
 CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight)
 {
   // This function runs on neither the Main Thread nor the Camera Thread.
   //  On Gonk, it is called from the camera driver's preview thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %zu preview frame listener(s)\n",
     mListeners.Length());
 
   bool consumed = false;
 
   for (uint32_t i = 0; i < mListeners.Length(); ++i) {
     CameraControlListener* l = mListeners[i];
@@ -282,17 +260,17 @@ CameraControlImpl::OnNewPreviewFrame(lay
 }
 
 void
 CameraControlImpl::OnUserError(CameraControlListener::UserContext aContext,
                                nsresult aError)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   const char* context[] = {
     "StartCamera",
     "StopCamera",
     "AutoFocus",
     "StartFaceDetection",
     "StopFaceDetection",
     "TakePicture",
@@ -323,17 +301,17 @@ CameraControlImpl::OnUserError(CameraCon
 }
 
 void
 CameraControlImpl::OnSystemError(CameraControlListener::SystemContext aContext,
                                  nsresult aError)
 {
   // This callback can run on threads other than the Main Thread and
   //  the Camera Thread.
-  RwLockAutoEnterRead lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   const char* context[] = {
     "Camera Service"
   };
   if (static_cast<size_t>(aContext) < sizeof(context) / sizeof(context[0])) {
     DOM_CAMERA_LOGW("CameraControlImpl::OnSystemError : aContext='%s' (%d), aError=0x%x\n",
       context[aContext], aContext, aError);
   } else {
@@ -741,17 +719,17 @@ public:
 
 protected:
   nsRefPtr<CameraControlListener> mListener;
 };
 
 void
 CameraControlImpl::AddListenerImpl(already_AddRefed<CameraControlListener> aListener)
 {
-  RwLockAutoEnterWrite lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   CameraControlListener* l = *mListeners.AppendElement() = aListener;
   DOM_CAMERA_LOGI("Added camera control listener %p\n", l);
 
   // Update the newly-added listener's state
   l->OnConfigurationChange(mCurrentConfiguration);
   l->OnHardwareStateChange(mHardwareState, mHardwareStateChangeReason);
   l->OnPreviewStateChange(mPreviewState);
@@ -779,17 +757,17 @@ CameraControlImpl::AddListener(CameraCon
   if (aListener) {
     Dispatch(new Message(this, aListener));
   }
 }
 
 void
 CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener)
 {
-  RwLockAutoEnterWrite lock(mListenerLock);
+  MutexAutoLock lock(mListenerLock);
 
   nsRefPtr<CameraControlListener> l(aListener);
   mListeners.RemoveElement(l);
   DOM_CAMERA_LOGI("Removed camera control listener %p\n", l.get());
   // XXXmikeh - do we want to notify the listener that it has been removed?
 }
 
 void
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -4,19 +4,19 @@
 
 #ifndef DOM_CAMERA_CAMERACONTROLIMPL_H
 #define DOM_CAMERA_CAMERACONTROLIMPL_H
 
 #include "nsTArray.h"
 #include "nsWeakPtr.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ReentrantMonitor.h"
+#include "mozilla/Mutex.h"
 #include "nsIFile.h"
 #include "nsProxyRelease.h"
-#include "AutoRwLock.h"
 #include "ICameraControl.h"
 #include "CameraCommon.h"
 #include "DeviceStorage.h"
 #include "DeviceStorageFileDescriptor.h"
 #include "CameraControlListener.h"
 
 namespace mozilla {
 
@@ -86,17 +86,17 @@ protected:
 
   virtual void BeginBatchParameterSet() override { }
   virtual void EndBatchParameterSet() override { }
 
   // Manage camera event listeners.
   void AddListenerImpl(already_AddRefed<CameraControlListener> aListener);
   void RemoveListenerImpl(CameraControlListener* aListener);
   nsTArray<nsRefPtr<CameraControlListener> > mListeners;
-  PRRWLock* mListenerLock;
+  mutable Mutex mListenerLock;
 
   class ControlMessage;
   class ListenerMessage;
 
   nsresult Dispatch(ControlMessage* aMessage);
 
   // Asynchronous method implementations, invoked on the Camera Thread.
   //
--- a/dom/devicestorage/DeviceStorage.h
+++ b/dom/devicestorage/DeviceStorage.h
@@ -4,32 +4,35 @@
  * 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 DeviceStorage_h
 #define DeviceStorage_h
 
 #include "nsIFile.h"
 #include "nsIPrincipal.h"
-#include "nsIObserver.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/DOMRequest.h"
+#include "nsWeakReference.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 nsIInputStream;
 class nsIOutputStream;
 struct DeviceStorageFileDescriptor;
+#ifdef MOZ_WIDGET_GONK
+class nsIVolume;
+#endif
 
 namespace mozilla {
 class EventListenerManager;
 namespace dom {
 class Blob;
 struct DeviceStorageEnumerationParameters;
 class DOMCursor;
 class DOMRequest;
@@ -125,59 +128,36 @@ private:
   void AppendRelativePath(const nsAString& aPath);
   void AccumDirectoryUsage(nsIFile* aFile,
                            uint64_t* aPicturesSoFar,
                            uint64_t* aVideosSoFar,
                            uint64_t* aMusicSoFar,
                            uint64_t* aTotalSoFar);
 };
 
-/*
-  The FileUpdateDispatcher converts file-watcher-notify
-  observer events to file-watcher-update events.  This is
-  used to be able to broadcast events from one child to
-  another child in B2G.  (f.e., if one child decides to add
-  a file, we want to be able to able to send a onchange
-  notifications to every other child watching that device
-  storage object).
-
-  We create this object (via GetSingleton) in two places:
-    * ContentParent::Init (for IPC)
-    * nsDOMDeviceStorage::Init (for non-ipc)
-*/
-class FileUpdateDispatcher final
-  : public nsIObserver
-{
-  ~FileUpdateDispatcher() {}
-
- public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-
-  static FileUpdateDispatcher* GetSingleton();
- private:
-  static mozilla::StaticRefPtr<FileUpdateDispatcher> sSingleton;
-};
+#define NS_DOM_DEVICE_STORAGE_CID \
+  { 0xe4a9b969, 0x81fe, 0x44f1, \
+    { 0xaa, 0x0c, 0x9e, 0x16, 0x64, 0x86, 0x2a, 0xd5 } }
 
 class nsDOMDeviceStorage final
   : public mozilla::DOMEventTargetHelper
-  , public nsIObserver
+  , public nsSupportsWeakReference
 {
   typedef mozilla::ErrorResult ErrorResult;
   typedef mozilla::dom::DeviceStorageEnumerationParameters
     EnumerationParameters;
   typedef mozilla::dom::DOMCursor DOMCursor;
   typedef mozilla::dom::DOMRequest DOMRequest;
   typedef mozilla::dom::Promise Promise;
   typedef mozilla::dom::DeviceStorageFileSystem DeviceStorageFileSystem;
 public:
   typedef nsTArray<nsString> VolumeNameArray;
 
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_DEVICE_STORAGE_CID)
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIOBSERVER
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
 
   void EventListenerWasAdded(const nsAString& aType,
                              ErrorResult& aRv,
                              JSCompartment* aCompartment) override;
 
   explicit nsDOMDeviceStorage(nsPIDOMWindow* aWindow);
 
@@ -274,36 +254,47 @@ public:
   GetRoot(ErrorResult& aRv);
 
   static void
   CreateDeviceStorageFor(nsPIDOMWindow* aWin,
                          const nsAString& aType,
                          nsDOMDeviceStorage** aStore);
 
   static void
-  CreateDeviceStoragesFor(nsPIDOMWindow* aWin,
-                          const nsAString& aType,
-                          nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores);
-
-  static void
   CreateDeviceStorageByNameAndType(nsPIDOMWindow* aWin,
                                    const nsAString& aName,
                                    const nsAString& aType,
                                    nsDOMDeviceStorage** aStore);
 
+  bool Equals(nsPIDOMWindow* aWin,
+              const nsAString& aName,
+              const nsAString& aType);
+
   void Shutdown();
 
+  static void GetOrderedVolumeNames(const nsAString& aType,
+                                    nsTArray<nsString>& aVolumeNames);
+
   static void GetOrderedVolumeNames(nsTArray<nsString>& aVolumeNames);
 
   static void GetDefaultStorageName(const nsAString& aStorageType,
-                                    nsAString &aStorageName);
+                                    nsAString& aStorageName);
 
   static bool ParseFullPath(const nsAString& aFullPath,
                             nsAString& aOutStorageName,
                             nsAString& aOutStoragePath);
+
+  // DeviceStorageStatics callbacks
+  void OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile);
+  void OnDiskSpaceWatcher(bool aLowDiskSpace);
+  void OnWritableNameChanged();
+#ifdef MOZ_WIDGET_GONK
+  void OnVolumeStateChanged(nsIVolume* aVolume);
+#endif
+
 private:
   ~nsDOMDeviceStorage();
 
   already_AddRefed<DOMRequest>
   GetInternal(const nsAString& aPath, bool aEditable, ErrorResult& aRv);
 
   void
   GetInternal(nsPIDOMWindow* aWin, const nsAString& aPath, DOMRequest* aRequest,
@@ -336,17 +327,16 @@ private:
                             const nsAString& aStorageName,
                             const nsAString& aType);
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   bool mIsWatchingFile;
   bool mAllowedToWatchFile;
   bool mIsDefaultLocation;
-  void DispatchDefaultChangeEvent();
 
   nsresult Notify(const char* aReason, class DeviceStorageFile* aFile);
 
   friend class WatchFileEvent;
   friend class DeviceStorageRequest;
 
   static mozilla::StaticAutoPtr<nsTArray<nsString>> sVolumeNameCache;
 
@@ -355,9 +345,11 @@ private:
   nsString mLastStorageStatus;
   void DispatchStatusChangeEvent(nsAString& aStatus);
   void DispatchStorageStatusChangeEvent(nsAString& aStorageStatus);
 #endif
 
   nsRefPtr<DeviceStorageFileSystem> mFileSystem;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMDeviceStorage, NS_DOM_DEVICE_STORAGE_CID)
+
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageStatics.cpp
@@ -0,0 +1,833 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DeviceStorageStatics.h"
+#include "mozilla/Preferences.h"
+#include "nsDeviceStorage.h"
+#include "nsIObserverService.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsISupportsPrimitives.h"
+#include "nsPrintfCString.h"
+
+#ifdef MOZ_WIDGET_GONK
+#include "nsIVolume.h"
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
+namespace mozilla {
+namespace dom {
+namespace devicestorage {
+
+static const char* kPrefOverrideRootDir = "device.storage.overrideRootDir";
+static const char* kPrefTesting = "device.storage.testing";
+static const char* kPrefPromptTesting = "device.storage.prompt.testing";
+static const char* kPrefWritableName = "device.storage.writable.name";
+
+static const char* kFileWatcherUpdate = "file-watcher-update";
+static const char* kDiskSpaceWatcher = "disk-space-watcher";
+static const char* kFileWatcherNotify = "file-watcher-notify";
+static const char* kDownloadWatcherNotify = "download-watcher-notify";
+
+StaticRefPtr<DeviceStorageStatics> DeviceStorageStatics::sInstance;
+StaticMutex DeviceStorageStatics::sMutex;
+
+NS_IMPL_ISUPPORTS(DeviceStorageStatics,
+                  nsIObserver)
+
+/* static */ void
+DeviceStorageStatics::Initialize()
+{
+  MOZ_ASSERT(!sInstance);
+  StaticMutexAutoLock lock(sMutex);
+  sInstance = new DeviceStorageStatics();
+  sInstance->Init();
+}
+
+/* static */ void
+DeviceStorageStatics::InitializeDirs()
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (NS_WARN_IF(!sInstance)) {
+    return;
+  }
+
+  /* The actual initialization can only happen on the main thread. This will
+     either happen when device storage is first used on the main thread, or
+     (in the future) when a worker is created. */
+  if (!sInstance->mInitialized && NS_IsMainThread()) {
+    sInstance->InitDirs();
+    sInstance->mInitialized = true;
+  }
+
+  MOZ_ASSERT(sInstance->mInitialized);
+}
+
+DeviceStorageStatics::DeviceStorageStatics()
+  : mInitialized(false)
+  , mPromptTesting(false)
+{
+  DS_LOG_INFO("");
+}
+
+DeviceStorageStatics::~DeviceStorageStatics()
+{
+  DS_LOG_INFO("");
+}
+
+void
+DeviceStorageStatics::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  sMutex.AssertCurrentThreadOwns();
+  DS_LOG_INFO("");
+
+  Preferences::AddStrongObserver(this, kPrefTesting);
+  Preferences::AddStrongObserver(this, kPrefPromptTesting);
+  Preferences::AddStrongObserver(this, kPrefWritableName);
+
+  mWritableName = Preferences::GetString(kPrefWritableName);
+  mPromptTesting = Preferences::GetBool(kPrefPromptTesting, false);
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+    obs->AddObserver(this, kFileWatcherNotify, false);
+    obs->AddObserver(this, kDownloadWatcherNotify, false);
+  }
+  DS_LOG_INFO("");
+}
+
+void
+DeviceStorageStatics::InitDirs()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  sMutex.AssertCurrentThreadOwns();
+  DS_LOG_INFO("");
+
+  nsCOMPtr<nsIProperties> dirService
+    = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+  MOZ_ASSERT(dirService);
+
+#if !defined(MOZ_WIDGET_GONK)
+
+// Keep MOZ_WIDGET_COCOA above XP_UNIX,
+// because both are defined in Darwin builds.
+#if defined (MOZ_WIDGET_COCOA)
+  dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_PICTURES]));
+  dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_VIDEOS]));
+  dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_MUSIC]));
+
+// Keep MOZ_WIDGET_ANDROID above XP_UNIX,
+// because both are defined in Android builds.
+#elif defined (MOZ_WIDGET_ANDROID)
+  nsAutoString path;
+  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
+      NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), path))) {
+    NS_NewLocalFile(path, /* aFollowLinks */ true,
+                    getter_AddRefs(mDirs[TYPE_PICTURES]));
+  }
+  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
+      NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS), path))) {
+    NS_NewLocalFile(path, /* aFollowLinks */ true,
+                    getter_AddRefs(mDirs[TYPE_VIDEOS]));
+  }
+  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
+      NS_LITERAL_STRING(DEVICESTORAGE_MUSIC), path))) {
+    NS_NewLocalFile(path, /* aFollowLinks */ true,
+                    getter_AddRefs(mDirs[TYPE_MUSIC]));
+  }
+  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
+      NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), path))) {
+    NS_NewLocalFile(path, /* aFollowLinks */ true,
+                    getter_AddRefs(mDirs[TYPE_SDCARD]));
+  }
+
+#elif defined (XP_UNIX)
+  dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_PICTURES]));
+  dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_VIDEOS]));
+  dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_MUSIC]));
+
+#elif defined (XP_WIN)
+  dirService->Get(NS_WIN_PICTURES_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_PICTURES]));
+  dirService->Get(NS_WIN_VIDEOS_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_VIDEOS]));
+  dirService->Get(NS_WIN_MUSIC_DIR,
+                  NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_MUSIC]));
+#endif
+
+#ifndef MOZ_WIDGET_ANDROID
+  // Eventually, on desktop, we want to do something smarter -- for example,
+  // detect when an sdcard is inserted, and use that instead of this.
+  dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_SDCARD]));
+  if (mDirs[TYPE_SDCARD]) {
+    mDirs[TYPE_SDCARD]->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
+  }
+#endif // !MOZ_WIDGET_ANDROID
+
+  dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
+                  getter_AddRefs(mDirs[TYPE_APPS]));
+  if (mDirs[TYPE_APPS]) {
+    mDirs[TYPE_APPS]->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
+  }
+#endif // !MOZ_WIDGET_GONK
+
+#ifdef MOZ_WIDGET_GONK
+  NS_NewLocalFile(NS_LITERAL_STRING("/data"),
+                  false,
+                  getter_AddRefs(mDirs[TYPE_APPS]));
+#endif
+
+  if (XRE_IsParentProcess()) {
+    NS_GetSpecialDirectory("UAppData", getter_AddRefs(mDirs[TYPE_CRASHES]));
+    if (mDirs[TYPE_CRASHES]) {
+      mDirs[TYPE_CRASHES]->Append(NS_LITERAL_STRING("Crash Reports"));
+    }
+  }
+#ifdef MOZ_WIDGET_GONK
+  // NS_GetSpecialDirectory("UAppData") fails in content processes because
+  // gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
+  else {
+    NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
+                                      false,
+                                      getter_AddRefs(mDirs[TYPE_CRASHES]));
+  }
+#endif
+
+  // 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.
+  Preferences::AddStrongObserver(this, kPrefOverrideRootDir);
+  ResetOverrideRootDir();
+}
+
+void
+DeviceStorageStatics::DumpDirs()
+{
+#ifdef DS_LOGGING
+  sMutex.AssertCurrentThreadOwns();
+
+  static const char* storageTypes[] = {
+    "app",
+    "crashes",
+    "pictures",
+    "videos",
+    "music",
+    "sdcard",
+    "override",
+    nullptr
+  };
+
+  for (uint32_t i = 0; i < TYPE_COUNT; ++i) {
+    MOZ_ASSERT(storageTypes[i]);
+
+    nsString path;
+    if (mDirs[i]) {
+      mDirs[i]->GetPath(path);
+    }
+    DS_LOG_INFO("%s: '%s'",
+      storageTypes[i], NS_LossyConvertUTF16toASCII(path).get());
+  }
+#endif
+}
+
+void
+DeviceStorageStatics::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  sMutex.AssertCurrentThreadOwns();
+  DS_LOG_INFO("");
+
+  Preferences::RemoveObserver(this, kPrefOverrideRootDir);
+  Preferences::RemoveObserver(this, kPrefTesting);
+  Preferences::RemoveObserver(this, kPrefPromptTesting);
+  Preferences::RemoveObserver(this, kPrefWritableName);
+}
+
+/* static */ already_AddRefed<nsIFile>
+DeviceStorageStatics::GetDir(DeviceStorageType aType)
+{
+  MOZ_ASSERT(aType < TYPE_COUNT);
+
+  StaticMutexAutoLock lock(sMutex);
+  if (NS_WARN_IF(!sInstance)) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIFile> file;
+  switch (aType) {
+    case TYPE_APPS:
+    case TYPE_CRASHES:
+    case TYPE_OVERRIDE:
+      file = sInstance->mDirs[aType];
+      return file.forget();
+    default:
+      break;
+  }
+
+  // In testing, we default all device storage types to a temp directory.
+  // This is only initialized if the preference device.storage.testing
+  // was set to true, or if device.storage.overrideRootDir is set.
+  file = sInstance->mDirs[TYPE_OVERRIDE];
+  if (!file) {
+    file = sInstance->mDirs[aType];
+#ifdef MOZ_WIDGET_GONK
+    /* We should use volume mount points on B2G. */
+    MOZ_ASSERT(!file);
+#endif
+  }
+  return file.forget();
+}
+
+/* static */ bool
+DeviceStorageStatics::HasOverrideRootDir()
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (NS_WARN_IF(!sInstance)) {
+    return false;
+  }
+  return sInstance->mDirs[TYPE_OVERRIDE];
+}
+
+/* static */ already_AddRefed<nsIFile>
+DeviceStorageStatics::GetAppsDir()
+{
+  return GetDir(TYPE_APPS);
+}
+
+/* static */ already_AddRefed<nsIFile>
+DeviceStorageStatics::GetCrashesDir()
+{
+  return GetDir(TYPE_CRASHES);
+}
+
+/* static */ already_AddRefed<nsIFile>
+DeviceStorageStatics::GetPicturesDir()
+{
+  return GetDir(TYPE_PICTURES);
+}
+
+/* static */ already_AddRefed<nsIFile>
+DeviceStorageStatics::GetVideosDir()
+{
+  return GetDir(TYPE_VIDEOS);
+}
+
+/* static */ already_AddRefed<nsIFile>
+DeviceStorageStatics::GetMusicDir()
+{
+  return GetDir(TYPE_MUSIC);
+}
+
+/* static */ already_AddRefed<nsIFile>
+DeviceStorageStatics::GetSdcardDir()
+{
+  return GetDir(TYPE_SDCARD);
+}
+
+/* static */ bool
+DeviceStorageStatics::IsPromptTesting()
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (NS_WARN_IF(!sInstance)) {
+    return false;
+  }
+  return sInstance->mPromptTesting;
+}
+
+/* static */ void
+DeviceStorageStatics::GetWritableName(nsString& aName)
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (NS_WARN_IF(!sInstance)) {
+    aName.Truncate();
+    return;
+  }
+  aName = sInstance->mWritableName;
+}
+
+/* static */ void
+DeviceStorageStatics::SetWritableName(const nsAString& aName)
+{
+  StaticMutexAutoLock lock(sMutex);
+  if (!NS_WARN_IF(!sInstance)) {
+    // Update inline although it will be updated again in case
+    // another thread comes in checking it before the update takes
+    sInstance->mWritableName = aName;
+  }
+
+  nsString name;
+  name.Assign(aName);
+  NS_DispatchToMainThread(NS_NewRunnableFunction([name] () -> void {
+    Preferences::SetString(kPrefWritableName, name);
+  }));
+}
+
+/* static */ void
+DeviceStorageStatics::AddListener(nsDOMDeviceStorage* aListener)
+{
+  DS_LOG_DEBUG("%p", aListener);
+
+  StaticMutexAutoLock lock(sMutex);
+  if (NS_WARN_IF(!sInstance)) {
+    return;
+  }
+
+  MOZ_ASSERT(sInstance->mInitialized);
+  if (sInstance->mListeners.IsEmpty()) {
+    NS_DispatchToMainThread(
+      NS_NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Register));
+  }
+
+  nsRefPtr<ListenerWrapper> wrapper =
+    new ListenerWrapper(aListener);
+  sInstance->mListeners.AppendElement(wrapper.forget());
+}
+
+/* static */ void
+DeviceStorageStatics::RemoveListener(nsDOMDeviceStorage* aListener)
+{
+  DS_LOG_DEBUG("%p", aListener);
+
+  StaticMutexAutoLock lock(sMutex);
+  if (!sInstance) {
+    return;
+  }
+
+  bool removed = false;
+  uint32_t i = sInstance->mListeners.Length();
+  while (i > 0) {
+    --i;
+    if (sInstance->mListeners[i]->Equals(aListener)) {
+      sInstance->mListeners.RemoveElementAt(i);
+      removed = true;
+      break;
+    }
+  }
+
+  if (removed && sInstance->mListeners.IsEmpty()) {
+    NS_DispatchToMainThread(
+      NS_NewRunnableMethod(sInstance.get(), &DeviceStorageStatics::Deregister));
+  }
+}
+
+void
+DeviceStorageStatics::Register()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  DS_LOG_INFO("");
+
+  StaticMutexAutoLock lock(sMutex);
+  if (NS_WARN_IF(!sInstance)) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(this, kFileWatcherUpdate, false);
+    obs->AddObserver(this, kDiskSpaceWatcher, false);
+#ifdef MOZ_WIDGET_GONK
+    obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, false);
+#endif
+  }
+}
+
+void
+DeviceStorageStatics::Deregister()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  DS_LOG_INFO("");
+
+  StaticMutexAutoLock lock(sMutex);
+  if (!sInstance) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->RemoveObserver(this, kFileWatcherUpdate);
+    obs->RemoveObserver(this, kDiskSpaceWatcher);
+#ifdef MOZ_WIDGET_GONK
+    obs->RemoveObserver(this, NS_VOLUME_STATE_CHANGED);
+#endif
+  }
+}
+
+void
+DeviceStorageStatics::ResetOverrideRootDir()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  sMutex.AssertCurrentThreadOwns();
+  nsCOMPtr<nsIFile> f;
+  DS_LOG_INFO("");
+
+  if (Preferences::GetBool(kPrefTesting, false)) {
+    DS_LOG_INFO("temp");
+    nsCOMPtr<nsIProperties> dirService
+      = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+    MOZ_ASSERT(dirService);
+    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f));
+    if (f) {
+      f->AppendRelativeNativePath(
+        NS_LITERAL_CSTRING("device-storage-testing"));
+    }
+  } else {
+    // For users running on desktop, it's convenient to be able to override
+    // all of the directories to point to a single tree, much like what happens
+    // on a real device.
+    const nsAdoptingString& overrideRootDir =
+      mozilla::Preferences::GetString(kPrefOverrideRootDir);
+    if (overrideRootDir && !overrideRootDir.IsEmpty()) {
+      NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f));
+    }
+  }
+
+  if (f) {
+    if (XRE_IsParentProcess()) {
+      // Only the parent process can create directories. In testing, because
+      // the preference is updated after startup, its entirely possible that
+      // the preference updated notification will be received by a child
+      // prior to the parent.
+      nsresult rv = f->Create(nsIFile::DIRECTORY_TYPE, 0777);
+      if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
+        nsString path;
+        f->GetPath(path);
+        nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
+                            NS_LossyConvertUTF16toASCII(path).get());
+        NS_WARNING(msg.get());
+      }
+    }
+    f->Normalize();
+  }
+
+  mDirs[TYPE_OVERRIDE] = f.forget();
+  DumpDirs();
+}
+
+NS_IMETHODIMP
+DeviceStorageStatics::Observe(nsISupports* aSubject,
+                              const char* aTopic,
+                              const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+    MOZ_ASSERT(aData);
+
+    StaticMutexAutoLock lock(sMutex);
+    if (NS_WARN_IF(!sInstance)) {
+      return NS_OK;
+    }
+
+    nsDependentString name(aData);
+    if (name.EqualsASCII(kPrefTesting) ||
+        name.EqualsASCII(kPrefOverrideRootDir)) {
+      ResetOverrideRootDir();
+    } else if(name.EqualsASCII(kPrefPromptTesting)) {
+      mPromptTesting = Preferences::GetBool(kPrefPromptTesting, false);
+      DS_LOG_INFO("prompt testing %d", mPromptTesting);
+    } else if(name.EqualsASCII(kPrefWritableName)) {
+      mWritableName = Preferences::GetString(kPrefWritableName);
+      uint32_t i = mListeners.Length();
+      DS_LOG_INFO("writable name '%s' (%u)",
+                  NS_LossyConvertUTF16toASCII(mWritableName).get(), i);
+      while (i > 0) {
+        --i;
+        mListeners[i]->OnWritableNameChanged();
+      }
+    }
+    return NS_OK;
+  }
+
+#ifdef MOZ_WIDGET_GONK
+  if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
+    nsCOMPtr<nsIVolume> volume = do_QueryInterface(aSubject);
+    if (NS_WARN_IF(!volume)) {
+      return NS_OK;
+    }
+
+    StaticMutexAutoLock lock(sMutex);
+    if (NS_WARN_IF(!sInstance)) {
+      return NS_OK;
+    }
+
+    uint32_t i = mListeners.Length();
+    DS_LOG_INFO("volume updated (%u)", i);
+    while (i > 0) {
+      --i;
+      mListeners[i]->OnVolumeStateChanged(volume);
+    }
+    return NS_OK;
+  }
+#endif
+
+  if (!strcmp(aTopic, kFileWatcherUpdate)) {
+    DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
+    if (NS_WARN_IF(!file)) {
+      return NS_OK;
+    }
+
+    StaticMutexAutoLock lock(sMutex);
+    if (NS_WARN_IF(!sInstance)) {
+      return NS_OK;
+    }
+
+    auto data = NS_ConvertUTF16toUTF8(aData);
+    uint32_t i = mListeners.Length();
+    DS_LOG_INFO("file updated (%u)", i);
+    while (i > 0) {
+      --i;
+      mListeners[i]->OnFileWatcherUpdate(data, file);
+    }
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, kDiskSpaceWatcher)) {
+    // 'disk-space-watcher' notifications are sent when there is a modification
+    // of a file in a specific location while a low device storage situation
+    // exists or after recovery of a low storage situation. For Firefox OS,
+    // these notifications are specific for apps storage.
+    bool lowDiskSpace = false;
+    if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
+      lowDiskSpace = true;
+    } else if (NS_strcmp(aData, MOZ_UTF16("free"))) {
+      return NS_OK;
+    }
+
+    StaticMutexAutoLock lock(sMutex);
+    if (NS_WARN_IF(!sInstance)) {
+      return NS_OK;
+    }
+
+    uint32_t i = mListeners.Length();
+    DS_LOG_INFO("disk space %d (%u)", lowDiskSpace, i);
+    while (i > 0) {
+      --i;
+      mListeners[i]->OnDiskSpaceWatcher(lowDiskSpace);
+    }
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    StaticMutexAutoLock lock(sMutex);
+    if (NS_WARN_IF(!sInstance)) {
+      return NS_OK;
+    }
+
+    Shutdown();
+    sInstance = nullptr;
+    return NS_OK;
+  }
+
+  /* Here we convert file-watcher-notify and download-watcher-notify observer
+     events to file-watcher-update events.  This is used to be able to
+     broadcast events from one child to another child in B2G.  (f.e., if one
+     child decides to add a file, we want to be able to able to send a onchange
+     notifications to every other child watching that device storage object).*/
+  nsRefPtr<DeviceStorageFile> dsf;
+  if (!strcmp(aTopic, kDownloadWatcherNotify)) {
+    // aSubject will be an nsISupportsString with the native path to the file
+    // in question.
+
+    nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(aSubject);
+    if (!supportsString) {
+      return NS_OK;
+    }
+    nsString path;
+    nsresult rv = supportsString->GetData(path);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_OK;
+    }
+
+    // The downloader uses the sdcard storage type.
+    nsString volName;
+#ifdef MOZ_WIDGET_GONK
+    if (DeviceStorageTypeChecker::IsVolumeBased(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD))) {
+      nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
+      if (NS_WARN_IF(!vs)) {
+        return NS_OK;
+      }
+      nsCOMPtr<nsIVolume> vol;
+      rv = vs->GetVolumeByPath(path, getter_AddRefs(vol));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_OK;
+      }
+      rv = vol->GetName(volName);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_OK;
+      }
+      nsString mountPoint;
+      rv = vol->GetMountPoint(mountPoint);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_OK;
+      }
+      if (!Substring(path, 0, mountPoint.Length()).Equals(mountPoint)) {
+        return NS_OK;
+      }
+      path = Substring(path, mountPoint.Length() + 1);
+    }
+#endif
+    dsf = new DeviceStorageFile(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), volName, path);
+
+  } else if (!strcmp(aTopic, kFileWatcherNotify)) {
+    dsf = static_cast<DeviceStorageFile*>(aSubject);
+  } else {
+    DS_LOG_WARN("unhandled topic '%s'", aTopic);
+    return NS_OK;
+  }
+
+  if (NS_WARN_IF(!dsf || !dsf->mFile)) {
+    return NS_OK;
+  }
+
+  if (!XRE_IsParentProcess()) {
+    // Child process. Forward the notification to the parent.
+    ContentChild::GetSingleton()
+      ->SendFilePathUpdateNotify(dsf->mStorageType,
+                                 dsf->mStorageName,
+                                 dsf->mPath,
+                                 NS_ConvertUTF16toUTF8(aData));
+    return NS_OK;
+  }
+
+  // Multiple storage types may match the same files. So walk through each of
+  // the storage types, and if the extension matches, tell them about it.
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (DeviceStorageTypeChecker::IsSharedMediaRoot(dsf->mStorageType)) {
+    DeviceStorageTypeChecker* typeChecker
+      = DeviceStorageTypeChecker::CreateOrGet();
+    MOZ_ASSERT(typeChecker);
+
+    static const nsLiteralString kMediaTypes[] = {
+      NS_LITERAL_STRING(DEVICESTORAGE_SDCARD),
+      NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
+      NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS),
+      NS_LITERAL_STRING(DEVICESTORAGE_MUSIC),
+    };
+
+    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(kMediaTypes); i++) {
+      nsRefPtr<DeviceStorageFile> dsf2;
+      if (typeChecker->Check(kMediaTypes[i], dsf->mPath)) {
+        if (dsf->mStorageType.Equals(kMediaTypes[i])) {
+          dsf2 = dsf;
+        } else {
+          dsf2 = new DeviceStorageFile(kMediaTypes[i],
+                                       dsf->mStorageName, dsf->mPath);
+        }
+        obs->NotifyObservers(dsf2, kFileWatcherUpdate, aData);
+      }
+    }
+  } else {
+    obs->NotifyObservers(dsf, kFileWatcherUpdate, aData);
+  }
+  return NS_OK;
+}
+
+DeviceStorageStatics::ListenerWrapper::ListenerWrapper(nsDOMDeviceStorage* aListener)
+  : mListener(do_GetWeakReference(static_cast<DOMEventTargetHelper*>(aListener)))
+  , mOwningThread(NS_GetCurrentThread())
+{
+}
+
+DeviceStorageStatics::ListenerWrapper::~ListenerWrapper()
+{
+  // Even weak pointers are not thread safe
+  NS_ProxyRelease(mOwningThread, mListener);
+}
+
+bool
+DeviceStorageStatics::ListenerWrapper::Equals(nsDOMDeviceStorage* aListener)
+{
+  bool current = false;
+  mOwningThread->IsOnCurrentThread(&current);
+  if (current) {
+    // It is only safe to acquire the reference on the owning thread
+    nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(mListener);
+    return listener.get() == aListener;
+  }
+  return false;
+}
+
+void
+DeviceStorageStatics::ListenerWrapper::OnFileWatcherUpdate(const nsCString& aData,
+                                                                 DeviceStorageFile* aFile)
+{
+  nsRefPtr<ListenerWrapper> self = this;
+  nsCString data = aData;
+  nsRefPtr<DeviceStorageFile> file = aFile;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, data, file] () -> void {
+    nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
+    if (listener) {
+      listener->OnFileWatcherUpdate(data, file);
+    }
+  });
+  mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+void
+DeviceStorageStatics::ListenerWrapper::OnDiskSpaceWatcher(bool aLowDiskSpace)
+{
+  nsRefPtr<ListenerWrapper> self = this;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aLowDiskSpace] () -> void {
+    nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
+    if (listener) {
+      listener->OnDiskSpaceWatcher(aLowDiskSpace);
+    }
+  });
+  mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+void
+DeviceStorageStatics::ListenerWrapper::OnWritableNameChanged()
+{
+  nsRefPtr<ListenerWrapper> self = this;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void {
+    nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
+    if (listener) {
+      listener->OnWritableNameChanged();
+    }
+  });
+  mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+#ifdef MOZ_WIDGET_GONK
+void
+DeviceStorageStatics::ListenerWrapper::OnVolumeStateChanged(nsIVolume* aVolume)
+{
+  nsRefPtr<ListenerWrapper> self = this;
+  nsCOMPtr<nsIVolume> volume = aVolume;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, volume] () -> void {
+    nsRefPtr<nsDOMDeviceStorage> listener = do_QueryReferent(self->mListener);
+    if (listener) {
+      listener->OnVolumeStateChanged(volume);
+    }
+  });
+  mOwningThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+#endif
+
+} // namespace devicestorage
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/DeviceStorageStatics.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_devicestorage_DeviceStorageStatics_h
+#define mozilla_dom_devicestorage_DeviceStorageStatics_h
+
+#include "mozilla/Mutex.h"
+
+class nsDOMDeviceStorage;
+class DeviceStorageFile;
+#ifdef MOZ_WIDGET_GONK
+class nsIVolume;
+#endif
+
+namespace mozilla {
+namespace dom {
+namespace devicestorage {
+
+class DeviceStorageStatics final : public nsIObserver
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static void Initialize();
+  static void InitializeDirs();
+  static void AddListener(nsDOMDeviceStorage* aListener);
+  static void RemoveListener(nsDOMDeviceStorage* aListener);
+
+  static bool IsPromptTesting();
+  static void GetWritableName(nsString& aName);
+  static void SetWritableName(const nsAString& aName);
+
+  static bool HasOverrideRootDir();
+  static already_AddRefed<nsIFile> GetAppsDir();
+  static already_AddRefed<nsIFile> GetCrashesDir();
+  static already_AddRefed<nsIFile> GetPicturesDir();
+  static already_AddRefed<nsIFile> GetVideosDir();
+  static already_AddRefed<nsIFile> GetMusicDir();
+  static already_AddRefed<nsIFile> GetSdcardDir();
+
+private:
+  enum DeviceStorageType {
+    TYPE_APPS,
+    TYPE_CRASHES,
+    TYPE_PICTURES,
+    TYPE_VIDEOS,
+    TYPE_MUSIC,
+    TYPE_SDCARD,
+    TYPE_OVERRIDE,
+    TYPE_COUNT
+  };
+
+  static already_AddRefed<nsIFile> GetDir(DeviceStorageType aType);
+
+  DeviceStorageStatics();
+  virtual ~DeviceStorageStatics();
+
+  void Init();
+  void InitDirs();
+  void DumpDirs();
+  void Shutdown();
+  void Register();
+  void Deregister();
+  void ResetOverrideRootDir();
+
+  class ListenerWrapper final {
+  public:
+    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ListenerWrapper)
+
+    explicit ListenerWrapper(nsDOMDeviceStorage* aListener);
+    bool Equals(nsDOMDeviceStorage* aListener);
+    void OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile);
+    void OnDiskSpaceWatcher(bool aLowDiskSpace);
+    void OnWritableNameChanged();
+#ifdef MOZ_WIDGET_GONK
+    void OnVolumeStateChanged(nsIVolume* aVolume);
+#endif
+
+  private:
+    virtual ~ListenerWrapper();
+
+    nsWeakPtr mListener;
+    nsCOMPtr<nsIThread> mOwningThread;
+  };
+
+  nsTArray<nsRefPtr<ListenerWrapper> > mListeners;
+  nsCOMPtr<nsIFile> mDirs[TYPE_COUNT];
+
+  bool mInitialized;
+  bool mPromptTesting;
+  nsString mWritableName;
+
+  static StaticRefPtr<DeviceStorageStatics> sInstance;
+  static StaticMutex sMutex;
+};
+
+} // namespace devicestorage
+} // namespace dom
+} // namespace mozilla
+
+#endif
--- a/dom/devicestorage/moz.build
+++ b/dom/devicestorage/moz.build
@@ -12,22 +12,24 @@ EXPORTS += [
 
 EXPORTS.mozilla.dom += [
     'DeviceStorageAreaListener.h',
 ]
 
 EXPORTS.mozilla.dom.devicestorage += [
     'DeviceStorageRequestChild.h',
     'DeviceStorageRequestParent.h',
+    'DeviceStorageStatics.h',
 ]
 
 UNIFIED_SOURCES += [
     'DeviceStorageAreaListener.cpp',
     'DeviceStorageRequestChild.cpp',
     'DeviceStorageRequestParent.cpp',
+    'DeviceStorageStatics.cpp',
     'nsDeviceStorage.cpp',
 ]
 
 IPDL_SOURCES += [
     'PDeviceStorageRequest.ipdl',
 ]
 
 FAIL_ON_WARNINGS = True
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -19,82 +19,67 @@
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/LazyIdleThread.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/Services.h"
 
 #include "nsArrayUtils.h"
 #include "nsAutoPtr.h"
 #include "nsGlobalWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIFile.h"
 #include "nsIDirectoryEnumerator.h"
-#include "nsAppDirectoryServiceDefs.h"
-#include "nsDirectoryServiceDefs.h"
 #include "nsNetUtil.h"
 #include "nsIOutputStream.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPrincipal.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsXULAppAPI.h"
 #include "DeviceStorageFileDescriptor.h"
 #include "DeviceStorageRequestChild.h"
+#include "DeviceStorageStatics.h"
 #include "nsCRT.h"
 #include "nsIObserverService.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIPermissionManager.h"
 #include "nsIStringBundle.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIDocument.h"
-#include "nsPrintfCString.h"
 #include <algorithm>
 #include "private/pprio.h"
 #include "nsContentPermissionHelper.h"
 
 #include "mozilla/dom/DeviceStorageBinding.h"
 
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
-#ifdef MOZ_WIDGET_ANDROID
-#include "AndroidBridge.h"
-#endif
-
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #endif
 
 #define DEVICESTORAGE_PROPERTIES \
   "chrome://global/content/devicestorage.properties"
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
-#define PREF_STORAGE_WRITABLE_NAME \
-  "device.storage.writable.name"
 #define STORAGE_CHANGE_EVENT "change"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::ipc;
 
-#include "nsDirectoryServiceDefs.h"
-
-const char* kFileWatcherUpdate = "file-watcher-update";
-const char* kFileWatcherNotify = "file-watcher-notify";
-const char *kDownloadWatcherNotify = "download-watcher-notify";
-
 namespace mozilla {
   MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
 } // namespace mozilla
 
 StaticAutoPtr<DeviceStorageUsedSpaceCache>
   DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
 
 DeviceStorageUsedSpaceCache::DeviceStorageUsedSpaceCache()
@@ -195,35 +180,16 @@ DeviceStorageUsedSpaceCache::SetUsedSize
 
   cacheEntry->mPicturesUsedSize = aPictureSize;
   cacheEntry->mVideosUsedSize = aVideosSize;
   cacheEntry->mMusicUsedSize = aMusicSize;
   cacheEntry->mTotalUsedSize = aTotalUsedSize;
   cacheEntry->mDirty = false;
 }
 
-class GlobalDirs
-{
-private:
-  ~GlobalDirs() {}
-public:
-  NS_INLINE_DECL_REFCOUNTING(GlobalDirs)
-#if !defined(MOZ_WIDGET_GONK)
-  nsCOMPtr<nsIFile> pictures;
-  nsCOMPtr<nsIFile> videos;
-  nsCOMPtr<nsIFile> music;
-  nsCOMPtr<nsIFile> sdcard;
-#endif
-  nsCOMPtr<nsIFile> apps;
-  nsCOMPtr<nsIFile> crashes;
-  nsCOMPtr<nsIFile> overrideRootDir;
-};
-
-static StaticRefPtr<GlobalDirs> sDirs;
-
 StaticAutoPtr<DeviceStorageTypeChecker>
   DeviceStorageTypeChecker::sDeviceStorageTypeChecker;
 
 DeviceStorageTypeChecker::DeviceStorageTypeChecker()
 {
 }
 
 DeviceStorageTypeChecker::~DeviceStorageTypeChecker()
@@ -470,146 +436,20 @@ DeviceStorageTypeChecker::IsSharedMediaR
 {
   // This function determines if aType shares a root directory with the
   // other media types (so only applies to music, videos, pictures and sdcard).
 #ifdef MOZ_WIDGET_GONK
   return IsMediaType(aType);
 #else
   // For desktop, if the directories have been overridden, then they share
   // a common root.
-  return IsMediaType(aType) && sDirs->overrideRootDir;
+  return IsMediaType(aType) && DeviceStorageStatics::HasOverrideRootDir();
 #endif
 }
 
-NS_IMPL_ISUPPORTS(FileUpdateDispatcher, nsIObserver)
-
-mozilla::StaticRefPtr<FileUpdateDispatcher> FileUpdateDispatcher::sSingleton;
-
-FileUpdateDispatcher*
-FileUpdateDispatcher::GetSingleton()
-{
-  if (sSingleton) {
-    return sSingleton;
-  }
-
-  sSingleton = new FileUpdateDispatcher();
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->AddObserver(sSingleton, kFileWatcherNotify, false);
-  obs->AddObserver(sSingleton, kDownloadWatcherNotify, false);
-  ClearOnShutdown(&sSingleton);
-
-  return sSingleton;
-}
-
-NS_IMETHODIMP
-FileUpdateDispatcher::Observe(nsISupports* aSubject,
-                              const char* aTopic,
-                              const char16_t* aData)
-{
-  nsRefPtr<DeviceStorageFile> dsf;
-
-  if (!strcmp(aTopic, kDownloadWatcherNotify)) {
-    // aSubject will be an nsISupportsString with the native path to the file
-    // in question.
-
-    nsCOMPtr<nsISupportsString> supportsString = do_QueryInterface(aSubject);
-    if (!supportsString) {
-      return NS_OK;
-    }
-    nsString path;
-    nsresult rv = supportsString->GetData(path);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return NS_OK;
-    }
-
-    // The downloader uses the sdcard storage type.
-    nsString volName;
-#ifdef MOZ_WIDGET_GONK
-    if (DeviceStorageTypeChecker::IsVolumeBased(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD))) {
-      nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
-      if (NS_WARN_IF(!vs)) {
-        return NS_OK;
-      }
-      nsCOMPtr<nsIVolume> vol;
-      rv = vs->GetVolumeByPath(path, getter_AddRefs(vol));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_OK;
-      }
-      rv = vol->GetName(volName);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_OK;
-      }
-      nsString mountPoint;
-      rv = vol->GetMountPoint(mountPoint);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_OK;
-      }
-      if (!Substring(path, 0, mountPoint.Length()).Equals(mountPoint)) {
-        return NS_OK;
-      }
-      path = Substring(path, mountPoint.Length() + 1);
-    }
-#endif
-    dsf = new DeviceStorageFile(NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), volName, path);
-
-  } else if (!strcmp(aTopic, kFileWatcherNotify)) {
-    dsf = static_cast<DeviceStorageFile*>(aSubject);
-  } else {
-    NS_WARNING("FileUpdateDispatcher: Unrecognized topic");
-    return NS_OK;
-  }
-
-  if (!dsf || !dsf->mFile) {
-    NS_WARNING("FileUpdateDispatcher: Device storage file looks invalid!");
-    return NS_OK;
-  }
-
-  if (!XRE_IsParentProcess()) {
-    // Child process. Forward the notification to the parent.
-    ContentChild::GetSingleton()
-      ->SendFilePathUpdateNotify(dsf->mStorageType,
-                                 dsf->mStorageName,
-                                 dsf->mPath,
-                                 NS_ConvertUTF16toUTF8(aData));
-    return NS_OK;
-  }
-
-  // Multiple storage types may match the same files. So walk through each of
-  // the storage types, and if the extension matches, tell them about it.
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (DeviceStorageTypeChecker::IsSharedMediaRoot(dsf->mStorageType)) {
-    DeviceStorageTypeChecker* typeChecker
-      = DeviceStorageTypeChecker::CreateOrGet();
-    MOZ_ASSERT(typeChecker);
-
-    static const nsLiteralString kMediaTypes[] = {
-      NS_LITERAL_STRING(DEVICESTORAGE_SDCARD),
-      NS_LITERAL_STRING(DEVICESTORAGE_PICTURES),
-      NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS),
-      NS_LITERAL_STRING(DEVICESTORAGE_MUSIC),
-    };
-
-    for (size_t i = 0; i < MOZ_ARRAY_LENGTH(kMediaTypes); i++) {
-      nsRefPtr<DeviceStorageFile> dsf2;
-      if (typeChecker->Check(kMediaTypes[i], dsf->mPath)) {
-        if (dsf->mStorageType.Equals(kMediaTypes[i])) {
-          dsf2 = dsf;
-        } else {
-          dsf2 = new DeviceStorageFile(kMediaTypes[i],
-                                       dsf->mStorageName, dsf->mPath);
-        }
-        obs->NotifyObservers(dsf2, kFileWatcherUpdate, aData);
-      }
-    }
-  } else {
-    obs->NotifyObservers(dsf, kFileWatcherUpdate, aData);
-  }
-  return NS_OK;
-}
-
 class IOEventComplete : public nsRunnable
 {
 public:
   IOEventComplete(DeviceStorageFile *aFile, const char *aType)
     : mFile(aFile)
     , mType(aType)
   {
   }
@@ -618,17 +458,17 @@ public:
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
     nsString data;
     CopyASCIItoUTF16(mType, data);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 
-    obs->NotifyObservers(mFile, kFileWatcherNotify, data.get());
+    obs->NotifyObservers(mFile, "file-watcher-notify", data.get());
 
     DeviceStorageUsedSpaceCache* usedSpaceCache
       = DeviceStorageUsedSpaceCache::CreateOrGet();
     MOZ_ASSERT(usedSpaceCache);
     usedSpaceCache->Invalidate(mFile->mStorageName);
     return NS_OK;
   }
 
@@ -716,250 +556,16 @@ DeviceStorageFile::Init()
                                              mStorageName,
                                              getter_AddRefs(mFile));
 
   DebugOnly<DeviceStorageTypeChecker*> typeChecker
     = DeviceStorageTypeChecker::CreateOrGet();
   MOZ_ASSERT(typeChecker);
 }
 
-// The OverrideRootDir is needed to facilitate testing of the
-// device.storage.overrideRootDir preference. The preference is normally
-// only read once during initialization, but since the test environment has
-// no convenient way to restart, we use a pref watcher instead.
-class OverrideRootDir final : public nsIObserver
-{
-  ~OverrideRootDir();
-
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-
-  static OverrideRootDir* GetSingleton();
-  void Init();
-private:
-  static mozilla::StaticRefPtr<OverrideRootDir> sSingleton;
-};
-
-NS_IMPL_ISUPPORTS(OverrideRootDir, nsIObserver)
-
-mozilla::StaticRefPtr<OverrideRootDir>
-  OverrideRootDir::sSingleton;
-
-OverrideRootDir*
-OverrideRootDir::GetSingleton()
-{
-  if (sSingleton) {
-    return sSingleton;
-  }
-  // Preference changes are automatically forwarded from parent to child
-  // in ContentParent::Observe, so we'll see the change in both the parent
-  // and the child process.
-
-  sSingleton = new OverrideRootDir();
-  Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir");
-  Preferences::AddStrongObserver(sSingleton, "device.storage.testing");
-  ClearOnShutdown(&sSingleton);
-
-  return sSingleton;
-}
-
-OverrideRootDir::~OverrideRootDir()
-{
-  Preferences::RemoveObserver(this, "device.storage.overrideRootDir");
-  Preferences::RemoveObserver(this, "device.storage.testing");
-}
-
-NS_IMETHODIMP
-OverrideRootDir::Observe(nsISupports *aSubject,
-                              const char *aTopic,
-                              const char16_t *aData)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (sSingleton) {
-    sSingleton->Init();
-  }
-  return NS_OK;
-}
-
-void
-OverrideRootDir::Init()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!sDirs) {
-    return;
-  }
-
-  if (mozilla::Preferences::GetBool("device.storage.testing", false)) {
-    nsCOMPtr<nsIProperties> dirService
-      = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
-    MOZ_ASSERT(dirService);
-    dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile),
-                    getter_AddRefs(sDirs->overrideRootDir));
-    if (sDirs->overrideRootDir) {
-      sDirs->overrideRootDir->AppendRelativeNativePath(
-        NS_LITERAL_CSTRING("device-storage-testing"));
-    }
-  } else {
-    // For users running on desktop, it's convenient to be able to override
-    // all of the directories to point to a single tree, much like what happens
-    // on a real device.
-    const nsAdoptingString& overrideRootDir =
-      mozilla::Preferences::GetString("device.storage.overrideRootDir");
-    if (overrideRootDir && overrideRootDir.Length() > 0) {
-      NS_NewLocalFile(overrideRootDir, false,
-                      getter_AddRefs(sDirs->overrideRootDir));
-    } else {
-      sDirs->overrideRootDir = nullptr;
-    }
-  }
-
-  if (sDirs->overrideRootDir) {
-    if (XRE_IsParentProcess()) {
-      // Only the parent process can create directories. In testing, because
-      // the preference is updated after startup, its entirely possible that
-      // the preference updated notification will be received by a child
-      // prior to the parent.
-      nsresult rv
-        = sDirs->overrideRootDir->Create(nsIFile::DIRECTORY_TYPE, 0777);
-      nsString path;
-      sDirs->overrideRootDir->GetPath(path);
-      if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
-        nsPrintfCString msg("DeviceStorage: Unable to create directory '%s'",
-                            NS_LossyConvertUTF16toASCII(path).get());
-        NS_WARNING(msg.get());
-      }
-    }
-    sDirs->overrideRootDir->Normalize();
-  }
-}
-
-// 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()
-{
-  if (sDirs) {
-    return;
-  }
-  MOZ_ASSERT(NS_IsMainThread());
-  sDirs = new GlobalDirs;
-  ClearOnShutdown(&sDirs);
-
-  nsCOMPtr<nsIProperties> dirService
-    = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
-  MOZ_ASSERT(dirService);
-
-#if !defined(MOZ_WIDGET_GONK)
-
-// Keep MOZ_WIDGET_COCOA above XP_UNIX,
-// because both are defined in Darwin builds.
-#if defined (MOZ_WIDGET_COCOA)
-  dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->pictures));
-  dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->videos));
-  dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->music));
-
-// Keep MOZ_WIDGET_ANDROID above XP_UNIX,
-// because both are defined in Android builds.
-#elif defined (MOZ_WIDGET_ANDROID)
-  nsAutoString path;
-  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
-      NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), path))) {
-    NS_NewLocalFile(path, /* aFollowLinks */ true,
-                    getter_AddRefs(sDirs->pictures));
-  }
-  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
-      NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS), path))) {
-    NS_NewLocalFile(path, /* aFollowLinks */ true,
-                    getter_AddRefs(sDirs->videos));
-  }
-  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
-      NS_LITERAL_STRING(DEVICESTORAGE_MUSIC), path))) {
-    NS_NewLocalFile(path, /* aFollowLinks */ true,
-                    getter_AddRefs(sDirs->music));
-  }
-  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
-      NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), path))) {
-    NS_NewLocalFile(path, /* aFollowLinks */ true,
-                    getter_AddRefs(sDirs->sdcard));
-  }
-
-#elif defined (XP_UNIX)
-  dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->pictures));
-  dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->videos));
-  dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->music));
-
-#elif defined (XP_WIN)
-  dirService->Get(NS_WIN_PICTURES_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->pictures));
-  dirService->Get(NS_WIN_VIDEOS_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->videos));
-  dirService->Get(NS_WIN_MUSIC_DIR,
-                  NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->music));
-#endif
-
-#ifndef MOZ_WIDGET_ANDROID
-  // Eventually, on desktop, we want to do something smarter -- for example,
-  // detect when an sdcard is inserted, and use that instead of this.
-  dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->sdcard));
-  if (sDirs->sdcard) {
-    sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
-  }
-#endif // !MOZ_WIDGET_ANDROID
-#endif // !MOZ_WIDGET_GONK
-
-#ifdef MOZ_WIDGET_GONK
-  NS_NewLocalFile(NS_LITERAL_STRING("/data"),
-                  false,
-                  getter_AddRefs(sDirs->apps));
-#else
-  dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
-                  getter_AddRefs(sDirs->apps));
-  if (sDirs->apps) {
-    sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps"));
-  }
-#endif
-
-  if (XRE_IsParentProcess()) {
-    NS_GetSpecialDirectory("UAppData", getter_AddRefs(sDirs->crashes));
-    if (sDirs->crashes) {
-      sDirs->crashes->Append(NS_LITERAL_STRING("Crash Reports"));
-    }
-  } else {
-    // NS_GetSpecialDirectory("UAppData") fails in content processes because
-    // gAppData from toolkit/xre/nsAppRunner.cpp is not initialized.
-#ifdef MOZ_WIDGET_GONK
-    NS_NewLocalFile(NS_LITERAL_STRING("/data/b2g/mozilla/Crash Reports"),
-                                      false,
-                                      getter_AddRefs(sDirs->crashes));
-#endif
-  }
-
-  OverrideRootDir::GetSingleton()->Init();
-}
-
 void
 DeviceStorageFile::GetFullPath(nsAString &aFullPath)
 {
   aFullPath.Truncate();
   if (!mStorageName.EqualsLiteral("")) {
     aFullPath.Append('/');
     aFullPath.Append(mStorageName);
     aFullPath.Append('/');
@@ -968,125 +574,75 @@ DeviceStorageFile::GetFullPath(nsAString
     aFullPath.Append(mRootDir);
     aFullPath.Append('/');
   }
   aFullPath.Append(mPath);
 }
 
 
 // Directories which don't depend on a volume should be calculated once
-// in InitDirs. Directories which depend on the root directory of a volume
-// should be calculated in this method.
+// in DeviceStorageStatics::Initialize. Directories which depend on the
+// root directory of a volume should be calculated in this method.
 void
 DeviceStorageFile::GetRootDirectoryForType(const nsAString& aStorageType,
                                            const nsAString& aStorageName,
                                            nsIFile** aFile)
 {
   nsCOMPtr<nsIFile> f;
   *aFile = nullptr;
-  bool allowOverride = true;
-
-  InitDirs();
+
+  DeviceStorageStatics::InitializeDirs();
 
 #ifdef MOZ_WIDGET_GONK
   nsresult rv;
   nsString volMountPoint;
   if (DeviceStorageTypeChecker::IsVolumeBased(aStorageType)) {
     nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
     NS_ENSURE_TRUE_VOID(vs);
     nsCOMPtr<nsIVolume> vol;
     rv = vs->GetVolumeByName(aStorageName, getter_AddRefs(vol));
-    if(NS_FAILED(rv)) {
+    if (NS_FAILED(rv)) {
       printf_stderr("##### DeviceStorage: GetVolumeByName('%s') failed\n",
                     NS_LossyConvertUTF16toASCII(aStorageName).get());
     }
     NS_ENSURE_SUCCESS_VOID(rv);
     vol->GetMountPoint(volMountPoint);
   }
 #endif
 
-  // Picture directory
   if (aStorageType.EqualsLiteral(DEVICESTORAGE_PICTURES)) {
-#ifdef MOZ_WIDGET_GONK
-    rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
-    if(NS_FAILED(rv)) {
-      printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
-                    NS_LossyConvertUTF16toASCII(volMountPoint).get(),
-                    NS_LossyConvertUTF16toASCII(aStorageType).get());
-    }
-#else
-    f = sDirs->pictures;
-#endif
+    f = DeviceStorageStatics::GetPicturesDir();
+  } else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
+    f = DeviceStorageStatics::GetVideosDir();
+  } else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
+    f = DeviceStorageStatics::GetMusicDir();
+  } else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) {
+    f = DeviceStorageStatics::GetAppsDir();
+  } else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
+    f = DeviceStorageStatics::GetCrashesDir();
+  } else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
+    f = DeviceStorageStatics::GetSdcardDir();
+  } else {
+    printf_stderr("##### DeviceStorage: Unrecognized StorageType: '%s'\n",
+                  NS_LossyConvertUTF16toASCII(aStorageType).get());
+    return;
   }
 
-  // Video directory
-  else if (aStorageType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) {
 #ifdef MOZ_WIDGET_GONK
+  /* For volume based storage types, we will only have a file already
+     if the override root directory option is in effect. */
+  if (!f && !volMountPoint.IsEmpty()) {
     rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
-    if(NS_FAILED(rv)) {
-      printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
-                    NS_LossyConvertUTF16toASCII(volMountPoint).get(),
-                    NS_LossyConvertUTF16toASCII(aStorageType).get());
-    }
-#else
-    f = sDirs->videos;
-#endif
-  }
-
-  // Music directory
-  else if (aStorageType.EqualsLiteral(DEVICESTORAGE_MUSIC)) {
-#ifdef MOZ_WIDGET_GONK
-    rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
-    if(NS_FAILED(rv)) {
+    if (NS_FAILED(rv)) {
       printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
                     NS_LossyConvertUTF16toASCII(volMountPoint).get(),
                     NS_LossyConvertUTF16toASCII(aStorageType).get());
     }
-#else
-    f = sDirs->music;
+  }
 #endif
-  }
-
-  // Apps directory
-  else if (aStorageType.EqualsLiteral(DEVICESTORAGE_APPS)) {
-    f = sDirs->apps;
-    allowOverride = false;
-  }
-
-   // default SDCard
-   else if (aStorageType.EqualsLiteral(DEVICESTORAGE_SDCARD)) {
-#ifdef MOZ_WIDGET_GONK
-     rv = NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f));
-     if(NS_FAILED(rv)) {
-       printf_stderr("##### DeviceStorage: NS_NewLocalFile failed StorageType: '%s' path '%s'\n",
-                     NS_LossyConvertUTF16toASCII(volMountPoint).get(),
-                     NS_LossyConvertUTF16toASCII(aStorageType).get());
-     }
-#else
-     f = sDirs->sdcard;
-#endif
-  }
-
-  // crash reports directory.
-  else if (aStorageType.EqualsLiteral(DEVICESTORAGE_CRASHES)) {
-    f = sDirs->crashes;
-    allowOverride = false;
-  } else {
-    // Not a storage type that we recognize. Return null
-    return;
-  }
-
-  // In testing, we default all device storage types to a temp directory.
-  // sDirs->overrideRootDir will only have been initialized (in InitDirs)
-  // if the preference device.storage.testing was set to true, or if
-  // device.storage.overrideRootDir is set. We can't test the preferences
-  // directly here, since we may not be on the main thread.
-  if (allowOverride && sDirs->overrideRootDir) {
-    f = sDirs->overrideRootDir;
-  }
 
   if (f) {
     f->Clone(aFile);
   } else {
     // This should never happen unless something is severely wrong. So
     // scream a little.
     printf_stderr("##### GetRootDirectoryForType('%s', '%s') failed #####",
                   NS_LossyConvertUTF16toASCII(aStorageType).get(),
@@ -1836,47 +1392,26 @@ DeviceStorageFile::GetStorageStatus(nsAS
   rv = vol->GetState(&volState);
   NS_ENSURE_SUCCESS_VOID(rv);
   aStatus.AssignASCII(mozilla::system::NS_VolumeStateStr(volState));
 #endif
 }
 
 NS_IMPL_ISUPPORTS0(DeviceStorageFile)
 
-static void
-RegisterForSDCardChanges(nsIObserver* aObserver)
-{
-#ifdef MOZ_WIDGET_GONK
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->AddObserver(aObserver, NS_VOLUME_STATE_CHANGED, false);
-#endif
-}
-
-static void
-UnregisterForSDCardChanges(nsIObserver* aObserver)
-{
-#ifdef MOZ_WIDGET_GONK
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->RemoveObserver(aObserver, NS_VOLUME_STATE_CHANGED);
-#endif
-}
-
 void
 nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aStorageType,
                                             const nsAString& aStorageName)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsIFile> f;
   DeviceStorageFile::GetRootDirectoryForType(aStorageType,
                                              aStorageName,
                                              getter_AddRefs(f));
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->AddObserver(this, kFileWatcherUpdate, false);
-  obs->AddObserver(this, "disk-space-watcher", false);
   mRootDirectory = f;
   mStorageType = aStorageType;
   mStorageName = aStorageName;
 }
 
 JS::Value
 InterfaceToJsval(nsPIDOMWindow* aWindow,
                  nsISupports* aObject,
@@ -2925,17 +2460,17 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DeviceStorageRequest,
                                            nsIContentPermissionRequest)
 
   NS_IMETHOD Run() override
   {
     MOZ_ASSERT(NS_IsMainThread());
 
-    if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
+    if (DeviceStorageStatics::IsPromptTesting()) {
       Allow(JS::UndefinedHandleValue);
       return NS_OK;
     }
 
     return nsContentPermissionUtils::AskPermission(this, mWindow);
   }
 
   NS_IMETHODIMP GetTypes(nsIArray** aTypes) override
@@ -3358,17 +2893,22 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(DeviceS
 NS_IMPL_CYCLE_COLLECTION(DeviceStorageRequest,
                          mRequest,
                          mWindow,
                          mBlob,
                          mDeviceStorage)
 
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceStorage)
-  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+  /* nsISupports is an ambiguous base of nsDOMDeviceStorage
+     so we have to work around that. */
+  if ( aIID.Equals(NS_GET_IID(nsDOMDeviceStorage)) )
+    foundInterface = static_cast<nsISupports*>(static_cast<void*>(this));
+  else
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceStorage, DOMEventTargetHelper)
 
 int nsDOMDeviceStorage::sInstanceCount = 0;
 
 nsDOMDeviceStorage::nsDOMDeviceStorage(nsPIDOMWindow* aWindow)
@@ -3387,30 +2927,26 @@ nsDOMDeviceStorage::WrapObject(JSContext
 {
   return DeviceStorageBinding::Wrap(aCx, this, aGivenProto);
 }
 
 nsresult
 nsDOMDeviceStorage::Init(nsPIDOMWindow* aWindow, const nsAString &aType,
                          const nsAString &aVolName)
 {
-  DebugOnly<FileUpdateDispatcher*> observer
-    = FileUpdateDispatcher::GetSingleton();
-  MOZ_ASSERT(observer);
-
   MOZ_ASSERT(aWindow);
 
   SetRootDirectoryForType(aType, aVolName);
   if (!mRootDirectory) {
     return NS_ERROR_NOT_AVAILABLE;
   }
+
+  DeviceStorageStatics::AddListener(this);
   if (!mStorageName.IsEmpty()) {
-    Preferences::AddStrongObserver(this, PREF_STORAGE_WRITABLE_NAME);
     mIsDefaultLocation = Default();
-    RegisterForSDCardChanges(this);
 
 #ifdef MOZ_WIDGET_GONK
     if (DeviceStorageTypeChecker::IsVolumeBased(mStorageType)) {
       nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
       if (NS_WARN_IF(!vs)) {
         return NS_ERROR_FAILURE;
       }
       nsresult rv;
@@ -3462,36 +2998,30 @@ nsDOMDeviceStorage::Init(nsPIDOMWindow* 
 
   return NS_OK;
 }
 
 nsDOMDeviceStorage::~nsDOMDeviceStorage()
 {
   MOZ_ASSERT(NS_IsMainThread());
   sInstanceCount--;
+  DeviceStorageStatics::RemoveListener(this);
 }
 
 void
 nsDOMDeviceStorage::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mFileSystem) {
     mFileSystem->Shutdown();
     mFileSystem = nullptr;
   }
 
-  if (!mStorageName.IsEmpty()) {
-    Preferences::RemoveObserver(this, PREF_STORAGE_WRITABLE_NAME);
-    UnregisterForSDCardChanges(this);
-  }
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  obs->RemoveObserver(this, kFileWatcherUpdate);
-  obs->RemoveObserver(this, "disk-space-watcher");
+  DeviceStorageStatics::RemoveListener(this);
 }
 
 StaticAutoPtr<nsTArray<nsString>> nsDOMDeviceStorage::sVolumeNameCache;
 
 // static
 void nsDOMDeviceStorage::InvalidateVolumeCaches()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -3500,17 +3030,30 @@ void nsDOMDeviceStorage::InvalidateVolum
   // calls this function any time it detects a volume was added or removed.
 
   sVolumeNameCache = nullptr;
 }
 
 // static
 void
 nsDOMDeviceStorage::GetOrderedVolumeNames(
-  nsDOMDeviceStorage::VolumeNameArray &aVolumeNames)
+  const nsAString& aType,
+  nsDOMDeviceStorage::VolumeNameArray& aVolumeNames)
+{
+  if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
+    aVolumeNames.Clear();
+    return;
+  }
+  GetOrderedVolumeNames(aVolumeNames);
+}
+
+// static
+void
+nsDOMDeviceStorage::GetOrderedVolumeNames(
+  nsDOMDeviceStorage::VolumeNameArray& aVolumeNames)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sVolumeNameCache && sVolumeNameCache->Length() > 0) {
     aVolumeNames.AppendElements(*sVolumeNameCache);
     return;
   }
 #ifdef MOZ_WIDGET_GONK
@@ -3549,64 +3092,28 @@ nsDOMDeviceStorage::GetOrderedVolumeName
 
 // static
 void
 nsDOMDeviceStorage::CreateDeviceStorageFor(nsPIDOMWindow* aWin,
                                            const nsAString &aType,
                                            nsDOMDeviceStorage** aStore)
 {
   nsString storageName;
-  if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
-    // The storage name will be the empty string
-    storageName.Truncate();
-  } else {
-    GetDefaultStorageName(aType, storageName);
-  }
+  GetDefaultStorageName(aType, storageName);
 
   nsRefPtr<nsDOMDeviceStorage> ds = new nsDOMDeviceStorage(aWin);
   if (NS_FAILED(ds->Init(aWin, aType, storageName))) {
     *aStore = nullptr;
     return;
   }
   ds.forget(aStore);
 }
 
 // static
 void
-nsDOMDeviceStorage::CreateDeviceStoragesFor(
-  nsPIDOMWindow* aWin,
-  const nsAString &aType,
-  nsTArray<nsRefPtr<nsDOMDeviceStorage> > &aStores)
-{
-  nsresult rv;
-
-  if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
-    nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
-    rv = storage->Init(aWin, aType, EmptyString());
-    if (NS_SUCCEEDED(rv)) {
-      aStores.AppendElement(storage);
-    }
-    return;
-  }
-  VolumeNameArray volNames;
-  GetOrderedVolumeNames(volNames);
-
-  VolumeNameArray::size_type numVolumeNames = volNames.Length();
-  for (VolumeNameArray::index_type i = 0; i < numVolumeNames; i++) {
-    nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
-    rv = storage->Init(aWin, aType, volNames[i]);
-    if (NS_FAILED(rv)) {
-      break;
-    }
-    aStores.AppendElement(storage);
-  }
-}
-
-// static
-void
 nsDOMDeviceStorage::CreateDeviceStorageByNameAndType(
   nsPIDOMWindow* aWin,
   const nsAString& aName,
   const nsAString& aType,
   nsDOMDeviceStorage** aStore)
 {
   if (!DeviceStorageTypeChecker::IsVolumeBased(aType)) {
     nsRefPtr<nsDOMDeviceStorage> storage = new nsDOMDeviceStorage(aWin);
@@ -3623,16 +3130,29 @@ nsDOMDeviceStorage::CreateDeviceStorageB
                                                                  aType);
   if (!storage) {
     *aStore = nullptr;
     return;
   }
   NS_ADDREF(*aStore = storage.get());
 }
 
+bool
+nsDOMDeviceStorage::Equals(nsPIDOMWindow* aWin,
+                           const nsAString& aName,
+                           const nsAString& aType)
+{
+  MOZ_ASSERT(aWin);
+
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  return aWin && aWin == window &&
+         mStorageName.Equals(aName) &&
+         mStorageType.Equals(aType);
+}
+
 // static
 bool
 nsDOMDeviceStorage::ParseFullPath(const nsAString& aFullPath,
                                   nsAString& aOutStorageName,
                                   nsAString& aOutStoragePath)
 {
   aOutStorageName.Truncate();
   aOutStoragePath.Truncate();
@@ -3718,21 +3238,27 @@ nsDOMDeviceStorage::GetStorageByNameAndT
   return nullptr;
 }
 
 // static
 void
 nsDOMDeviceStorage::GetDefaultStorageName(const nsAString& aStorageType,
                                           nsAString& aStorageName)
 {
+  if (!DeviceStorageTypeChecker::IsVolumeBased(aStorageType)) {
+    // The storage name will be the empty string
+    aStorageName.Truncate();
+    return;
+  }
+
   // See if the preferred volume is available.
-  nsAdoptingString prefStorageName =
-    mozilla::Preferences::GetString(PREF_STORAGE_WRITABLE_NAME);
-
-  if (prefStorageName) {
+  nsString prefStorageName;
+  DeviceStorageStatics::GetWritableName(prefStorageName);
+
+  if (!prefStorageName.IsEmpty()) {
     nsString status;
     nsRefPtr<DeviceStorageFile> dsf = new DeviceStorageFile(aStorageType,
                                                             prefStorageName);
     dsf->GetStorageStatus(status);
 
     if (!status.EqualsLiteral("NoMedia")) {
       aStorageName = prefStorageName;
       return;
@@ -3741,17 +3267,17 @@ nsDOMDeviceStorage::GetDefaultStorageNam
 
   // If there is no preferred storage or preferred storage is not presented,
   // we'll use the first one (which should be sdcard).
   VolumeNameArray volNames;
   GetOrderedVolumeNames(volNames);
   if (volNames.Length() > 0) {
     aStorageName = volNames[0];
     // overwrite the value of "device.storage.writable.name"
-    mozilla::Preferences::SetString(PREF_STORAGE_WRITABLE_NAME, aStorageName);
+    DeviceStorageStatics::SetWritableName(aStorageName);
     return;
   }
 
   // No volumes available, return the empty string. This is normal for
   // b2g-desktop.
   aStorageName.Truncate();
 }
 
@@ -4328,28 +3854,28 @@ nsDOMDeviceStorage::EnumerateInternal(co
                                                           EmptyString());
   dsf->SetEditable(aEditable);
 
   nsRefPtr<nsDOMDeviceStorageCursor> cursor
     = new nsDOMDeviceStorageCursor(win, mPrincipal, dsf, since);
   nsRefPtr<DeviceStorageCursorRequest> r
     = new DeviceStorageCursorRequest(cursor);
 
-  if (mozilla::Preferences::GetBool("device.storage.prompt.testing", false)) {
+  if (DeviceStorageStatics::IsPromptTesting()) {
     r->Allow(JS::UndefinedHandleValue);
     return cursor.forget();
   }
 
   nsContentPermissionUtils::AskPermission(r, win);
 
   return cursor.forget();
 }
 
 void
-nsDOMDeviceStorage::DispatchDefaultChangeEvent()
+nsDOMDeviceStorage::OnWritableNameChanged()
 {
   nsAdoptingString DefaultLocation;
   GetDefaultStorageName(mStorageType, DefaultLocation);
 
   DeviceStorageChangeEventInit init;
   init.mBubbles = true;
   init.mCancelable = false;
   init.mPath = DefaultLocation;
@@ -4417,88 +3943,68 @@ nsDOMDeviceStorage::DispatchStorageStatu
                                           init);
   event->SetTrusted(true);
 
   bool ignore;
   DispatchEvent(event, &ignore);
 }
 #endif
 
-NS_IMETHODIMP
-nsDOMDeviceStorage::Observe(nsISupports *aSubject,
-                            const char *aTopic,
-                            const char16_t *aData)
+void
+nsDOMDeviceStorage::OnFileWatcherUpdate(const nsCString& aData, DeviceStorageFile* aFile)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  Notify(aData.get(), aFile);
+}
+
+void
+nsDOMDeviceStorage::OnDiskSpaceWatcher(bool aLowDiskSpace)
 {
   MOZ_ASSERT(NS_IsMainThread());
-
-  if (!strcmp(aTopic, kFileWatcherUpdate)) {
-
-    DeviceStorageFile* file = static_cast<DeviceStorageFile*>(aSubject);
-    Notify(NS_ConvertUTF16toUTF8(aData).get(), file);
-    return NS_OK;
+  nsRefPtr<DeviceStorageFile> file =
+    new DeviceStorageFile(mStorageType, mStorageName);
+  if (aLowDiskSpace) {
+    Notify("low-disk-space", file);
+  } else {
+    Notify("available-disk-space", file);
   }
-  if (!strcmp(aTopic, "disk-space-watcher")) {
-    // 'disk-space-watcher' notifications are sent when there is a modification
-    // of a file in a specific location while a low device storage situation
-    // exists or after recovery of a low storage situation. For Firefox OS,
-    // these notifications are specific for apps storage.
-    nsRefPtr<DeviceStorageFile> file =
-      new DeviceStorageFile(mStorageType, mStorageName);
-    if (!NS_strcmp(aData, MOZ_UTF16("full"))) {
-      Notify("low-disk-space", file);
-    } else if (!NS_strcmp(aData, MOZ_UTF16("free"))) {
-      Notify("available-disk-space", file);
-    }
-    return NS_OK;
-  }
-
-  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) &&
-               aData &&
-               nsDependentString(aData).Equals(NS_LITERAL_STRING(PREF_STORAGE_WRITABLE_NAME)))
-  {
-    DispatchDefaultChangeEvent();
-    return NS_OK;
-  }
+}
 
 #ifdef MOZ_WIDGET_GONK
-  else if (!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
-    // We invalidate the used space cache for the volume that actually changed
-    // state.
-    nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
-    if (!vol) {
-      return NS_OK;
-    }
-    nsString volName;
-    vol->GetName(volName);
-
-    DeviceStorageUsedSpaceCache* usedSpaceCache
-      = DeviceStorageUsedSpaceCache::CreateOrGet();
-    MOZ_ASSERT(usedSpaceCache);
-    usedSpaceCache->Invalidate(volName);
-
-    if (!volName.Equals(mStorageName)) {
-      // Not our volume - we can ignore.
-      return NS_OK;
-    }
-
-    nsRefPtr<DeviceStorageFile> dsf(new DeviceStorageFile(mStorageType, mStorageName));
-    nsString status, storageStatus;
-
-    // Get Status (one of "available, unavailable, shared")
-    dsf->GetStatus(status);
-    DispatchStatusChangeEvent(status);
-
-    // Get real volume status (defined in dom/system/gonk/nsIVolume.idl)
-    dsf->GetStorageStatus(storageStatus);
-    DispatchStorageStatusChangeEvent(storageStatus);
-    return NS_OK;
+void
+nsDOMDeviceStorage::OnVolumeStateChanged(nsIVolume* aVolume) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // We invalidate the used space cache for the volume that actually changed
+  // state.
+  nsString volName;
+  aVolume->GetName(volName);
+
+  DeviceStorageUsedSpaceCache* usedSpaceCache
+    = DeviceStorageUsedSpaceCache::CreateOrGet();
+  MOZ_ASSERT(usedSpaceCache);
+  usedSpaceCache->Invalidate(volName);
+
+  if (!volName.Equals(mStorageName)) {
+    // Not our volume - we can ignore.
+    return;
   }
+
+  nsRefPtr<DeviceStorageFile> dsf(new DeviceStorageFile(mStorageType, mStorageName));
+  nsString status, storageStatus;
+
+  // Get Status (one of "available, unavailable, shared")
+  dsf->GetStatus(status);
+  DispatchStatusChangeEvent(status);
+
+  // Get real volume status (defined in dom/system/gonk/nsIVolume.idl)
+  dsf->GetStorageStatus(storageStatus);
+  DispatchStorageStatusChangeEvent(storageStatus);
+}
 #endif
-  return NS_OK;
-}
 
 nsresult
 nsDOMDeviceStorage::Notify(const char* aReason, DeviceStorageFile* aFile)
 {
   if (!mAllowedToWatchFile) {
     return NS_OK;
   }
 
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -34,16 +34,35 @@ class nsPIDOMWindow;
 namespace mozilla {
 class ErrorResult;
 
 namespace dom {
 class Blob;
 } // namespace dom
 } // namespace mozilla
 
+//#define DS_LOGGING 1
+
+#ifdef DS_LOGGING
+/* Polyfill __func__ on MSVC to pass to the log. */
+#ifdef _MSC_VER
+#define __func__ __FUNCTION__
+#endif
+
+#define DS_LOG_DEBUG(msg, ...)  printf_stderr("[%s:%d] " msg "\n", __func__, __LINE__, ##__VA_ARGS__)
+#define DS_LOG_INFO DS_LOG_DEBUG
+#define DS_LOG_WARN DS_LOG_DEBUG
+#define DS_LOG_ERROR DS_LOG_DEBUG
+#else
+#define DS_LOG_DEBUG(msg, ...)
+#define DS_LOG_INFO(msg, ...)
+#define DS_LOG_WARN(msg, ...)
+#define DS_LOG_ERROR(msg, ...)
+#endif
+
 #define POST_ERROR_EVENT_FILE_EXISTS                 "NoModificationAllowedError"
 #define POST_ERROR_EVENT_FILE_DOES_NOT_EXIST         "NotFoundError"
 #define POST_ERROR_EVENT_FILE_NOT_ENUMERABLE         "TypeMismatchError"
 #define POST_ERROR_EVENT_PERMISSION_DENIED           "SecurityError"
 #define POST_ERROR_EVENT_ILLEGAL_TYPE                "TypeMismatchError"
 #define POST_ERROR_EVENT_UNKNOWN                     "Unknown"
 
 enum DeviceStorageRequestType {
--- a/dom/filesystem/DeviceStorageFileSystem.cpp
+++ b/dom/filesystem/DeviceStorageFileSystem.cpp
@@ -11,24 +11,25 @@
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FileSystemUtils.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsDeviceStorage.h"
 #include "nsIFile.h"
 #include "nsPIDOMWindow.h"
+#include "nsGlobalWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 DeviceStorageFileSystem::DeviceStorageFileSystem(
   const nsAString& aStorageType,
   const nsAString& aStorageName)
-  : mDeviceStorage(nullptr)
+  : mWindowId(0)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
 
   mStorageType = aStorageType;
   mStorageName = aStorageName;
 
   // Generate the string representation of the file system.
   mString.AppendLiteral("devicestorage-");
@@ -71,35 +72,35 @@ DeviceStorageFileSystem::~DeviceStorageF
 {
 }
 
 void
 DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
   MOZ_ASSERT(aDeviceStorage);
-  mDeviceStorage = aDeviceStorage;
+  nsCOMPtr<nsPIDOMWindow> window = aDeviceStorage->GetOwner();
+  MOZ_ASSERT(window->IsInnerWindow());
+  mWindowId = window->WindowID();
 }
 
 void
 DeviceStorageFileSystem::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  mDeviceStorage = nullptr;
   mShutdown = true;
 }
 
 nsPIDOMWindow*
 DeviceStorageFileSystem::GetWindow() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
-  if (!mDeviceStorage) {
-    return nullptr;
-  }
-  return mDeviceStorage->GetOwner();
+  nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
+  MOZ_ASSERT_IF(!mShutdown, window);
+  return window;
 }
 
 void
 DeviceStorageFileSystem::GetRootName(nsAString& aRetval) const
 {
   aRetval = mStorageName;
 }
 
--- a/dom/filesystem/DeviceStorageFileSystem.h
+++ b/dom/filesystem/DeviceStorageFileSystem.h
@@ -43,15 +43,15 @@ public:
   IsSafeDirectory(Directory* aDir) const override;
 private:
   virtual
   ~DeviceStorageFileSystem();
 
   nsString mStorageType;
   nsString mStorageName;
 
-  nsDOMDeviceStorage* mDeviceStorage;
+  uint64_t mWindowId;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DeviceStorageFileSystem_h
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -837,19 +837,16 @@ ContentChild::InitXPCOM()
         if (!JS_ReadStructuredClone(jsapi.cx(), initialData.data, initialData.dataLength,
                                     JS_STRUCTURED_CLONE_VERSION, &data, nullptr, nullptr)) {
             MOZ_CRASH();
         }
         ProcessGlobal* global = ProcessGlobal::Get();
         global->SetInitialProcessData(data);
     }
 
-    DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
-    NS_ASSERTION(observer, "FileUpdateDispatcher is null");
-
     // This object is held alive by the observer service.
     nsRefPtr<SystemMessageHandledObserver> sysMsgObserver =
         new SystemMessageHandledObserver();
     sysMsgObserver->Init();
 
     InitOnContentProcessCreated();
 }
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1454,19 +1454,16 @@ ContentParent::Init()
 
 #ifdef ACCESSIBILITY
     // If accessibility is running in chrome process then start it in content
     // process.
     if (nsIPresShell::IsAccessibilityActive()) {
         unused << SendActivateA11y();
     }
 #endif
-
-    DebugOnly<FileUpdateDispatcher*> observer = FileUpdateDispatcher::GetSingleton();
-    NS_ASSERTION(observer, "FileUpdateDispatcher is null");
 }
 
 void
 ContentParent::ForwardKnownInfo()
 {
     MOZ_ASSERT(mMetamorphosed);
     if (!mMetamorphosed) {
         return;
--- a/dom/system/gonk/DataCallInterfaceService.js
+++ b/dom/system/gonk/DataCallInterfaceService.js
@@ -66,17 +66,18 @@ DataCall.prototype = {
   failCause: Ci.nsIDataCallInterface.DATACALL_FAIL_NONE,
   suggestedRetryTime: -1,
   cid: -1,
   active: -1,
   pdpType: -1,
   ifname: null,
   addreses: null,
   dnses: null,
-  gateways: null
+  gateways: null,
+  pcscf: null
 };
 
 function DataCallInterfaceService() {
   this._dataCallInterfaces = [];
 
   let numClients = gRil.numRadioInterfaces;
   for (let i = 0; i < numClients; i++) {
     this._dataCallInterfaces.push(new DataCallInterface(i));
--- a/dom/system/gonk/DataCallManager.js
+++ b/dom/system/gonk/DataCallManager.js
@@ -955,17 +955,18 @@ function DataCall(aClientId, aApnSetting
     protocol: aApnSetting.protocol,
     roaming_protocol: aApnSetting.roaming_protocol
   };
   this.linkInfo = {
     cid: null,
     ifname: null,
     addresses: [],
     dnses: [],
-    gateways: []
+    gateways: [],
+    pcscf: []
   };
   this.state = NETWORK_STATE_UNKNOWN;
   this.requestedNetworkIfaces = [];
 }
 DataCall.prototype = {
   /**
    * Standard values for the APN connection retry process
    * Retry funcion: time(secs) = A * numer_of_retries^2 + B
@@ -1086,16 +1087,17 @@ DataCall.prototype = {
       this.deactivate();
       return;
     }
 
     this.linkInfo.ifname = aDataCall.ifname;
     this.linkInfo.addresses = aDataCall.addresses ? aDataCall.addresses.split(" ") : [];
     this.linkInfo.gateways = aDataCall.gateways ? aDataCall.gateways.split(" ") : [];
     this.linkInfo.dnses = aDataCall.dnses ? aDataCall.dnses.split(" ") : [];
+    this.linkInfo.pcscf = aDataCall.pcscf ? aDataCall.pcscf.split(" ") : [];
     this.state = this._getGeckoDataCallState(aDataCall);
 
     // Notify DataCallHandler about data call connected.
     this.dataCallHandler.notifyDataCallChanged(this);
 
     for (let i = 0; i < this.requestedNetworkIfaces.length; i++) {
       this.requestedNetworkIfaces[i].notifyRILNetworkInterface();
     }
@@ -1138,17 +1140,18 @@ DataCall.prototype = {
         dataCallState != NETWORK_STATE_CONNECTED) {
       return;
     }
 
     let newLinkInfo = {
       ifname: aUpdatedDataCall.ifname,
       addresses: aUpdatedDataCall.addresses ? aUpdatedDataCall.addresses.split(" ") : [],
       dnses: aUpdatedDataCall.dnses ? aUpdatedDataCall.dnses.split(" ") : [],
-      gateways: aUpdatedDataCall.gateways ? aUpdatedDataCall.gateways.split(" ") : []
+      gateways: aUpdatedDataCall.gateways ? aUpdatedDataCall.gateways.split(" ") : [],
+      pcscf: aUpdatedDataCall.pcscf ? aUpdatedDataCall.pcscf.split(" ") : []
     };
 
     switch (dataCallState) {
       case NETWORK_STATE_CONNECTED:
         if (this.state == NETWORK_STATE_CONNECTED) {
           let result =
             this._compareDataCallLink(newLinkInfo, this.linkInfo);
 
@@ -1164,16 +1167,17 @@ DataCall.prototype = {
           // Minor change, just update and notify.
           if (DEBUG) {
             this.debug("Data link minor change, just update and notify.");
           }
 
           this.linkInfo.addresses = newLinkInfo.addresses.slice();
           this.linkInfo.gateways = newLinkInfo.gateways.slice();
           this.linkInfo.dnses = newLinkInfo.dnses.slice();
+          this.linkInfo.pcscf = newLinkInfo.pcscf.slice();
         }
         break;
       case NETWORK_STATE_DISCONNECTED:
       case NETWORK_STATE_UNKNOWN:
         if (this.state == NETWORK_STATE_CONNECTED) {
           // Notify first on unexpected data call disconnection.
           this.state = dataCallState;
           for (let i = 0; i < this.requestedNetworkIfaces.length; i++) {
@@ -1262,16 +1266,17 @@ DataCall.prototype = {
   },
 
   resetLinkInfo: function() {
     this.linkInfo.cid = null;
     this.linkInfo.ifname = null;
     this.linkInfo.addresses = [];
     this.linkInfo.dnses = [];
     this.linkInfo.gateways = [];
+    this.linkInfo.pcscf = [];
   },
 
   reset: function() {
     this.resetLinkInfo();
 
     this.state = NETWORK_STATE_UNKNOWN;
   },
 
@@ -1599,16 +1604,30 @@ RILNetworkInfo.prototype = {
       if (DEBUG) this.debug("Error! Only MMS network can get MMS port.");
       throw Cr.NS_ERROR_UNEXPECTED;
     }
 
     // Note: Port 0 is reserved, so we treat it as invalid as well.
     // See http://www.iana.org/assignments/port-numbers
     return this.getApnSetting().mmsport || -1;
   },
+
+  getPcscf: function(aCount) {
+    if (this.type != NETWORK_TYPE_MOBILE_IMS) {
+      if (DEBUG) this.debug("Error! Only IMS network can get pcscf.");
+      throw Cr.NS_ERROR_UNEXPECTED;
+    }
+
+    let linkInfo = this.getDataCall().linkInfo;
+
+    if (aCount) {
+      aCount.value = linkInfo.pcscf.length;
+    }
+    return linkInfo.pcscf.slice();
+  },
 };
 
 function RILNetworkInterface(aDataCallHandler, aType, aApnSetting, aDataCall) {
   if (!aDataCall) {
     throw new Error("No dataCall for RILNetworkInterface: " + type);
   }
 
   this.dataCallHandler = aDataCallHandler;
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -387,17 +387,18 @@ DataCall.prototype = {
   failCause: Ci.nsIDataCallInterface.DATACALL_FAIL_NONE,
   suggestedRetryTime: -1,
   cid: -1,
   active: -1,
   pdpType: -1,
   ifname: null,
   addreses: null,
   dnses: null,
-  gateways: null
+  gateways: null,
+  pcscf: null
 };
 
 function RadioInterfaceLayer() {
   let workerMessenger = new WorkerMessenger();
   workerMessenger.init();
   this.setWorkerDebugFlag = workerMessenger.setDebugFlag.bind(workerMessenger);
 
   let numIfaces = this.numRadioInterfaces;
--- a/dom/system/gonk/nsIDataCallInterfaceService.idl
+++ b/dom/system/gonk/nsIDataCallInterfaceService.idl
@@ -1,15 +1,15 @@
 /* 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 "nsISupports.idl"
 
-[scriptable, uuid(d27ce247-9c7c-4582-826b-125e8275e9c2)]
+[scriptable, uuid(88f18811-8f19-4902-a9b8-2a6430c71c94)]
 interface nsIDataCall : nsISupports
 {
   /**
    * Data call fail cause. One of the nsIDataCallInterface.DATACALL_FAIL_*
    * values.
    */
   readonly attribute long failCause;
 
@@ -50,16 +50,22 @@ interface nsIDataCall : nsISupports
    * A space-delimited list of DNS server addresses.
    */
   readonly attribute DOMString dnses;
 
   /**
    * A space-delimited list of default gateway addresses.
    */
   readonly attribute DOMString gateways;
+
+  /**
+   * A space-delimited list of Proxy Call State Control Function addresses for
+   * IMS client.
+   */
+  readonly attribute DOMString pcscf;
 };
 
 [scriptable, uuid(e119c54b-9354-4ad6-a1ee-18608bde9320)]
 interface nsIDataCallInterfaceListener : nsISupports
 {
   /**
    * Notify data call interface listeners about unsolicited data call state
    * changes.
--- a/dom/system/gonk/nsIDataCallManager.idl
+++ b/dom/system/gonk/nsIDataCallManager.idl
@@ -1,25 +1,36 @@
 /* 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 "nsISupports.idl"
 #include "nsINetworkInterface.idl"
 
-[scriptable, uuid(501b7041-0754-4ddb-9174-946e2c2ebd83)]
+[scriptable, uuid(b8bcd6aa-5b06-4362-a68c-317878429e51)]
 interface nsIRilNetworkInfo : nsINetworkInfo
 {
   readonly attribute unsigned long serviceId;
   readonly attribute DOMString iccId;
 
   /* The following attributes are for MMS proxy settings. */
   readonly attribute DOMString mmsc;     // Empty string if not set.
   readonly attribute DOMString mmsProxy; // Empty string if not set.
   readonly attribute long      mmsPort;  // -1 if not set.
+
+  /**
+   * Get the list of pcscf addresses, could be IPv4 or IPv6.
+   *
+   * @param count
+   *        The length of the list of pcscf addresses.
+   *
+   * @returns the list of pcscf addresses.
+   */
+  void getPcscf([optional] out unsigned long count,
+                [array, size_is(count), retval] out wstring pcscf);
 };
 
 [scriptable, function, uuid(cb2f0f5b-67f4-4c14-93e8-01e66b630464)]
 interface nsIDeactivateDataCallsCallback : nsISupports
 {
   /**
    * Callback function used to notify when all data calls are disconnected.
    */
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -4231,22 +4231,21 @@ RilObject.prototype[REQUEST_SEND_SMS_EXP
 
 RilObject.prototype[REQUEST_SETUP_DATA_CALL] = function REQUEST_SETUP_DATA_CALL(length, options) {
   if (options.errorMsg) {
     this.sendChromeMessage(options);
     return;
   }
 
   let Buf = this.context.Buf;
-  // Skip version of data call.
-  Buf.readInt32();
+  let version = Buf.readInt32();
   // Skip number of data calls.
   Buf.readInt32();
 
-  this.readDataCall_v6(options);
+  this.readDataCall(options, version);
   this.sendChromeMessage(options);
 };
 RilObject.prototype[REQUEST_SIM_IO] = function REQUEST_SIM_IO(length, options) {
   if (options.errorMsg) {
     if (options.onerror) {
       options.onerror(options.errorMsg);
     }
     return;
@@ -4507,33 +4506,41 @@ RilObject.prototype[REQUEST_QUERY_CLIP] 
 RilObject.prototype[REQUEST_LAST_DATA_CALL_FAIL_CAUSE] = null;
 
 /**
  * V6:
  *  # addresses - A space-delimited list of addresses with optional "/" prefix
  *                length.
  *  # dnses     - A space-delimited list of DNS server addresses.
  *  # gateways  - A space-delimited list of default gateway addresses.
+ *
+ * V10:
+ *  # pcscf     - A space-delimited list of Proxy Call State Control Function
+ *                addresses.
  */
 
-RilObject.prototype.readDataCall_v6 = function(options) {
+RilObject.prototype.readDataCall = function(options, version) {
   if (!options) {
     options = {};
   }
   let Buf = this.context.Buf;
   options.failCause = Buf.readInt32();  // DATACALL_FAIL_*
   options.suggestedRetryTime = Buf.readInt32();
   options.cid = Buf.readInt32().toString();
   options.active = Buf.readInt32();  // DATACALL_ACTIVE_*
   options.type = Buf.readString();
   options.ifname = Buf.readString();
   options.addresses = Buf.readString();
   options.dnses = Buf.readString();
   options.gateways = Buf.readString();
 
+  if (version >= 10) {
+    options.pcscf = Buf.readString();
+  }
+
   return options;
 };
 
 RilObject.prototype[REQUEST_DATA_CALL_LIST] = function REQUEST_DATA_CALL_LIST(length, options) {
   if (options.errorMsg) {
     if (options.rilMessageType) {
       this.sendChromeMessage(options);
     }
@@ -4552,17 +4559,17 @@ RilObject.prototype[REQUEST_DATA_CALL_LI
   }
 
   let Buf = this.context.Buf;
   let version = Buf.readInt32();
   let num = Buf.readInt32();
   let datacalls = [];
   for (let i = 0; i < num; i++) {
     let datacall;
-    datacall = this.readDataCall_v6();
+    datacall = this.readDataCall({}, version);
     datacalls.push(datacall);
   }
 
   options.datacalls = datacalls;
   this.sendChromeMessage(options);
 };
 RilObject.prototype[REQUEST_RESET_RADIO] = null;
 RilObject.prototype[REQUEST_OEM_HOOK_RAW] = null;
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -131,16 +131,17 @@ using namespace mozilla::system;
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/IMEStateManager.h"
 #include "nsDocument.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "CameraPreferences.h"
 #include "TouchManager.h"
 #include "MediaDecoder.h"
 #include "mozilla/layers/CompositorLRU.h"
+#include "mozilla/dom/devicestorage/DeviceStorageStatics.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
 nsrefcnt nsLayoutStatics::sLayoutStaticRefcnt = 0;
 
@@ -314,16 +315,18 @@ nsLayoutStatics::Initialize()
 #endif
 
   MediaDecoder::InitStatics();
 
   PromiseDebugging::Init();
 
   layers::CompositorLRU::Init();
 
+  mozilla::dom::devicestorage::DeviceStorageStatics::Initialize();
+
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
 {
   // Don't need to shutdown nsWindowMemoryReporter, that will be done by the
   // memory reporter manager.