Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 10 Aug 2015 09:46:20 -0400
changeset 257121 86b3b49cae3712b44f0474d9acd8be0ecd85a9b5
parent 257120 2fcd73fa8a7e713719ae9ce2c04cc6a458d7735a (current diff)
parent 257066 c07833808ff1098d9eec76397d4f17881e6c2222 (diff)
child 257122 93338c9fb309dd687dab0eb673783382d21f044a
push id63526
push userkwierso@gmail.com
push dateMon, 10 Aug 2015 22:02:03 +0000
treeherdermozilla-inbound@c4ab17503c09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <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="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="1eb9029d752b12c3bacb6399bd78c2c63dfdda3c"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <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="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="1eb9029d752b12c3bacb6399bd78c2c63dfdda3c"/>
--- 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="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <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="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1eb9029d752b12c3bacb6399bd78c2c63dfdda3c"/>
   <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
@@ -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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <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="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="1eb9029d752b12c3bacb6399bd78c2c63dfdda3c"/>
--- 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="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <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="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="1eb9029d752b12c3bacb6399bd78c2c63dfdda3c"/>
--- 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="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <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="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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
@@ -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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <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="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="1eb9029d752b12c3bacb6399bd78c2c63dfdda3c"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f", 
+        "git_revision": "fa89e03dc489e79baa0e74cb1d205260c7924caa", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "27c4198462ca807ccff1e51229a77830930eb182", 
+    "revision": "0224f3b60c5bb9d2dcae2d6bc0f125113bbd934f", 
     "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="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="62cfa11ae7d77f6330de019a5aa79607e35be7d1"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="1eb9029d752b12c3bacb6399bd78c2c63dfdda3c"/>
   <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="09dea2d5ff21cdb56da35fe4aa5bf4c90cf1da7f"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="fa89e03dc489e79baa0e74cb1d205260c7924caa"/>
   <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="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <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="1eb9029d752b12c3bacb6399bd78c2c63dfdda3c"/>
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -61,15 +61,8 @@ MOZ_WEBGL_CONFORMANT=1
 MOZ_PAY=1
 # Enable activities. These are used for FxOS developers currently.
 MOZ_ACTIVITIES=1
 MOZ_JSDOWNLOADS=1
 MOZ_WEBM_ENCODER=1
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
-if test "$MOZ_OFFICIAL_BRANDING"; then
-  if test "$MOZ_UPDATE_CHANNEL" = "beta" -o \
-          "$MOZ_UPDATE_CHANNEL" = "release" -o \
-          "$MOZ_UPDATE_CHANNEL" = "esr"; then
-    MOZ_REQUIRE_SIGNING=1
-  fi
-fi
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -45,29 +45,16 @@ public:
 
   ASTConsumerPtr makeASTConsumer() {
     return astMatcher.newASTConsumer();
   }
 
 private:
   class ScopeChecker : public MatchFinder::MatchCallback {
   public:
-    enum Scope {
-      eLocal,
-      eGlobal
-    };
-    ScopeChecker(Scope scope_) :
-      scope(scope_) {}
-    virtual void run(const MatchFinder::MatchResult &Result);
-  private:
-    Scope scope;
-  };
-
-  class NonHeapClassChecker : public MatchFinder::MatchCallback {
-  public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
   class ArithmeticArgChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
@@ -111,19 +98,17 @@ private:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
   class ExplicitImplicitChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
-  ScopeChecker stackClassChecker;
-  ScopeChecker globalClassChecker;
-  NonHeapClassChecker nonheapClassChecker;
+  ScopeChecker scopeChecker;
   ArithmeticArgChecker arithmeticArgChecker;
   TrivialCtorDtorChecker trivialCtorDtorChecker;
   NaNExprChecker nanExprChecker;
   NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
   RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
   ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
   NoDuplicateRefCntMemberChecker noDuplicateRefCntMemberChecker;
   NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
@@ -272,16 +257,18 @@ private:
 };
 
 static CustomTypeAnnotation StackClass =
   CustomTypeAnnotation("moz_stack_class", "stack");
 static CustomTypeAnnotation GlobalClass =
   CustomTypeAnnotation("moz_global_class", "global");
 static CustomTypeAnnotation NonHeapClass =
   CustomTypeAnnotation("moz_nonheap_class", "non-heap");
+static CustomTypeAnnotation HeapClass =
+  CustomTypeAnnotation("moz_heap_class", "heap");
 static CustomTypeAnnotation MustUse =
   CustomTypeAnnotation("moz_must_use", "must-use");
 
 class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
   DiagnosticsEngine &Diag;
   const CompilerInstance &CI;
   DiagnosticsMatcher matcher;
 public:
@@ -609,34 +596,16 @@ bool typeHasVTable(QualType T) {
   return offender && offender->hasDefinition() && offender->isDynamicClass();
 }
 
 }
 
 namespace clang {
 namespace ast_matchers {
 
-/// This matcher will match any class with the stack class assertion or an
-/// array of such classes.
-AST_MATCHER(QualType, stackClassAggregate) {
-  return StackClass.hasEffectiveAnnotation(Node);
-}
-
-/// This matcher will match any class with the global class assertion or an
-/// array of such classes.
-AST_MATCHER(QualType, globalClassAggregate) {
-  return GlobalClass.hasEffectiveAnnotation(Node);
-}
-
-/// This matcher will match any class with the stack class assertion or an
-/// array of such classes.
-AST_MATCHER(QualType, nonheapClassAggregate) {
-  return NonHeapClass.hasEffectiveAnnotation(Node);
-}
-
 /// This matcher will match any function declaration that is declared as a heap
 /// allocator.
 AST_MATCHER(FunctionDecl, heapAllocator) {
   return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator");
 }
 
 /// This matcher will match any declaration that is marked as not accepting
 /// arithmetic expressions in its arguments.
@@ -941,64 +910,33 @@ CustomTypeAnnotation::AnnotationReason C
     }
   }
 
   AnnotationReason Reason = { QualType(), RK_None, nullptr };
   Cache[Key] = Reason;
   return Reason;
 }
 
-bool isPlacementNew(const CXXNewExpr *expr) {
+bool isPlacementNew(const CXXNewExpr *Expr) {
   // Regular new expressions aren't placement new
-  if (expr->getNumPlacementArgs() == 0)
+  if (Expr->getNumPlacementArgs() == 0)
     return false;
-  if (MozChecker::hasCustomAnnotation(expr->getOperatorNew(),
-      "moz_heap_allocator"))
+  const FunctionDecl *Decl = Expr->getOperatorNew();
+  if (Decl && MozChecker::hasCustomAnnotation(Decl, "moz_heap_allocator")) {
     return false;
+  }
   return true;
 }
 
-DiagnosticsMatcher::DiagnosticsMatcher()
-  : stackClassChecker(ScopeChecker::eLocal),
-    globalClassChecker(ScopeChecker::eGlobal)
-{
-  // Stack class assertion: non-local variables of a stack class are forbidden
-  // (non-localness checked in the callback)
-  astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"),
-    &stackClassChecker);
-  // Stack class assertion: new stack class is forbidden (unless placement new)
-  astMatcher.addMatcher(newExpr(hasType(pointerType(
-      pointee(stackClassAggregate())
-    ))).bind("node"), &stackClassChecker);
-  // Global class assertion: non-global variables of a global class are forbidden
-  // (globalness checked in the callback)
-  astMatcher.addMatcher(varDecl(hasType(globalClassAggregate())).bind("node"),
-    &globalClassChecker);
-  // Global class assertion: new global class is forbidden
-  astMatcher.addMatcher(newExpr(hasType(pointerType(
-      pointee(globalClassAggregate())
-    ))).bind("node"), &globalClassChecker);
-  // Non-heap class assertion: new non-heap class is forbidden (unless placement
-  // new)
-  astMatcher.addMatcher(newExpr(hasType(pointerType(
-      pointee(nonheapClassAggregate())
-    ))).bind("node"), &nonheapClassChecker);
-
-  // Any heap allocation function that returns a non-heap or a stack class or
-  // a global class is definitely doing something wrong
-  astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
-      returns(pointerType(pointee(nonheapClassAggregate()))))))).bind("node"),
-    &nonheapClassChecker);
-  astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
-      returns(pointerType(pointee(stackClassAggregate()))))))).bind("node"),
-    &stackClassChecker);
-
-  astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
-      returns(pointerType(pointee(globalClassAggregate()))))))).bind("node"),
-    &globalClassChecker);
+DiagnosticsMatcher::DiagnosticsMatcher() {
+  astMatcher.addMatcher(varDecl().bind("node"), &scopeChecker);
+  astMatcher.addMatcher(newExpr().bind("node"), &scopeChecker);
+  astMatcher.addMatcher(materializeTemporaryExpr().bind("node"), &scopeChecker);
+  astMatcher.addMatcher(callExpr(callee(functionDecl(heapAllocator()))).bind("node"),
+                        &scopeChecker);
 
   astMatcher.addMatcher(callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()),
           anyOf(
               hasDescendant(binaryOperator(allOf(binaryArithmeticOperator(),
                   hasLHS(hasDescendant(declRefExpr())),
                   hasRHS(hasDescendant(declRefExpr()))
               )).bind("node")),
               hasDescendant(unaryOperator(allOf(unaryArithmeticOperator(),
@@ -1082,86 +1020,147 @@ DiagnosticsMatcher::DiagnosticsMatcher()
 
   astMatcher.addMatcher(
       constructorDecl(isInterestingImplicitCtor(),
                       ofClass(allOf(isConcreteClass(), decl().bind("class"))),
                       unless(isMarkedImplicit())).bind("ctor"),
       &explicitImplicitChecker);
 }
 
+// These enum variants determine whether an allocation has occured in the code.
+enum AllocationVariety {
+  AV_None,
+  AV_Global,
+  AV_Automatic,
+  AV_Temporary,
+  AV_Heap,
+};
+
 void DiagnosticsMatcher::ScopeChecker::run(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
-    DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
-  unsigned globalID = Diag.getDiagnosticIDs()->getCustomDiagID(
-    DiagnosticIDs::Error, "variable of type %0 only valid as global");
 
+  // There are a variety of different reasons why something could be allocated
+  AllocationVariety Variety = AV_None;
   SourceLocation Loc;
   QualType T;
-  if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
-    if (scope == eLocal) {
-      // Ignore the match if it's a local variable.
-      if (d->hasLocalStorage())
-        return;
-    } else if (scope == eGlobal) {
-      // Ignore the match if it's a global variable or a static member of a
-      // class.  The latter is technically not in the global scope, but for the
-      // use case of classes that intend to avoid introducing static
-      // initializers that is fine.
-      if (d->hasGlobalStorage() && !d->isStaticLocal())
-        return;
+
+  // Determine the type of allocation which we detected
+  if (const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node")) {
+    if (D->hasGlobalStorage()) {
+      Variety = AV_Global;
+    } else {
+      Variety = AV_Automatic;
     }
-
-    Loc = d->getLocation();
-    T = d->getType();
-  } else if (const CXXNewExpr *expr =
-      Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
-    // If it's placement new, then this match doesn't count.
-    if (scope == eLocal && isPlacementNew(expr))
-      return;
-
-    Loc = expr->getStartLoc();
-    T = expr->getAllocatedType();
-  } else if (const CallExpr *expr =
-      Result.Nodes.getNodeAs<CallExpr>("node")) {
-    Loc = expr->getLocStart();
-    T = GetCallReturnType(expr)->getPointeeType();
+    T = D->getType();
+    Loc = D->getLocStart();
+  } else if (const CXXNewExpr *E = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
+    // New allocates things on the heap.
+    // We don't consider placement new to do anything, as it doesn't actually
+    // allocate the storage, and thus gives us no useful information.
+    if (!isPlacementNew(E)) {
+      Variety = AV_Heap;
+      T = E->getAllocatedType();
+      Loc = E->getLocStart();
+    }
+  } else if (const Expr *E = Result.Nodes.getNodeAs<MaterializeTemporaryExpr>("node")) {
+    Variety = AV_Temporary;
+    T = E->getType().getUnqualifiedType();
+    Loc = E->getLocStart();
+  } else if (const CallExpr *E = Result.Nodes.getNodeAs<CallExpr>("node")) {
+    T = E->getType()->getPointeeType();
+    if (!T.isNull()) {
+      // This will always allocate on the heap, as the heapAllocator() check
+      // was made in the matcher
+      Variety = AV_Heap;
+      Loc = E->getLocStart();
+    }
   }
 
-  if (scope == eLocal) {
-    Diag.Report(Loc, stackID) << T;
-    StackClass.dumpAnnotationReason(Diag, T, Loc);
-  } else if (scope == eGlobal) {
-    Diag.Report(Loc, globalID) << T;
-    GlobalClass.dumpAnnotationReason(Diag, T, Loc);
-  }
-}
+  // Error messages for incorrect allocations.
+  unsigned StackID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
+  unsigned GlobalID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 only valid as global");
+  unsigned HeapID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 only valid on the heap");
+  unsigned NonHeapID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
 
-void DiagnosticsMatcher::NonHeapClassChecker::run(
-    const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
-    DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
+  unsigned StackNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "value incorrectly allocated in an automatic variable");
+  unsigned GlobalNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "value incorrectly allocated in a global variable");
+  unsigned HeapNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "value incorrectly allocated on the heap");
+  unsigned TemporaryNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "value incorrectly allocated in a temporary");
+
+  // Report errors depending on the annotations on the input types.
+  switch (Variety) {
+  case AV_None:
+    return;
+
+  case AV_Global:
+    if (StackClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, StackID) << T;
+      Diag.Report(Loc, GlobalNoteID);
+      StackClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (HeapClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, HeapID) << T;
+      Diag.Report(Loc, GlobalNoteID);
+      HeapClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    break;
 
-  SourceLocation Loc;
-  QualType T;
-  if (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
-    // If it's placement new, then this match doesn't count.
-    if (isPlacementNew(expr))
-      return;
-    Loc = expr->getLocStart();
-    T = expr->getAllocatedType();
-  } else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) {
-    Loc = expr->getLocStart();
-    T = GetCallReturnType(expr)->getPointeeType();
+  case AV_Automatic:
+    if (GlobalClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, GlobalID) << T;
+      Diag.Report(Loc, StackNoteID);
+      GlobalClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (HeapClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, HeapID) << T;
+      Diag.Report(Loc, StackNoteID);
+      HeapClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    break;
+
+  case AV_Temporary:
+    if (GlobalClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, GlobalID) << T;
+      Diag.Report(Loc, TemporaryNoteID);
+      GlobalClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (HeapClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, HeapID) << T;
+      Diag.Report(Loc, TemporaryNoteID);
+      HeapClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    break;
+
+  case AV_Heap:
+    if (GlobalClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, GlobalID) << T;
+      Diag.Report(Loc, HeapNoteID);
+      GlobalClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (StackClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, StackID) << T;
+      Diag.Report(Loc, HeapNoteID);
+      StackClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (NonHeapClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, NonHeapID) << T;
+      Diag.Report(Loc, HeapNoteID);
+      NonHeapClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    break;
   }
-
-  Diag.Report(Loc, stackID) << T;
-  NonHeapClass.dumpAnnotationReason(Diag, T, Loc);
 }
 
 void DiagnosticsMatcher::ArithmeticArgChecker::run(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Error, "cannot pass an arithmetic expression of built-in types to %0");
   const Expr *expr = Result.Nodes.getNodeAs<Expr>("node");
--- a/build/clang-plugin/tests/TestCustomHeap.cpp
+++ b/build/clang-plugin/tests/TestCustomHeap.cpp
@@ -18,11 +18,11 @@ void *operator new(size_t x, int qual) M
 template <typename T>
 T *customAlloc() MOZ_HEAP_ALLOCATOR {
   T *arg =  static_cast<T*>(malloc(sizeof(T)));
   return new (arg) T();
 }
 
 template <typename T>
 void misuseX(T q) {
-  X *foo = customAlloc<X>(); // expected-error {{variable of type 'X' is not valid on the heap}}
-  X *foo2 = new (100) X(); // expected-error {{variable of type 'X' is not valid on the heap}}
+  X *foo = customAlloc<X>(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  X *foo2 = new (100) X(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
 }
--- a/build/clang-plugin/tests/TestGlobalClass.cpp
+++ b/build/clang-plugin/tests/TestGlobalClass.cpp
@@ -10,43 +10,43 @@ struct MOZ_GLOBAL_CLASS Global {
 template <class T>
 struct MOZ_GLOBAL_CLASS TemplateClass {
   T i;
 };
 
 void gobble(void *) { }
 
 void misuseGlobalClass(int len) {
-  Global notValid; // expected-error {{variable of type 'Global' only valid as global}}
-  Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}}
-  static Global notValid2; // expected-error {{variable of type 'Global' only valid as global}}
-  static Global alsoNotValid2[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}}
+  Global notValid; // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}}
+  Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}} expected-note {{value incorrectly allocated in an automatic variable}}
+  static Global valid;
+  static Global alsoValid[2];
 
-  gobble(&notValid2);
   gobble(&notValid);
-  gobble(&alsoNotValid2[0]);
+  gobble(&valid);
+  gobble(&alsoValid[0]);
 
-  gobble(new Global); // expected-error {{variable of type 'Global' only valid as global}}
-  gobble(new Global[10]); // expected-error {{variable of type 'Global' only valid as global}}
-  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid as global}}
-  gobble(len <= 5 ? &notValid2 : new Global); // expected-error {{variable of type 'Global' only valid as global}}
+  gobble(new Global); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new Global[10]); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid as global}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(len <= 5 ? &valid : new Global); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}}
 
   char buffer[sizeof(Global)];
-  gobble(new (buffer) Global); // expected-error {{variable of type 'Global' only valid as global}}
+  gobble(new (buffer) Global);
 }
 
 Global valid;
 struct RandomClass {
   Global nonstaticMember; // expected-note {{'RandomClass' is a global type because member 'nonstaticMember' is a global type 'Global'}}
   static Global staticMember;
 };
 struct MOZ_GLOBAL_CLASS RandomGlobalClass {
   Global nonstaticMember;
   static Global staticMember;
 };
 
 struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global type because it inherits from a global type 'Global'}}
 struct MOZ_GLOBAL_CLASS GoodInherit : Global {};
 
 void misuseGlobalClassEvenMore(int len) {
-  BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid as global}}
-  RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid as global}}
+  BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}}
+  RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}}
 }
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestHeapClass.cpp
@@ -0,0 +1,64 @@
+#define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class")))
+#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
+
+#include <stddef.h>
+
+struct MOZ_HEAP_CLASS Heap {
+  int i;
+  Heap() {}
+  MOZ_IMPLICIT Heap(int i) {}
+  Heap(int i, int j) {}
+  void *operator new(size_t x) throw() { return 0; }
+  void *operator new(size_t blah, char *buffer) { return buffer; }
+};
+
+template <class T>
+struct MOZ_HEAP_CLASS TemplateClass {
+  T i;
+};
+
+void gobble(void *) { }
+
+void gobbleref(const Heap&) { }
+
+void misuseHeapClass(int len) {
+  Heap invalid; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}}
+  Heap alsoInvalid[2]; // expected-error {{variable of type 'Heap [2]' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} expected-note {{'Heap [2]' is a heap type because it is an array of heap type 'Heap'}}
+  static Heap invalidStatic; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}}
+  static Heap alsoInvalidStatic[2]; // expected-error {{variable of type 'Heap [2]' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} expected-note {{'Heap [2]' is a heap type because it is an array of heap type 'Heap'}}
+
+  gobble(&invalid);
+  gobble(&invalidStatic);
+  gobble(&alsoInvalid[0]);
+
+  gobbleref(Heap()); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(Heap(10, 20)); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(Heap(10)); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(10); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}}
+
+  gobble(new Heap);
+  gobble(new Heap[10]);
+  gobble(new TemplateClass<int>);
+  gobble(len <= 5 ? &invalid : new Heap);
+
+  char buffer[sizeof(Heap)];
+  gobble(new (buffer) Heap);
+}
+
+Heap invalidStatic; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}}
+struct RandomClass {
+  Heap nonstaticMember; // expected-note {{'RandomClass' is a heap type because member 'nonstaticMember' is a heap type 'Heap'}}
+  static Heap staticMember; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}}
+};
+struct MOZ_HEAP_CLASS RandomHeapClass {
+  Heap nonstaticMember;
+  static Heap staticMember; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}}
+};
+
+struct BadInherit : Heap {}; // expected-note {{'BadInherit' is a heap type because it inherits from a heap type 'Heap'}}
+struct MOZ_HEAP_CLASS GoodInherit : Heap {};
+
+void useStuffWrongly() {
+  BadInherit i; // expected-error {{variable of type 'BadInherit' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}}
+  RandomClass r; // expected-error {{variable of type 'RandomClass' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}}
+}
--- a/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp
+++ b/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp
@@ -5,13 +5,13 @@
 class Normal {};
 class MOZ_STACK_CLASS Stack {};
 class IndirectStack : Stack {}; // expected-note {{'IndirectStack' is a stack type because it inherits from a stack type 'Stack'}}
 
 template<class T>
 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Template {}; // expected-note 2 {{'Template<Stack>' is a stack type because it has a template argument stack type 'Stack'}} expected-note {{'Template<IndirectStack>' is a stack type because it has a template argument stack type 'IndirectStack'}}
 class IndirectTemplate : Template<Stack> {}; // expected-note {{'IndirectTemplate' is a stack type because it inherits from a stack type 'Template<Stack>'}}
 
-static Template<Stack> a; // expected-error {{variable of type 'Template<Stack>' only valid on the stack}}
-static Template<IndirectStack> b; // expected-error {{variable of type 'Template<IndirectStack>' only valid on the stack}}
+static Template<Stack> a; // expected-error {{variable of type 'Template<Stack>' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
+static Template<IndirectStack> b; // expected-error {{variable of type 'Template<IndirectStack>' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 static Template<Normal> c;
-static IndirectTemplate d; // expected-error {{variable of type 'IndirectTemplate' only valid on the stack}}
+static IndirectTemplate d; // expected-error {{variable of type 'IndirectTemplate' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 
--- a/build/clang-plugin/tests/TestMultipleAnnotations.cpp
+++ b/build/clang-plugin/tests/TestMultipleAnnotations.cpp
@@ -1,14 +1,14 @@
 #define MOZ_MUST_USE __attribute__((annotate("moz_must_use")))
 #define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
 
 class MOZ_MUST_USE MOZ_STACK_CLASS TestClass {};
 
-TestClass foo; // expected-error {{variable of type 'TestClass' only valid on the stack}}
+TestClass foo; // expected-error {{variable of type 'TestClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 
 TestClass f()
 {
   TestClass bar;
   return bar;
 }
 
 void g()
--- a/build/clang-plugin/tests/TestNonHeapClass.cpp
+++ b/build/clang-plugin/tests/TestNonHeapClass.cpp
@@ -20,20 +20,20 @@ void misuseNonHeapClass(int len) {
   NonHeap alsoValid[2];
   static NonHeap validStatic;
   static NonHeap alsoValidStatic[2];
 
   gobble(&valid);
   gobble(&validStatic);
   gobble(&alsoValid[0]);
 
-  gobble(new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}}
-  gobble(new NonHeap[10]); // expected-error {{variable of type 'NonHeap' is not valid on the heap}}
-  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' is not valid on the heap}}
-  gobble(len <= 5 ? &valid : new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}}
+  gobble(new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new NonHeap[10]); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(len <= 5 ? &valid : new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
 
   char buffer[sizeof(NonHeap)];
   gobble(new (buffer) NonHeap);
 }
 
 NonHeap validStatic;
 struct RandomClass {
   NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap type because member 'nonstaticMember' is a non-heap type 'NonHeap'}}
@@ -43,20 +43,20 @@ struct MOZ_NONHEAP_CLASS RandomNonHeapCl
   NonHeap nonstaticMember;
   static NonHeap staticMember;
 };
 
 struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap type because it inherits from a non-heap type 'NonHeap'}}
 struct MOZ_NONHEAP_CLASS GoodInherit : NonHeap {};
 
 void useStuffWrongly() {
-  gobble(new BadInherit); // expected-error {{variable of type 'BadInherit' is not valid on the heap}}
-  gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}}
+  gobble(new BadInherit); // expected-error {{variable of type 'BadInherit' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
 }
 
 // Stack class overrides non-heap typees.
 struct MOZ_STACK_CLASS StackClass {};
 struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit {
   NonHeap nonstaticMember;
   StackClass stackClass; // expected-note {{'InferredStackClass' is a stack type because member 'stackClass' is a stack type 'StackClass'}}
 };
 
-InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}}
+InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
--- a/build/clang-plugin/tests/TestStackClass.cpp
+++ b/build/clang-plugin/tests/TestStackClass.cpp
@@ -12,39 +12,39 @@ struct MOZ_STACK_CLASS TemplateClass {
   T i;
 };
 
 void gobble(void *) { }
 
 void misuseStackClass(int len) {
   Stack valid;
   Stack alsoValid[2];
-  static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
-  static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} expected-note {{'Stack [2]' is a stack type because it is an array of stack type 'Stack'}}
+  static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
+  static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} expected-note {{'Stack [2]' is a stack type because it is an array of stack type 'Stack'}} expected-note {{value incorrectly allocated in a global variable}}
 
   gobble(&valid);
   gobble(&notValid);
   gobble(&alsoValid[0]);
 
-  gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
-  gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}}
-  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid on the stack}}
-  gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
+  gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}}
 
   char buffer[sizeof(Stack)];
   gobble(new (buffer) Stack);
 }
 
-Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
+Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 struct RandomClass {
   Stack nonstaticMember; // expected-note {{'RandomClass' is a stack type because member 'nonstaticMember' is a stack type 'Stack'}}
-  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
+  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 };
 struct MOZ_STACK_CLASS RandomStackClass {
   Stack nonstaticMember;
-  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
+  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 };
 
 struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack type because it inherits from a stack type 'Stack'}}
 struct MOZ_STACK_CLASS GoodInherit : Stack {};
 
-BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}}
-RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}}
+BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
+RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 SOURCES += [
     'TestBadImplicitConversionCtor.cpp',
     'TestCustomHeap.cpp',
     'TestExplicitOperatorBool.cpp',
     'TestGlobalClass.cpp',
+    'TestHeapClass.cpp',
     'TestInheritTypeAnnotationsFromTemplateArgs.cpp',
     'TestMultipleAnnotations.cpp',
     'TestMustOverride.cpp',
     'TestMustUse.cpp',
     'TestNANTestingExpr.cpp',
     'TestNANTestingExprC.c',
     'TestNeedsNoVTableType.cpp',
     'TestNoAddRefReleaseOnReturn.cpp',
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -164,17 +164,17 @@ GetWindowURI(nsIDOMWindow *aWindow)
 
 static void
 AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr, bool aAnonymize)
 {
   nsCOMPtr<nsIURI> uri = GetWindowURI(aWindow);
 
   if (uri) {
     if (aAnonymize && !aWindow->IsChromeWindow()) {
-      aStr.AppendPrintf("<anonymized-%d>", aWindow->WindowID());
+      aStr.AppendPrintf("<anonymized-%llu>", aWindow->WindowID());
     } else {
       nsCString spec;
       uri->GetSpec(spec);
 
       // A hack: replace forward slashes with '\\' so they aren't
       // treated as path separators.  Users of the reporters
       // (such as about:memory) have to undo this change.
       spec.ReplaceChar('/', '\\');
@@ -273,19 +273,17 @@ CollectWindowReports(nsGlobalWindow *aWi
     }
   }
 
   windowPath += NS_LITERAL_CSTRING("window-objects/");
 
   if (top) {
     windowPath += NS_LITERAL_CSTRING("top(");
     AppendWindowURI(top, windowPath, aAnonymize);
-    windowPath += NS_LITERAL_CSTRING(", id=");
-    windowPath.AppendInt(top->WindowID());
-    windowPath += NS_LITERAL_CSTRING(")");
+    windowPath.AppendPrintf(", id=%llu)", top->WindowID());
 
     aTopWindowPaths->Put(aWindow->WindowID(), windowPath);
 
     windowPath += aWindow->IsFrozen() ? NS_LITERAL_CSTRING("/cached/")
                                       : NS_LITERAL_CSTRING("/active/");
   } else {
     if (aGhostWindowIDs->Contains(aWindow->WindowID())) {
       windowPath += NS_LITERAL_CSTRING("top(none)/ghost/");
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -36,19 +36,16 @@
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/ipc/SocketBase.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
-#ifndef MOZ_B2G_BT_API_V1
-#include "nsDataHashtable.h"
-#endif
 
 #define ENSURE_BLUETOOTH_IS_READY(runnable, result)                    \
   do {                                                                 \
     if (!sBtInterface || !IsEnabled()) {                               \
       DispatchReplyError(runnable,                                     \
         NS_LITERAL_STRING("Bluetooth is not ready"));                  \
       return result;                                                   \
     }                                                                  \
@@ -84,57 +81,18 @@
 // Rendering: Major service class = 0x20 (Bit 18 is set)
 #define SET_RENDERING_BIT(cod)           (cod |= 0x40000)
 #endif
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
-static nsString sAdapterBdAddress;
-static nsString sAdapterBdName;
-
-static bool sAdapterDiscoverable(false);
-static bool sAdapterDiscovering(false);
-
-// InfallibleTArray is an alias for nsTArray.
-static InfallibleTArray<nsString> sAdapterBondedAddressArray;
-
 static BluetoothInterface* sBtInterface;
-
 static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray;
-static InfallibleTArray<BluetoothNamedValue> sRemoteDevicesPack;
-static nsTArray<int> sRequestedDeviceCountArray;
-
-static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
-static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
-
-static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
-static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
-static bool sIsRestart(false);
-static bool sIsFirstTimeToggleOffBt(false);
-
-#ifndef MOZ_B2G_BT_API_V1
-static bool sAdapterEnabled(false);
-
-// Static hash table to map device name from address
-static nsDataHashtable<nsStringHashKey, nsString> sDeviceNameMap;
-
-static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeAdapterStateRunnableArray;
-static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
-static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sFetchUuidsRunnableArray;
-#else
-// Missing in Bluetooth v1
-#endif
-
-#ifndef MOZ_B2G_BT_API_V1
-// Missing in Bluetooth v2
-#else
-static uint32_t sAdapterDiscoverableTimeout(0);
-#endif
 
 /*
  *  Static methods
  */
 
 ControlPlayStatus
 BluetoothServiceBluedroid::PlayStatusStringToControlPlayStatus(
   const nsAString& aPlayStatus)
@@ -344,18 +302,18 @@ public:
       BluetoothGattManager::InitGattInterface
 #else
       // Missing in Bluetooth v1
 #endif
     };
 
     MOZ_ASSERT(NS_IsMainThread());
 
-    // Register all the bluedroid callbacks before enable() get called
-    // It is required to register a2dp callbacks before a2dp media task starts up.
+    // Register all the bluedroid callbacks before enable() gets called. This is
+    // required to register a2dp callbacks before a2dp media task starts up.
     // If any interface cannot be initialized, turn on bluetooth core anyway.
     nsRefPtr<ProfileInitResultHandler> res =
       new ProfileInitResultHandler(MOZ_ARRAY_LENGTH(sInitManager));
 
     for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sInitManager); ++i) {
       sInitManager[i](res);
     }
   }
@@ -430,46 +388,57 @@ BluetoothServiceBluedroid::StopGonkBluet
   return NS_OK;
 }
 
 /*
  *  Member functions
  */
 
 BluetoothServiceBluedroid::BluetoothServiceBluedroid()
+  : mEnabled(false)
+  , mDiscoverable(false)
+  , mDiscovering(false)
+#ifndef MOZ_B2G_BT_API_V1
+  // Missing in Bluetooth v2
+#else
+  , mDiscoverableTimeout(0)
+#endif
+  , mIsRestart(false)
+  , mIsFirstTimeToggleOffBt(false)
 {
   sBtInterface = BluetoothInterface::GetInstance();
   if (!sBtInterface) {
     BT_LOGR("Error! Failed to get instance of bluetooth interface");
+    return;
   }
 }
 
 BluetoothServiceBluedroid::~BluetoothServiceBluedroid()
 {
 }
 
 #ifndef MOZ_B2G_BT_API_V1
 nsresult
 BluetoothServiceBluedroid::StartInternal(BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // aRunnable will be a nullptr while startup
   if (aRunnable) {
-    sChangeAdapterStateRunnableArray.AppendElement(aRunnable);
+    mChangeAdapterStateRunnables.AppendElement(aRunnable);
   }
 
   nsresult ret = StartGonkBluetooth();
   if (NS_FAILED(ret)) {
     BluetoothService::AcknowledgeToggleBt(false);
 
     // Reject Promise
     if (aRunnable) {
       DispatchReplyError(aRunnable, NS_LITERAL_STRING("StartBluetoothError"));
-      sChangeAdapterStateRunnableArray.RemoveElement(aRunnable);
+      mChangeAdapterStateRunnables.RemoveElement(aRunnable);
     }
 
     BT_LOGR("Error");
   }
 
   return ret;
 }
 
@@ -501,27 +470,27 @@ BluetoothServiceBluedroid::StopInternal(
     } else if (!profileName.EqualsLiteral("OPP") &&
                !profileName.EqualsLiteral("PBAP")) {
       sProfiles[i]->Reset();
     }
   }
 
   // aRunnable will be a nullptr during starup and shutdown
   if (aRunnable) {
-    sChangeAdapterStateRunnableArray.AppendElement(aRunnable);
+    mChangeAdapterStateRunnables.AppendElement(aRunnable);
   }
 
   nsresult ret = StopGonkBluetooth();
   if (NS_FAILED(ret)) {
     BluetoothService::AcknowledgeToggleBt(true);
 
     // Reject Promise
     if (aRunnable) {
       DispatchReplyError(aRunnable, NS_LITERAL_STRING("StopBluetoothError"));
-      sChangeAdapterStateRunnableArray.RemoveElement(aRunnable);
+      mChangeAdapterStateRunnables.RemoveElement(aRunnable);
     }
 
     BT_LOGR("Error");
   }
 
   return ret;
 }
 #else
@@ -790,104 +759,115 @@ BluetoothServiceBluedroid::GetAdaptersIn
    */
   BluetoothValue adaptersProperties = InfallibleTArray<BluetoothNamedValue>();
   uint32_t numAdapters = 1; // Bluedroid supports single adapter only
 
   for (uint32_t i = 0; i < numAdapters; i++) {
     BluetoothValue properties = InfallibleTArray<BluetoothNamedValue>();
 
     BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
-                          "State", sAdapterEnabled);
+                          "State", mEnabled);
     BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
-                          "Address", sAdapterBdAddress);
+                          "Address", mBdAddress);
     BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
-                          "Name", sAdapterBdName);
+                          "Name", mBdName);
     BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
-                          "Discoverable", sAdapterDiscoverable);
+                          "Discoverable", mDiscoverable);
     BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
-                          "Discovering", sAdapterDiscovering);
+                          "Discovering", mDiscovering);
     BT_APPEND_NAMED_VALUE(properties.get_ArrayOfBluetoothNamedValue(),
-                          "PairedDevices", sAdapterBondedAddressArray);
+                          "PairedDevices", mBondedAddresses);
 
     BT_APPEND_NAMED_VALUE(adaptersProperties.get_ArrayOfBluetoothNamedValue(),
                           "Adapter", properties);
   }
 
   DispatchReplySuccess(aRunnable, adaptersProperties);
   return NS_OK;
 }
 #else
 nsresult
 BluetoothServiceBluedroid::GetDefaultAdapterPathInternal(
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  // Since Atomic<*> is not acceptable for BT_APPEND_NAMED_VALUE(),
-  // create another variable to store data.
-  bool discoverable = sAdapterDiscoverable;
-  uint32_t discoverableTimeout = sAdapterDiscoverableTimeout;
-
   BluetoothValue v = InfallibleTArray<BluetoothNamedValue>();
 
   BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
-                        "Address", sAdapterBdAddress);
+                        "Address", mBdAddress);
 
   BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
-                        "Name", sAdapterBdName);
+                        "Name", mBdName);
 
   BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
-                        "Discoverable", discoverable);
+                        "Discoverable", mDiscoverable);
 
   BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
-                        "DiscoverableTimeout", discoverableTimeout);
+                        "DiscoverableTimeout", mDiscoverableTimeout);
 
   BT_APPEND_NAMED_VALUE(v.get_ArrayOfBluetoothNamedValue(),
-                        "Devices", sAdapterBondedAddressArray);
+                        "Devices", mBondedAddresses);
 
   DispatchReplySuccess(aRunnable, v);
 
   return NS_OK;
 }
 #endif
 
+class BluetoothServiceBluedroid::GetDeviceRequest final
+{
+public:
+  GetDeviceRequest(int aDeviceCount, BluetoothReplyRunnable* aRunnable)
+  : mDeviceCount(aDeviceCount)
+  , mRunnable(aRunnable)
+  { }
+
+  int mDeviceCount;
+  InfallibleTArray<BluetoothNamedValue> mDevicesPack;
+  nsRefPtr<BluetoothReplyRunnable> mRunnable;
+};
+
 class BluetoothServiceBluedroid::GetRemoteDevicePropertiesResultHandler
   final
   : public BluetoothResultHandler
 {
 public:
-  GetRemoteDevicePropertiesResultHandler(const nsAString& aDeviceAddress)
-  : mDeviceAddress(aDeviceAddress)
+  GetRemoteDevicePropertiesResultHandler(
+    nsTArray<GetDeviceRequest>& aRequests,
+    const nsAString& aDeviceAddress)
+  : mRequests(aRequests)
+  , mDeviceAddress(aDeviceAddress)
   { }
 
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(!mRequests.IsEmpty());
 
     BT_WARNING("GetRemoteDeviceProperties(%s) failed: %d",
                NS_ConvertUTF16toUTF8(mDeviceAddress).get(), aStatus);
 
-    /* dispatch result after final pending operation */
-    if (--sRequestedDeviceCountArray[0] == 0) {
-      if (!sGetDeviceRunnableArray.IsEmpty()) {
+    /* Dispatch result after the final pending operation */
+    if (--mRequests[0].mDeviceCount == 0) {
+      if (mRequests[0].mRunnable) {
 #ifndef MOZ_B2G_BT_API_V1
-        DispatchReplyError(sGetDeviceRunnableArray[0],
+        DispatchReplyError(mRequests[0].mRunnable,
           NS_LITERAL_STRING("GetRemoteDeviceProperties failed"));
 #else
-        DispatchReplySuccess(sGetDeviceRunnableArray[0], sRemoteDevicesPack);
+        DispatchReplySuccess(mRequests[0].mRunnable,
+                             mRequests[0].mDevicesPack);
 #endif
-        sGetDeviceRunnableArray.RemoveElementAt(0);
       }
-
-      sRequestedDeviceCountArray.RemoveElementAt(0);
-      sRemoteDevicesPack.Clear();
+      mRequests.RemoveElementAt(0);
     }
   }
 
 private:
+  nsTArray<GetDeviceRequest> mRequests;
   nsString mDeviceAddress;
 };
 
 nsresult
 BluetoothServiceBluedroid::GetConnectedDevicePropertiesInternal(
   uint16_t aServiceUuid, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -896,89 +876,83 @@ BluetoothServiceBluedroid::GetConnectedD
 
   BluetoothProfileManagerBase* profile =
     BluetoothUuidHelper::GetBluetoothProfileManager(aServiceUuid);
   if (!profile) {
     DispatchReplyError(aRunnable, NS_LITERAL_STRING(ERR_UNKNOWN_PROFILE));
     return NS_OK;
   }
 
-  nsTArray<nsString> deviceAddresses;
-  if (profile->IsConnected()) {
-    nsString address;
-    profile->GetAddress(address);
-    deviceAddresses.AppendElement(address);
-  }
-
-  int requestedDeviceCount = deviceAddresses.Length();
-  if (requestedDeviceCount == 0) {
-    InfallibleTArray<BluetoothNamedValue> emptyArr;
-    DispatchReplySuccess(aRunnable, emptyArr);
+  // Reply success if no device of this profile is connected
+  if (!profile->IsConnected()) {
+    DispatchReplySuccess(aRunnable, InfallibleTArray<BluetoothNamedValue>());
     return NS_OK;
   }
 
-  sRequestedDeviceCountArray.AppendElement(requestedDeviceCount);
-  sGetDeviceRunnableArray.AppendElement(aRunnable);
-
-  for (int i = 0; i < requestedDeviceCount; i++) {
-    // Retrieve all properties of devices
-    sBtInterface->GetRemoteDeviceProperties(deviceAddresses[i],
-      new GetRemoteDevicePropertiesResultHandler(deviceAddresses[i]));
-  }
+  // Get address of the connected device
+  nsString address;
+  profile->GetAddress(address);
+
+  // Append request of the connected device
+  GetDeviceRequest request(1, aRunnable);
+  mGetDeviceRequests.AppendElement(request);
+
+  sBtInterface->GetRemoteDeviceProperties(address,
+    new GetRemoteDevicePropertiesResultHandler(mGetDeviceRequests, address));
 
   return NS_OK;
 }
 
 nsresult
 BluetoothServiceBluedroid::GetPairedDevicePropertiesInternal(
   const nsTArray<nsString>& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
-  int requestedDeviceCount = aDeviceAddress.Length();
-
+  if (aDeviceAddress.IsEmpty()) {
 #ifndef MOZ_B2G_BT_API_V1
-  if (requestedDeviceCount == 0) {
     DispatchReplySuccess(aRunnable);
+#else
+    DispatchReplySuccess(aRunnable, InfallibleTArray<BluetoothNamedValue>());
+#endif
     return NS_OK;
   }
-#else
-  if (requestedDeviceCount == 0) {
-    DispatchReplySuccess(aRunnable, InfallibleTArray<BluetoothNamedValue>());
-    return NS_OK;
-  }
-
-  sRequestedDeviceCountArray.AppendElement(requestedDeviceCount);
-  sGetDeviceRunnableArray.AppendElement(aRunnable);
-#endif
-
-  for (int i = 0; i < requestedDeviceCount; i++) {
+
+  // Append request of all paired devices
+  GetDeviceRequest request(aDeviceAddress.Length(), aRunnable);
+  mGetDeviceRequests.AppendElement(request);
+
+  for (uint8_t i = 0; i < aDeviceAddress.Length(); i++) {
     // Retrieve all properties of devices
     sBtInterface->GetRemoteDeviceProperties(aDeviceAddress[i],
-      new GetRemoteDevicePropertiesResultHandler(aDeviceAddress[i]));
+      new GetRemoteDevicePropertiesResultHandler(mGetDeviceRequests,
+                                                 aDeviceAddress[i]));
   }
 
   return NS_OK;
 }
 
 class BluetoothServiceBluedroid::StartDiscoveryResultHandler final
   : public BluetoothResultHandler
 {
 public:
-  StartDiscoveryResultHandler(BluetoothReplyRunnable* aRunnable)
-  : mRunnable(aRunnable)
+  StartDiscoveryResultHandler(
+    nsTArray<nsRefPtr<BluetoothReplyRunnable>>& aRunnableArray,
+    BluetoothReplyRunnable* aRunnable)
+  : mRunnableArray(aRunnableArray)
+  , mRunnable(aRunnable)
   { }
 
 #ifndef MOZ_B2G_BT_API_V1
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
-    sChangeDiscoveryRunnableArray.RemoveElement(mRunnable);
+    mRunnableArray.RemoveElement(mRunnable);
     DispatchReplyError(mRunnable, aStatus);
   }
 #else
   void StartDiscovery() override
   {
     MOZ_ASSERT(NS_IsMainThread());
     DispatchReplySuccess(mRunnable);
   }
@@ -986,48 +960,53 @@ public:
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
     ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("StartDiscovery"));
   }
 #endif
 
 private:
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mRunnableArray;
   BluetoothReplyRunnable* mRunnable;
 };
 
 void
 BluetoothServiceBluedroid::StartDiscoveryInternal(
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
 
 #ifndef MOZ_B2G_BT_API_V1
-  sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
+  mChangeDiscoveryRunnables.AppendElement(aRunnable);
+  sBtInterface->StartDiscovery(
+    new StartDiscoveryResultHandler(mChangeDiscoveryRunnables, aRunnable));
 #else
-  // Missing in bluetooth1
+  sBtInterface->StartDiscovery(
+    new StartDiscoveryResultHandler(nullptr, aRunnable));
 #endif
-
-  sBtInterface->StartDiscovery(new StartDiscoveryResultHandler(aRunnable));
 }
 
 class BluetoothServiceBluedroid::CancelDiscoveryResultHandler final
   : public BluetoothResultHandler
 {
 public:
-  CancelDiscoveryResultHandler(BluetoothReplyRunnable* aRunnable)
-  : mRunnable(aRunnable)
+  CancelDiscoveryResultHandler(
+    nsTArray<nsRefPtr<BluetoothReplyRunnable>>& aRunnableArray,
+    BluetoothReplyRunnable* aRunnable)
+  : mRunnableArray(aRunnableArray)
+  , mRunnable(aRunnable)
   { }
 
 #ifndef MOZ_B2G_BT_API_V1
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
-    sChangeDiscoveryRunnableArray.RemoveElement(mRunnable);
+    mRunnableArray.RemoveElement(mRunnable);
     DispatchReplyError(mRunnable, aStatus);
   }
 #else
   void CancelDiscovery() override
   {
     MOZ_ASSERT(NS_IsMainThread());
     DispatchReplySuccess(mRunnable);
   }
@@ -1035,118 +1014,131 @@ public:
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
     ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("StopDiscovery"));
   }
 #endif
 
 private:
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mRunnableArray;
   BluetoothReplyRunnable* mRunnable;
 };
 
 #ifndef MOZ_B2G_BT_API_V1
 class BluetoothServiceBluedroid::GetRemoteServicesResultHandler final
   : public BluetoothResultHandler
 {
 public:
-  GetRemoteServicesResultHandler(BluetoothReplyRunnable* aRunnable)
-  : mRunnable(aRunnable)
+  GetRemoteServicesResultHandler(
+    nsTArray<nsRefPtr<BluetoothReplyRunnable>>& aRunnableArray,
+    BluetoothReplyRunnable* aRunnable)
+  : mRunnableArray(aRunnableArray)
+  , mRunnable(aRunnable)
   { }
 
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
-    sFetchUuidsRunnableArray.RemoveElement(mRunnable);
+    mRunnableArray.RemoveElement(mRunnable);
     DispatchReplyError(mRunnable, aStatus);
   }
 
 private:
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mRunnableArray;
   BluetoothReplyRunnable* mRunnable;
 };
 
 nsresult
 BluetoothServiceBluedroid::FetchUuidsInternal(
   const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
   /*
    * get_remote_services request will not be performed by bluedroid
    * if it is currently discovering nearby remote devices.
    */
-  if (sAdapterDiscovering) {
-    sBtInterface->CancelDiscovery(new CancelDiscoveryResultHandler(aRunnable));
+  if (mDiscovering) {
+    sBtInterface->CancelDiscovery(
+      new CancelDiscoveryResultHandler(mChangeDiscoveryRunnables, aRunnable));
   }
 
-  sFetchUuidsRunnableArray.AppendElement(aRunnable);
+  mFetchUuidsRunnables.AppendElement(aRunnable);
 
   sBtInterface->GetRemoteServices(aDeviceAddress,
-    new GetRemoteServicesResultHandler(aRunnable));
+    new GetRemoteServicesResultHandler(mFetchUuidsRunnables, aRunnable));
 
   return NS_OK;
 }
 #else
 // Missing in bluetooth1
 #endif
 
 void
 BluetoothServiceBluedroid::StopDiscoveryInternal(
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
 
 #ifndef MOZ_B2G_BT_API_V1
-  sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
+  mChangeDiscoveryRunnables.AppendElement(aRunnable);
+  sBtInterface->CancelDiscovery(
+    new CancelDiscoveryResultHandler(mChangeDiscoveryRunnables,
+                                     aRunnable));
 #else
-  // Missing in bluetooth1
+  sBtInterface->CancelDiscovery(
+    new CancelDiscoveryResultHandler(nullptr, aRunnable));
 #endif
-
-  sBtInterface->CancelDiscovery(new CancelDiscoveryResultHandler(aRunnable));
 }
 
 class BluetoothServiceBluedroid::SetAdapterPropertyResultHandler final
   : public BluetoothResultHandler
 {
 public:
-  SetAdapterPropertyResultHandler(BluetoothReplyRunnable* aRunnable)
-  : mRunnable(aRunnable)
+  SetAdapterPropertyResultHandler(
+    nsTArray<nsRefPtr<BluetoothReplyRunnable>>& aRunnableArray,
+    BluetoothReplyRunnable* aRunnable)
+  : mRunnableArray(aRunnableArray)
+  , mRunnable(aRunnable)
   { }
 
   void OnError(BluetoothStatus aStatus) override
   {
     MOZ_ASSERT(NS_IsMainThread());
 
 #ifndef MOZ_B2G_BT_API_V1
-    sSetPropertyRunnableArray.RemoveElement(mRunnable);
+    mRunnableArray.RemoveElement(mRunnable);
     DispatchReplyError(mRunnable, aStatus);
 #else
     ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("SetProperty"));
 #endif
   }
 private:
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mRunnableArray;
   BluetoothReplyRunnable* mRunnable;
 };
 
 nsresult
 BluetoothServiceBluedroid::SetProperty(BluetoothObjectType aType,
                                        const BluetoothNamedValue& aValue,
                                        BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
-  sSetPropertyRunnableArray.AppendElement(aRunnable);
+  mSetAdapterPropertyRunnables.AppendElement(aRunnable);
 
   sBtInterface->SetAdapterProperty(aValue,
-    new SetAdapterPropertyResultHandler(aRunnable));
+    new SetAdapterPropertyResultHandler(mSetAdapterPropertyRunnables,
+                                        aRunnable));
 
   return NS_OK;
 }
 
 nsresult
 BluetoothServiceBluedroid::GetServiceChannel(
   const nsAString& aDeviceAddress,
   const nsAString& aServiceUuid,
@@ -1162,90 +1154,99 @@ BluetoothServiceBluedroid::UpdateSdpReco
 {
   return true;
 }
 
 class BluetoothServiceBluedroid::CreateBondResultHandler final
   : public BluetoothResultHandler
 {
 public:
-  CreateBondResultHandler(BluetoothReplyRunnable* aRunnable)
-  : mRunnable(aRunnable)
+  CreateBondResultHandler(
+    nsTArray<nsRefPtr<BluetoothReplyRunnable>>& aRunnableArray,
+    BluetoothReplyRunnable* aRunnable)
+  : mRunnableArray(aRunnableArray)
+  , mRunnable(aRunnable)
   {
     MOZ_ASSERT(mRunnable);
   }
 
   void OnError(BluetoothStatus aStatus) override
   {
-    sBondingRunnableArray.RemoveElement(mRunnable);
+    mRunnableArray.RemoveElement(mRunnable);
 
 #ifndef MOZ_B2G_BT_API_V1
     DispatchReplyError(mRunnable, aStatus);
 #else
     ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("CreatedPairedDevice"));
 #endif
   }
 
 private:
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mRunnableArray;
   nsRefPtr<BluetoothReplyRunnable> mRunnable;
 };
 
 nsresult
 BluetoothServiceBluedroid::CreatePairedDeviceInternal(
   const nsAString& aDeviceAddress, int aTimeout,
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
-  sBondingRunnableArray.AppendElement(aRunnable);
+  mCreateBondRunnables.AppendElement(aRunnable);
 
   sBtInterface->CreateBond(aDeviceAddress, TRANSPORT_AUTO,
-                           new CreateBondResultHandler(aRunnable));
+    new CreateBondResultHandler(mCreateBondRunnables, aRunnable));
+
   return NS_OK;
 }
 
 class BluetoothServiceBluedroid::RemoveBondResultHandler final
   : public BluetoothResultHandler
 {
 public:
-  RemoveBondResultHandler(BluetoothReplyRunnable* aRunnable)
-  : mRunnable(aRunnable)
+  RemoveBondResultHandler(
+    nsTArray<nsRefPtr<BluetoothReplyRunnable>>& aRunnableArray,
+    BluetoothReplyRunnable* aRunnable)
+  : mRunnableArray(aRunnableArray)
+  , mRunnable(aRunnable)
   {
     MOZ_ASSERT(mRunnable);
   }
 
   void OnError(BluetoothStatus aStatus) override
   {
-    sUnbondingRunnableArray.RemoveElement(mRunnable);
+    mRunnableArray.RemoveElement(mRunnable);
 
 #ifndef MOZ_B2G_BT_API_V1
     DispatchReplyError(mRunnable, aStatus);
 #else
     ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("RemoveDevice"));
 #endif
   }
 
 private:
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mRunnableArray;
   nsRefPtr<BluetoothReplyRunnable> mRunnable;
 };
 
 nsresult
 BluetoothServiceBluedroid::RemoveDeviceInternal(
   const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
-  sUnbondingRunnableArray.AppendElement(aRunnable);
+  mRemoveBondRunnables.AppendElement(aRunnable);
 
   sBtInterface->RemoveBond(aDeviceAddress,
-                           new RemoveBondResultHandler(aRunnable));
+    new RemoveBondResultHandler(mRemoveBondRunnables, aRunnable));
 
   return NS_OK;
 }
 
 class BluetoothServiceBluedroid::PinReplyResultHandler final
   : public BluetoothResultHandler
 {
 public:
@@ -1436,18 +1437,18 @@ BluetoothServiceBluedroid::ConnectDiscon
 
   BluetoothProfileController* controller =
     new BluetoothProfileController(aConnect, aDeviceAddress, aRunnable,
                                    NextBluetoothProfileController,
                                    aServiceUuid, aCod);
   sControllerArray.AppendElement(controller);
 
   /**
-   * If the request is the first element of the quene, start from here. Note
-   * that other request is pushed into the quene and is popped out after the
+   * If the request is the first element of the queue, start from here. Note
+   * that other requests are pushed into the queue and popped out after the
    * first one is completed. See NextBluetoothProfileController() for details.
    */
   if (sControllerArray.Length() == 1) {
     sControllerArray[0]->StartSession();
   }
 }
 
 void
@@ -1766,18 +1767,19 @@ public:
 /* |ProfileDeinitResultHandler| collects the results of all profile
  * result handlers and cleans up the Bluedroid driver after all handlers
  * have been run.
  */
 class BluetoothServiceBluedroid::ProfileDeinitResultHandler final
   : public BluetoothProfileResultHandler
 {
 public:
-  ProfileDeinitResultHandler(unsigned char aNumProfiles)
+  ProfileDeinitResultHandler(unsigned char aNumProfiles, bool aIsRestart)
     : mNumProfiles(aNumProfiles)
+    , mIsRestart(aIsRestart)
   {
     MOZ_ASSERT(mNumProfiles);
   }
 
   void Deinit() override
   {
     if (!(--mNumProfiles)) {
       Proceed();
@@ -1789,24 +1791,26 @@ public:
     if (!(--mNumProfiles)) {
       Proceed();
     }
   }
 
 private:
   void Proceed() const
   {
-    if (!sIsRestart) {
-      sBtInterface->Cleanup(new CleanupResultHandler());
-    } else {
+    if (mIsRestart) {
       BT_LOGR("ProfileDeinitResultHandler::Proceed cancel cleanup() ");
+      return;
     }
+
+    sBtInterface->Cleanup(new CleanupResultHandler());
   }
 
   unsigned char mNumProfiles;
+  bool mIsRestart;
 };
 
 class BluetoothServiceBluedroid::SetAdapterPropertyDiscoverableResultHandler
   final
   : public BluetoothResultHandler
 {
 public:
   void OnError(BluetoothStatus aStatus) override
@@ -1818,83 +1822,84 @@ public:
 void
 BluetoothServiceBluedroid::AdapterStateChangedNotification(bool aState)
 {
 #ifndef MOZ_B2G_BT_API_V1
   MOZ_ASSERT(NS_IsMainThread());
 
   BT_LOGR("BT_STATE: %d", aState);
 
-  if (sIsRestart && aState) {
+  if (mIsRestart && aState) {
     // daemon restarted, reset flag
     BT_LOGR("daemon restarted, reset flag");
-    sIsRestart = false;
-    sIsFirstTimeToggleOffBt = false;
+    mIsRestart = false;
+    mIsFirstTimeToggleOffBt = false;
   }
 
-  sAdapterEnabled = aState;
-
-  if (!sAdapterEnabled) {
+  mEnabled = aState;
+
+  if (!mEnabled) {
     static void (* const sDeinitManager[])(BluetoothProfileResultHandler*) = {
       BluetoothHfpManager::DeinitHfpInterface,
       BluetoothA2dpManager::DeinitA2dpInterface,
       BluetoothGattManager::DeinitGattInterface
     };
 
     // Return error if BluetoothService is unavailable
     BluetoothService* bs = BluetoothService::Get();
     NS_ENSURE_TRUE_VOID(bs);
 
     // Cleanup static adapter properties and notify adapter.
-    sAdapterBdAddress.Truncate();
-    sAdapterBdName.Truncate();
+    mBdAddress.Truncate();
+    mBdName.Truncate();
 
     InfallibleTArray<BluetoothNamedValue> props;
-    BT_APPEND_NAMED_VALUE(props, "Name", sAdapterBdName);
-    BT_APPEND_NAMED_VALUE(props, "Address", sAdapterBdAddress);
-    if (sAdapterDiscoverable) {
-      sAdapterDiscoverable = false;
+    BT_APPEND_NAMED_VALUE(props, "Name", mBdName);
+    BT_APPEND_NAMED_VALUE(props, "Address", mBdAddress);
+    if (mDiscoverable) {
+      mDiscoverable = false;
       BT_APPEND_NAMED_VALUE(props, "Discoverable", false);
     }
-    if (sAdapterDiscovering) {
-      sAdapterDiscovering = false;
+    if (mDiscovering) {
+      mDiscovering = false;
       BT_APPEND_NAMED_VALUE(props, "Discovering", false);
     }
 
     bs->DistributeSignal(NS_LITERAL_STRING("PropertyChanged"),
                          NS_LITERAL_STRING(KEY_ADAPTER),
                          BluetoothValue(props));
 
     // Cleanup Bluetooth interfaces after state becomes BT_STATE_OFF. This
     // will also stop the Bluetooth daemon and disable the adapter.
     nsRefPtr<ProfileDeinitResultHandler> res =
-      new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager));
+      new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager),
+                                     mIsRestart);
 
     for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDeinitManager); ++i) {
       sDeinitManager[i](res);
     }
   }
 
-  if (sAdapterEnabled) {
+  if (mEnabled) {
 
     // We enable the Bluetooth adapter here. Disabling is implemented
     // in |CleanupResultHandler|, which runs at the end of the shutdown
     // procedure. We cannot disable the adapter immediately, because re-
     // enabling it might interfere with the shutdown procedure.
     BluetoothService::AcknowledgeToggleBt(true);
 
     // Bluetooth just enabled, clear profile controllers and runnable arrays.
     sControllerArray.Clear();
-    sChangeDiscoveryRunnableArray.Clear();
-    sSetPropertyRunnableArray.Clear();
-    sGetDeviceRunnableArray.Clear();
-    sFetchUuidsRunnableArray.Clear();
-    sBondingRunnableArray.Clear();
-    sUnbondingRunnableArray.Clear();
-    sDeviceNameMap.Clear();
+    mGetDeviceRequests.Clear();
+    mChangeDiscoveryRunnables.Clear();
+    mSetAdapterPropertyRunnables.Clear();
+    mFetchUuidsRunnables.Clear();
+    mCreateBondRunnables.Clear();
+    mRemoveBondRunnables.Clear();
+    mDeviceNameMap.Clear();
 
     // Bluetooth scan mode is SCAN_MODE_CONNECTABLE by default, i.e., it should
     // be connectable and non-discoverable.
     NS_ENSURE_TRUE_VOID(sBtInterface);
     sBtInterface->SetAdapterProperty(
       BluetoothNamedValue(NS_ConvertUTF8toUTF16("Discoverable"), false),
       new SetAdapterPropertyDiscoverableResultHandler());
 
@@ -1906,69 +1911,69 @@ BluetoothServiceBluedroid::AdapterStateC
 
     BluetoothPbapManager* pbap = BluetoothPbapManager::Get();
     if (!pbap || !pbap->Listen()) {
       BT_LOGR("Fail to start BluetoothPbapManager listening");
     }
   }
 
   // Resolve promise if existed
-  if (!sChangeAdapterStateRunnableArray.IsEmpty()) {
-    DispatchReplySuccess(sChangeAdapterStateRunnableArray[0]);
-    sChangeAdapterStateRunnableArray.RemoveElementAt(0);
+  if (!mChangeAdapterStateRunnables.IsEmpty()) {
+    DispatchReplySuccess(mChangeAdapterStateRunnables[0]);
+    mChangeAdapterStateRunnables.RemoveElementAt(0);
   }
 
-  // After ProfileManagers deinit and cleanup, now restarts bluetooth daemon
-  if (sIsRestart && !aState) {
-    BT_LOGR("sIsRestart and off, now restart");
+  // After ProfileManagers deinit and cleanup, now restart bluetooth daemon
+  if (mIsRestart && !aState) {
+    BT_LOGR("mIsRestart and off, now restart");
     StartBluetooth(false, nullptr);
   }
 
 #else
   MOZ_ASSERT(NS_IsMainThread());
 
   BT_LOGR("BT_STATE: %d", aState);
 
-  if (sIsRestart && aState) {
+  if (mIsRestart && aState) {
     // daemon restarted, reset flag
     BT_LOGR("daemon restarted, reset flag");
-    sIsRestart = false;
-    sIsFirstTimeToggleOffBt = false;
+    mIsRestart = false;
+    mIsFirstTimeToggleOffBt = false;
   }
   bool isBtEnabled = (aState == true);
 
   if (!isBtEnabled) {
     static void (* const sDeinitManager[])(BluetoothProfileResultHandler*) = {
       BluetoothHfpManager::DeinitHfpInterface,
       BluetoothA2dpManager::DeinitA2dpInterface
     };
 
     // Set discoverable cache to default value after state becomes BT_STATE_OFF.
-    if (sAdapterDiscoverable) {
-      sAdapterDiscoverable = false;
+    if (mDiscoverable) {
+      mDiscoverable = false;
     }
 
     // Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
     nsRefPtr<ProfileDeinitResultHandler> res =
       new ProfileDeinitResultHandler(MOZ_ARRAY_LENGTH(sDeinitManager));
 
     for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDeinitManager); ++i) {
       sDeinitManager[i](res);
     }
   }
 
   BluetoothService::AcknowledgeToggleBt(isBtEnabled);
 
   if (isBtEnabled) {
     // Bluetooth just enabled, clear profile controllers and runnable arrays.
     sControllerArray.Clear();
-    sBondingRunnableArray.Clear();
-    sGetDeviceRunnableArray.Clear();
-    sSetPropertyRunnableArray.Clear();
-    sUnbondingRunnableArray.Clear();
+    mCreateBondRunnables.Clear();
+    mGetDeviceRunnables.Clear();
+    mSetAdapterPropertyRunnables.Clear();
+    mRemoveBondRunnables.Clear();
 
     // Bluetooth scan mode is SCAN_MODE_CONNECTABLE by default, i.e., It should
     // be connectable and non-discoverable.
     NS_ENSURE_TRUE_VOID(sBtInterface);
     sBtInterface->SetAdapterProperty(
       BluetoothNamedValue(NS_ConvertUTF8toUTF16("Discoverable"), false),
       new SetAdapterPropertyDiscoverableResultHandler());
 
@@ -1988,148 +1993,135 @@ BluetoothServiceBluedroid::AdapterStateC
 
     BluetoothPbapManager* pbap = BluetoothPbapManager::Get();
     if (!pbap || !pbap->Listen()) {
       BT_LOGR("Fail to start BluetoothPbapManager listening");
     }
   }
 
   // After ProfileManagers deinit and cleanup, now restarts bluetooth daemon
-  if (sIsRestart && !aState) {
-    BT_LOGR("sIsRestart and off, now restart");
+  if (mIsRestart && !aState) {
+    BT_LOGR("mIsRestart and off, now restart");
     StartBluetooth(false);
   }
 #endif
 }
 
 /**
- * AdapterPropertiesNotification will be called after enable() but
- * before AdapterStateChangeCallback is called. At that moment, both
- * BluetoothManager and BluetoothAdapter, do not register observer
- * yet.
+ * AdapterPropertiesNotification will be called after enable() but before
+ * AdapterStateChangeCallback is called. At that moment, both BluetoothManager
+ * and BluetoothAdapter have not registered observer yet.
  */
 void
 BluetoothServiceBluedroid::AdapterPropertiesNotification(
   BluetoothStatus aStatus, int aNumProperties,
   const BluetoothProperty* aProperties)
 {
 #ifndef MOZ_B2G_BT_API_V1
   MOZ_ASSERT(NS_IsMainThread());
 
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
 
   for (int i = 0; i < aNumProperties; i++) {
 
     const BluetoothProperty& p = aProperties[i];
 
     if (p.mType == PROPERTY_BDADDR) {
-      sAdapterBdAddress = p.mString;
-      BT_APPEND_NAMED_VALUE(propertiesArray, "Address", sAdapterBdAddress);
+      mBdAddress = p.mString;
+      BT_APPEND_NAMED_VALUE(propertiesArray, "Address", mBdAddress);
 
     } else if (p.mType == PROPERTY_BDNAME) {
-      sAdapterBdName = p.mString;
-      BT_APPEND_NAMED_VALUE(propertiesArray, "Name", sAdapterBdName);
+      mBdName = p.mString;
+      BT_APPEND_NAMED_VALUE(propertiesArray, "Name", mBdName);
 
     } else if (p.mType == PROPERTY_ADAPTER_SCAN_MODE) {
 
       // If BT is not enabled, Bluetooth scan mode should be non-discoverable
-      // by defalut. 'AdapterStateChangedNotification' would set the default
+      // by defalut. |AdapterStateChangedNotification| would set default
       // properties to bluetooth backend once Bluetooth is enabled.
       if (IsEnabled()) {
-        sAdapterDiscoverable =
-          (p.mScanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE);
-        BT_APPEND_NAMED_VALUE(propertiesArray, "Discoverable",
-                              sAdapterDiscoverable);
+        mDiscoverable = (p.mScanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+        BT_APPEND_NAMED_VALUE(propertiesArray, "Discoverable", mDiscoverable);
       }
     } else if (p.mType == PROPERTY_ADAPTER_BONDED_DEVICES) {
       // We have to cache addresses of bonded devices. Unlike BlueZ,
       // Bluedroid would not send another PROPERTY_ADAPTER_BONDED_DEVICES
       // event after bond completed.
       BT_LOGD("Adapter property: BONDED_DEVICES. Count: %d",
               p.mStringArray.Length());
 
       // Whenever reloading paired devices, force refresh
-      sAdapterBondedAddressArray.Clear();
-      sAdapterBondedAddressArray.AppendElements(p.mStringArray);
+      mBondedAddresses.Clear();
+      mBondedAddresses.AppendElements(p.mStringArray);
 
       BT_APPEND_NAMED_VALUE(propertiesArray, "PairedDevices",
-                            sAdapterBondedAddressArray);
+                            mBondedAddresses);
     } else if (p.mType == PROPERTY_UNKNOWN) {
       /* Bug 1065999: working around unknown properties */
     } else {
       BT_LOGD("Unhandled adapter property type: %d", p.mType);
       continue;
     }
   }
 
   NS_ENSURE_TRUE_VOID(propertiesArray.Length() > 0);
 
   DistributeSignal(NS_LITERAL_STRING("PropertyChanged"),
                    NS_LITERAL_STRING(KEY_ADAPTER),
                    BluetoothValue(propertiesArray));
 
   // Send reply for SetProperty
-  if (!sSetPropertyRunnableArray.IsEmpty()) {
-    DispatchReplySuccess(sSetPropertyRunnableArray[0]);
-    sSetPropertyRunnableArray.RemoveElementAt(0);
+  if (!mSetAdapterPropertyRunnables.IsEmpty()) {
+    DispatchReplySuccess(mSetAdapterPropertyRunnables[0]);
+    mSetAdapterPropertyRunnables.RemoveElementAt(0);
   }
 #else
   MOZ_ASSERT(NS_IsMainThread());
 
-  BluetoothValue propertyValue;
   InfallibleTArray<BluetoothNamedValue> props;
 
   for (int i = 0; i < aNumProperties; i++) {
 
     const BluetoothProperty& p = aProperties[i];
 
     if (p.mType == PROPERTY_BDADDR) {
-      sAdapterBdAddress = p.mString;
-      propertyValue = sAdapterBdAddress;
-      BT_APPEND_NAMED_VALUE(props, "Address", propertyValue);
+      mBdAddress = p.mString;
+      BT_APPEND_NAMED_VALUE(props, "Address", mBdAddress);
 
     } else if (p.mType == PROPERTY_BDNAME) {
-      sAdapterBdName = p.mString;
-      propertyValue = sAdapterBdName;
-      BT_APPEND_NAMED_VALUE(props, "Name", propertyValue);
+      mBdName = p.mString;
+      BT_APPEND_NAMED_VALUE(props, "Name", mBdName);
 
     } else if (p.mType == PROPERTY_ADAPTER_SCAN_MODE) {
       BluetoothScanMode newMode = p.mScanMode;
 
       // If BT is not enabled, Bluetooth scan mode should be non-discoverable
       // by defalut. 'AdapterStateChangedNotification' would set the default
       // properties to bluetooth backend once Bluetooth is enabled.
-      if (newMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE && IsEnabled()) {
-        propertyValue = sAdapterDiscoverable = true;
-      } else {
-        propertyValue = sAdapterDiscoverable = false;
-      }
-
-      BT_APPEND_NAMED_VALUE(props, "Discoverable", propertyValue);
+      mDiscoverable =
+        (newMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE && IsEnabled());
+      BT_APPEND_NAMED_VALUE(props, "Discoverable", mDiscoverable);
 
     } else if (p.mType == PROPERTY_ADAPTER_DISCOVERY_TIMEOUT) {
-      propertyValue = sAdapterDiscoverableTimeout = p.mUint32;
-      BT_APPEND_NAMED_VALUE(props, "DiscoverableTimeout", propertyValue);
+      mDiscoverableTimeout = p.mUint32;
+      BT_APPEND_NAMED_VALUE(props, "DiscoverableTimeout",
+                            mDiscoverableTimeout);
 
     } else if (p.mType == PROPERTY_ADAPTER_BONDED_DEVICES) {
       // We have to cache addresses of bonded devices. Unlike BlueZ,
       // Bluedroid would not send another PROPERTY_ADAPTER_BONDED_DEVICES
       // event after bond completed.
       BT_LOGD("Adapter property: BONDED_DEVICES. Count: %d",
               p.mStringArray.Length());
 
       // Whenever reloading paired devices, force refresh
-      sAdapterBondedAddressArray.Clear();
-
-      for (size_t index = 0; index < p.mStringArray.Length(); index++) {
-        sAdapterBondedAddressArray.AppendElement(p.mStringArray[index]);
-      }
-
-      propertyValue = sAdapterBondedAddressArray;
-      BT_APPEND_NAMED_VALUE(props, "Devices", propertyValue);
+      mBondedAddresses.Clear();
+      mBondedAddresses.AppendElements(p.mStringArray);
+
+      BT_APPEND_NAMED_VALUE(props, "Devices", mBondedAddresses);
 
     } else if (p.mType == PROPERTY_UUIDS) {
       //FIXME: This will be implemented in the later patchset
       continue;
     } else if (p.mType == PROPERTY_UNKNOWN) {
       /* Bug 1065999: working around unknown properties */
       continue;
     } else {
@@ -2140,19 +2132,19 @@ BluetoothServiceBluedroid::AdapterProper
 
   NS_ENSURE_TRUE_VOID(props.Length() > 0);
 
   DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PropertyChanged"),
                                    NS_LITERAL_STRING(KEY_ADAPTER),
                                    BluetoothValue(props)));
 
   // Send reply for SetProperty
-  if (!sSetPropertyRunnableArray.IsEmpty()) {
-    DispatchReplySuccess(sSetPropertyRunnableArray[0]);
-    sSetPropertyRunnableArray.RemoveElementAt(0);
+  if (!mSetAdapterPropertyRunnables.IsEmpty()) {
+    DispatchReplySuccess(mSetAdapterPropertyRunnables[0]);
+    mSetAdapterPropertyRunnables.RemoveElementAt(0);
   }
 #endif
 }
 
 /**
  * RemoteDevicePropertiesNotification will be called
  *
  *   (1) automatically by Bluedroid when BT is turning on, or
@@ -2177,18 +2169,18 @@ BluetoothServiceBluedroid::RemoteDeviceP
   for (int i = 0; i < aNumProperties; ++i) {
 
     const BluetoothProperty& p = aProperties[i];
 
     if (p.mType == PROPERTY_BDNAME) {
       BT_APPEND_NAMED_VALUE(propertiesArray, "Name", p.mString);
 
       // Update <address, name> mapping
-      sDeviceNameMap.Remove(bdAddr);
-      sDeviceNameMap.Put(bdAddr, p.mString);
+      mDeviceNameMap.Remove(bdAddr);
+      mDeviceNameMap.Put(bdAddr, p.mString);
     } else if (p.mType == PROPERTY_CLASS_OF_DEVICE) {
       uint32_t cod = p.mUint32;
       BT_APPEND_NAMED_VALUE(propertiesArray, "Cod", cod);
 
     } else if (p.mType == PROPERTY_UUIDS) {
       nsTArray<nsString> uuids;
 
       // Construct a sorted uuid set
@@ -2221,48 +2213,45 @@ BluetoothServiceBluedroid::RemoteDeviceP
   //
   // |DispatchReplySuccess| creates its own internal runnable, which is
   // always run after we completed the current method. This means that we
   // can exchange |DispatchReplySuccess| with other operations without
   // changing the order of (1,2) and (3).
 
   // Update to registered BluetoothDevice objects
   BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
-                         nsString(aBdAddr), propertiesArray);
+                         bdAddr, propertiesArray);
 
   // FetchUuids task
-  if (!sFetchUuidsRunnableArray.IsEmpty()) {
+  if (!mFetchUuidsRunnables.IsEmpty()) {
     // propertiesArray contains Address and Uuids only
-    DispatchReplySuccess(sFetchUuidsRunnableArray[0],
+    DispatchReplySuccess(mFetchUuidsRunnables[0],
                          propertiesArray[1].value()); /* Uuids */
-    sFetchUuidsRunnableArray.RemoveElementAt(0);
+    mFetchUuidsRunnables.RemoveElementAt(0);
     DistributeSignal(signal);
     return;
   }
 
   // GetDevices task
-  if (sRequestedDeviceCountArray.IsEmpty()) {
-    // This is possible because the callback would be called after turning
-    // Bluetooth on.
+  if (mGetDeviceRequests.IsEmpty()) {
+    // Callback is called after Bluetooth is turned on
     DistributeSignal(signal);
     return;
   }
 
   // Use address as the index
-  sRemoteDevicesPack.AppendElement(
-    BluetoothNamedValue(nsString(aBdAddr), propertiesArray));
-
-  if (--sRequestedDeviceCountArray[0] == 0) {
-    if (!sGetDeviceRunnableArray.IsEmpty()) {
-      DispatchReplySuccess(sGetDeviceRunnableArray[0], sRemoteDevicesPack);
-      sGetDeviceRunnableArray.RemoveElementAt(0);
+  mGetDeviceRequests[0].mDevicesPack.AppendElement(
+    BluetoothNamedValue(bdAddr, propertiesArray));
+
+  if (--mGetDeviceRequests[0].mDeviceCount == 0) {
+    if (mGetDeviceRequests[0].mRunnable) {
+      DispatchReplySuccess(mGetDeviceRequests[0].mRunnable,
+                           mGetDeviceRequests[0].mDevicesPack);
     }
-
-    sRequestedDeviceCountArray.RemoveElementAt(0);
-    sRemoteDevicesPack.Clear();
+    mGetDeviceRequests.RemoveElementAt(0);
   }
 
   DistributeSignal(signal);
 #else
   MOZ_ASSERT(NS_IsMainThread());
 
   InfallibleTArray<BluetoothNamedValue> props;
 
@@ -2328,46 +2317,44 @@ BluetoothServiceBluedroid::RemoteDeviceP
       BT_LOGD("Other non-handled device properties. Type: %d", p.mType);
     }
   }
 
   // BlueDroid wouldn't notify the status of connection, therefore, query the
   // connection state and append to properties array
   BT_APPEND_NAMED_VALUE(props, "Connected", IsConnected(aBdAddr));
 
-  if (sRequestedDeviceCountArray.IsEmpty()) {
+  if (mGetDeviceRequests.IsEmpty()) {
     /**
      * This is possible when
      *
      *  (1) the callback is called when BT is turning on, or
      *  (2) remote device properties get updated during discovery, or
      *  (3) as result of CreateBond
      */
-    if (sAdapterDiscovering) {
+    if (mDiscovering) {
       // Fire 'devicefound' again to update device name for (2).
       // See bug 1076553 for more information.
       DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("DeviceFound"),
                                        NS_LITERAL_STRING(KEY_ADAPTER),
                                        BluetoothValue(props)));
     }
     return;
   }
 
   // Use address as the index
-  sRemoteDevicesPack.AppendElement(
-    BluetoothNamedValue(nsString(aBdAddr), props));
-
-  if (--sRequestedDeviceCountArray[0] == 0) {
-    if (!sGetDeviceRunnableArray.IsEmpty()) {
-      DispatchReplySuccess(sGetDeviceRunnableArray[0], sRemoteDevicesPack);
-      sGetDeviceRunnableArray.RemoveElementAt(0);
+  mGetDeviceRequests[0].mDevicesPack.AppendElement(
+    BluetoothNamedValue(nsString(aBdAddr), propertiesArray));
+
+  if (--mGetDeviceRequests[0].mDeviceCount == 0) {
+    if (mGetDeviceRequests[0].mRunnable) {
+      DispatchReplySuccess(mGetDeviceRequests[0].mRunnable,
+                           mGetDeviceRequests[0].mDevicesPack);
     }
-
-    sRequestedDeviceCountArray.RemoveElementAt(0);
-    sRemoteDevicesPack.Clear();
+    mGetDeviceRequests.RemoveElementAt(0);
   }
 
   // Update to registered BluetoothDevice objects
   DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PropertyChanged"),
                                    nsString(aBdAddr),
                                    BluetoothValue(props)));
 #endif
 }
@@ -2416,18 +2403,18 @@ BluetoothServiceBluedroid::DeviceFoundNo
     } else if (p.mType == PROPERTY_UNKNOWN) {
       /* Bug 1065999: working around unknown properties */
     } else {
       BT_LOGD("Not handled remote device property: %d", p.mType);
     }
   }
 
   // Update <address, name> mapping
-  sDeviceNameMap.Remove(bdAddr);
-  sDeviceNameMap.Put(bdAddr, bdName);
+  mDeviceNameMap.Remove(bdAddr);
+  mDeviceNameMap.Put(bdAddr, bdName);
 
   DistributeSignal(NS_LITERAL_STRING("DeviceFound"),
                    NS_LITERAL_STRING(KEY_ADAPTER),
                    BluetoothValue(propertiesArray));
 #else
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothValue propertyValue;
@@ -2466,70 +2453,69 @@ BluetoothServiceBluedroid::DeviceFoundNo
 }
 
 void
 BluetoothServiceBluedroid::DiscoveryStateChangedNotification(bool aState)
 {
 #ifndef MOZ_B2G_BT_API_V1
   MOZ_ASSERT(NS_IsMainThread());
 
-  sAdapterDiscovering = aState;
+  mDiscovering = aState;
 
   // Fire PropertyChanged of Discovering
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
-  BT_APPEND_NAMED_VALUE(propertiesArray, "Discovering", sAdapterDiscovering);
+  BT_APPEND_NAMED_VALUE(propertiesArray, "Discovering", mDiscovering);
 
   DistributeSignal(NS_LITERAL_STRING("PropertyChanged"),
                    NS_LITERAL_STRING(KEY_ADAPTER),
                    BluetoothValue(propertiesArray));
 
   // Reply that Promise is resolved
-  if (!sChangeDiscoveryRunnableArray.IsEmpty()) {
-    DispatchReplySuccess(sChangeDiscoveryRunnableArray[0]);
-    sChangeDiscoveryRunnableArray.RemoveElementAt(0);
+  if (!mChangeDiscoveryRunnables.IsEmpty()) {
+    DispatchReplySuccess(mChangeDiscoveryRunnables[0]);
+    mChangeDiscoveryRunnables.RemoveElementAt(0);
   }
 #else
   MOZ_ASSERT(NS_IsMainThread());
 
-  sAdapterDiscovering = aState;
+  mDiscovering = aState;
 
   DistributeSignal(
     BluetoothSignal(NS_LITERAL_STRING(DISCOVERY_STATE_CHANGED_ID),
-                    NS_LITERAL_STRING(KEY_ADAPTER), sAdapterDiscovering));
+                    NS_LITERAL_STRING(KEY_ADAPTER), mDiscovering));
 
   // Distribute "PropertyChanged" signal to notice adapter this change since
   // Bluedroid don' treat "discovering" as a property of adapter.
   InfallibleTArray<BluetoothNamedValue> props;
-  BT_APPEND_NAMED_VALUE(props, "Discovering",
-                        BluetoothValue(sAdapterDiscovering));
+  BT_APPEND_NAMED_VALUE(props, "Discovering", mDiscovering);
   DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PropertyChanged"),
                                    NS_LITERAL_STRING(KEY_ADAPTER),
                                    BluetoothValue(props)));
 #endif
 }
 
 void
 BluetoothServiceBluedroid::PinRequestNotification(const nsAString& aRemoteBdAddr,
                                                   const nsAString& aBdName,
                                                   uint32_t aCod)
 {
 #ifndef MOZ_B2G_BT_API_V1
   MOZ_ASSERT(NS_IsMainThread());
 
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
 
-  // If |aBdName| is empty, get device name from |sDeviceNameMap|;
+  // If |aBdName| is empty, get device name from |mDeviceNameMap|;
   // Otherwise update <address, name> mapping with |aBdName|
   nsString bdAddr(aRemoteBdAddr);
   nsString bdName(aBdName);
   if (bdName.IsEmpty()) {
-    sDeviceNameMap.Get(bdAddr, &bdName);
+    mDeviceNameMap.Get(bdAddr, &bdName);
   } else {
-    sDeviceNameMap.Remove(bdAddr);
-    sDeviceNameMap.Put(bdAddr, bdName);
+    mDeviceNameMap.Remove(bdAddr);
+    mDeviceNameMap.Put(bdAddr, bdName);
   }
 
   BT_APPEND_NAMED_VALUE(propertiesArray, "address", bdAddr);
   BT_APPEND_NAMED_VALUE(propertiesArray, "name", bdName);
   BT_APPEND_NAMED_VALUE(propertiesArray, "passkey", EmptyString());
   BT_APPEND_NAMED_VALUE(propertiesArray, "type",
                         NS_LITERAL_STRING(PAIRING_REQ_TYPE_ENTERPINCODE));
 
@@ -2557,25 +2543,25 @@ BluetoothServiceBluedroid::SspRequestNot
   const nsAString& aRemoteBdAddr, const nsAString& aBdName, uint32_t aCod,
   BluetoothSspVariant aPairingVariant, uint32_t aPassKey)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
 #ifndef MOZ_B2G_BT_API_V1
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
 
-  // If |aBdName| is empty, get device name from |sDeviceNameMap|;
+  // If |aBdName| is empty, get device name from |mDeviceNameMap|;
   // Otherwise update <address, name> mapping with |aBdName|
   nsString bdAddr(aRemoteBdAddr);
   nsString bdName(aBdName);
   if (bdName.IsEmpty()) {
-    sDeviceNameMap.Get(bdAddr, &bdName);
+    mDeviceNameMap.Get(bdAddr, &bdName);
   } else {
-    sDeviceNameMap.Remove(bdAddr);
-    sDeviceNameMap.Put(bdAddr, bdName);
+    mDeviceNameMap.Remove(bdAddr);
+    mDeviceNameMap.Put(bdAddr, bdName);
   }
 
   /**
    * Assign pairing request type and passkey based on the pairing variant.
    *
    * passkey value based on pairing request type:
    * 1) aPasskey: PAIRING_REQ_TYPE_CONFIRMATION and
    *              PAIRING_REQ_TYPE_DISPLAYPASSKEY
@@ -2643,41 +2629,41 @@ BluetoothServiceBluedroid::BondStateChan
     if (!bonded) { // Active/passive pair failed
       BT_LOGR("Pair failed! Abort pairing.");
 
       // Notify adapter of pairing aborted
       DistributeSignal(NS_LITERAL_STRING(PAIRING_ABORTED_ID),
                        NS_LITERAL_STRING(KEY_ADAPTER));
 
       // Reject pair promise
-      if (!sBondingRunnableArray.IsEmpty()) {
-        DispatchReplyError(sBondingRunnableArray[0], aStatus);
-        sBondingRunnableArray.RemoveElementAt(0);
+      if (!mCreateBondRunnables.IsEmpty()) {
+        DispatchReplyError(mCreateBondRunnables[0], aStatus);
+        mCreateBondRunnables.RemoveElementAt(0);
       }
-    } else if (!sUnbondingRunnableArray.IsEmpty()) { // Active unpair failed
+    } else if (!mRemoveBondRunnables.IsEmpty()) { // Active unpair failed
       // Reject unpair promise
-      DispatchReplyError(sUnbondingRunnableArray[0], aStatus);
-      sUnbondingRunnableArray.RemoveElementAt(0);
+      DispatchReplyError(mRemoveBondRunnables[0], aStatus);
+      mRemoveBondRunnables.RemoveElementAt(0);
     }
 
     return;
   }
 
   // Query pairing device name from hash table
   nsString remoteBdAddr(aRemoteBdAddr);
   nsString remotebdName;
-  sDeviceNameMap.Get(remoteBdAddr, &remotebdName);
+  mDeviceNameMap.Get(remoteBdAddr, &remotebdName);
 
   // Update bonded address array and append pairing device name
   InfallibleTArray<BluetoothNamedValue> propertiesArray;
   if (!bonded) {
-    sAdapterBondedAddressArray.RemoveElement(remoteBdAddr);
+    mBondedAddresses.RemoveElement(remoteBdAddr);
   } else {
-    if (!sAdapterBondedAddressArray.Contains(remoteBdAddr)) {
-      sAdapterBondedAddressArray.AppendElement(remoteBdAddr);
+    if (!mBondedAddresses.Contains(remoteBdAddr)) {
+      mBondedAddresses.AppendElement(remoteBdAddr);
     }
 
     // We don't assert |!remotebdName.IsEmpty()| since empty string is also
     // valid, according to Bluetooth Core Spec. v3.0 - Sec. 6.22:
     // "a valid Bluetooth name is a UTF-8 encoding string which is up to 248
     // bytes in length."
     BT_APPEND_NAMED_VALUE(propertiesArray, "Name", remotebdName);
   }
@@ -2691,61 +2677,61 @@ BluetoothServiceBluedroid::BondStateChan
   // Notify adapter of device paired/unpaired
   BT_INSERT_NAMED_VALUE(propertiesArray, 0, "Address", remoteBdAddr);
   DistributeSignal(bonded ? NS_LITERAL_STRING(DEVICE_PAIRED_ID)
                           : NS_LITERAL_STRING(DEVICE_UNPAIRED_ID),
                    NS_LITERAL_STRING(KEY_ADAPTER),
                    BluetoothValue(propertiesArray));
 
   // Resolve existing pair/unpair promise
-  if (bonded && !sBondingRunnableArray.IsEmpty()) {
-    DispatchReplySuccess(sBondingRunnableArray[0]);
-    sBondingRunnableArray.RemoveElementAt(0);
-  } else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) {
-    DispatchReplySuccess(sUnbondingRunnableArray[0]);
-    sUnbondingRunnableArray.RemoveElementAt(0);
+  if (bonded && !mCreateBondRunnables.IsEmpty()) {
+    DispatchReplySuccess(mCreateBondRunnables[0]);
+    mCreateBondRunnables.RemoveElementAt(0);
+  } else if (!bonded && !mRemoveBondRunnables.IsEmpty()) {
+    DispatchReplySuccess(mRemoveBondRunnables[0]);
+    mRemoveBondRunnables.RemoveElementAt(0);
   }
 #else
   if (aState == BOND_STATE_BONDED &&
-      sAdapterBondedAddressArray.Contains(aRemoteBdAddr)) {
+      mBondedAddresses.Contains(aRemoteBdAddr)) {
     // See bug 940271 for more details about this case.
     return;
   }
 
   switch (aStatus) {
     case STATUS_SUCCESS:
     {
       bool bonded;
       if (aState == BOND_STATE_NONE) {
         bonded = false;
-        sAdapterBondedAddressArray.RemoveElement(aRemoteBdAddr);
+        mBondedAddresses.RemoveElement(aRemoteBdAddr);
       } else if (aState == BOND_STATE_BONDED) {
         bonded = true;
-        sAdapterBondedAddressArray.AppendElement(aRemoteBdAddr);
+        mBondedAddresses.AppendElement(aRemoteBdAddr);
       } else {
         return;
       }
 
       // Update bonded address list to BluetoothAdapter
       InfallibleTArray<BluetoothNamedValue> propertiesChangeArray;
       BT_APPEND_NAMED_VALUE(propertiesChangeArray, "Devices",
-                            sAdapterBondedAddressArray);
+                            mBondedAddresses);
 
       DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("PropertyChanged"),
                                        NS_LITERAL_STRING(KEY_ADAPTER),
                                        BluetoothValue(propertiesChangeArray)));
 
-      if (bonded && !sBondingRunnableArray.IsEmpty()) {
-        DispatchReplySuccess(sBondingRunnableArray[0]);
-
-        sBondingRunnableArray.RemoveElementAt(0);
-      } else if (!bonded && !sUnbondingRunnableArray.IsEmpty()) {
-        DispatchReplySuccess(sUnbondingRunnableArray[0]);
-
-        sUnbondingRunnableArray.RemoveElementAt(0);
+      if (bonded && !mCreateBondRunnables.IsEmpty()) {
+        DispatchReplySuccess(mCreateBondRunnables[0]);
+
+        mCreateBondRunnables.RemoveElementAt(0);
+      } else if (!bonded && !mRemoveBondRunnables.IsEmpty()) {
+        DispatchReplySuccess(mRemoveBondRunnables[0]);
+
+        mRemoveBondRunnables.RemoveElementAt(0);
       }
 
       // Update bonding status to gaia
       InfallibleTArray<BluetoothNamedValue> propertiesArray;
       BT_APPEND_NAMED_VALUE(propertiesArray, "address", nsString(aRemoteBdAddr));
       BT_APPEND_NAMED_VALUE(propertiesArray, "status", bonded);
 
       DistributeSignal(
@@ -2758,30 +2744,30 @@ BluetoothServiceBluedroid::BondStateChan
     case STATUS_AUTH_FAILURE:
     case STATUS_RMT_DEV_DOWN:
     {
       InfallibleTArray<BluetoothNamedValue> propertiesArray;
       DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("Cancel"),
                                  NS_LITERAL_STRING(KEY_LOCAL_AGENT),
                                  BluetoothValue(propertiesArray)));
 
-      if (!sBondingRunnableArray.IsEmpty()) {
-        DispatchReplyError(sBondingRunnableArray[0],
+      if (!mCreateBondRunnables.IsEmpty()) {
+        DispatchReplyError(mCreateBondRunnables[0],
                            NS_LITERAL_STRING("Authentication failure"));
-        sBondingRunnableArray.RemoveElementAt(0);
+        mCreateBondRunnables.RemoveElementAt(0);
       }
       break;
     }
     default:
       BT_WARNING("Got an unhandled status of BondStateChangedCallback!");
       // Dispatch a reply to unblock the waiting status of pairing.
-      if (!sBondingRunnableArray.IsEmpty()) {
-        DispatchReplyError(sBondingRunnableArray[0],
+      if (!mCreateBondRunnables.IsEmpty()) {
+        DispatchReplyError(mCreateBondRunnables[0],
                            NS_LITERAL_STRING("Internal failure"));
-        sBondingRunnableArray.RemoveElementAt(0);
+        mCreateBondRunnables.RemoveElementAt(0);
       }
       break;
   }
 #endif
 }
 
 void
 BluetoothServiceBluedroid::AclStateChangedNotification(
@@ -2836,35 +2822,35 @@ BluetoothServiceBluedroid::BackendErrorN
    */
   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   NS_ENSURE_TRUE_VOID(hfp);
   hfp->HandleBackendError();
   BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   NS_ENSURE_TRUE_VOID(a2dp);
   a2dp->HandleBackendError();
 
-  sIsRestart = true;
+  mIsRestart = true;
   BT_LOGR("Recovery step2: stop bluetooth");
 #ifndef MOZ_B2G_BT_API_V1
   StopBluetooth(false, nullptr);
 #else
   StopBluetooth(false);
 #endif
 }
 
 void
 BluetoothServiceBluedroid::CompleteToggleBt(bool aEnabled)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (sIsRestart && !aEnabled && sIsFirstTimeToggleOffBt) {
+  if (mIsRestart && !aEnabled && mIsFirstTimeToggleOffBt) {
     // Both StopBluetooth and AdapterStateChangedNotification
     // trigger CompleteToggleBt. We don't need to call CompleteToggleBt again
-  } else if (sIsRestart && !aEnabled && !sIsFirstTimeToggleOffBt) {
+  } else if (mIsRestart && !aEnabled && !mIsFirstTimeToggleOffBt) {
     // Recovery step 3: cleanup and deinit Profile managers
-    BT_LOGR("CompleteToggleBt set sIsFirstTimeToggleOffBt = true");
-    sIsFirstTimeToggleOffBt = true;
+    BT_LOGR("CompleteToggleBt set mIsFirstTimeToggleOffBt = true");
+    mIsFirstTimeToggleOffBt = true;
     BluetoothService::CompleteToggleBt(aEnabled);
     AdapterStateChangedNotification(false);
   } else {
     BluetoothService::CompleteToggleBt(aEnabled);
   }
 }
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.h
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.h
@@ -5,16 +5,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluedroid_BluetoothServiceBluedroid_h
 #define mozilla_dom_bluetooth_bluedroid_BluetoothServiceBluedroid_h
 
 #include "BluetoothCommon.h"
 #include "BluetoothInterface.h"
 #include "BluetoothService.h"
+#ifndef MOZ_B2G_BT_API_V1
+#include "nsDataHashtable.h"
+#endif
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothServiceBluedroid : public BluetoothService
                                 , public BluetoothNotificationHandler
 {
   class CancelDiscoveryResultHandler;
   class CleanupResultHandler;
@@ -32,16 +35,18 @@ class BluetoothServiceBluedroid : public
   class ProfileDeinitResultHandler;
   class ProfileInitResultHandler;
   class RemoveBondResultHandler;
   class SetAdapterPropertyDiscoverableResultHandler;
   class SetAdapterPropertyResultHandler;
   class SspReplyResultHandler;
   class StartDiscoveryResultHandler;
 
+  class GetDeviceRequest;
+
 public:
   BluetoothServiceBluedroid();
   ~BluetoothServiceBluedroid();
 
 #ifndef MOZ_B2G_BT_API_V1
   virtual nsresult StartInternal(BluetoothReplyRunnable* aRunnable);
   virtual nsresult StopInternal(BluetoothReplyRunnable* aRunnable);
 #else
@@ -385,13 +390,51 @@ protected:
   static bool EnsureBluetoothHalLoad();
 
   static void ClassToIcon(uint32_t aClass, nsAString& aRetIcon);
 
   uint16_t UuidToServiceClassInt(const BluetoothUuid& mUuid);
 
   static bool IsConnected(const nsAString& aRemoteBdAddr);
 #endif
+
+  // Adapter properties
+  nsString mBdAddress;
+  nsString mBdName;
+  bool mEnabled;
+  bool mDiscoverable;
+  bool mDiscovering;
+  nsTArray<nsString> mBondedAddresses;
+#ifndef MOZ_B2G_BT_API_V1
+  // Missing in Bluetooth v2
+#else
+  uint32_t mDiscoverableTimeout;
+#endif
+
+  // Backend error recovery
+  bool mIsRestart;
+  bool mIsFirstTimeToggleOffBt;
+
+  // Array of get device requests. Each request remembers
+  // 1) remaining device count to receive properties,
+  // 2) received remote device properties, and
+  // 3) runnable to reply success/error
+  nsTArray<GetDeviceRequest> mGetDeviceRequests;
+
+  // Runnable arrays
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mSetAdapterPropertyRunnables;
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mCreateBondRunnables;
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mRemoveBondRunnables;
+
+#ifndef MOZ_B2G_BT_API_V1
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mChangeAdapterStateRunnables;
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mChangeDiscoveryRunnables;
+  nsTArray<nsRefPtr<BluetoothReplyRunnable>> mFetchUuidsRunnables;
+
+  // <address, name> mapping table for remote devices
+  nsDataHashtable<nsStringHashKey, nsString> mDeviceNameMap;
+#endif
+
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif // mozilla_dom_bluetooth_bluedroid_BluetoothServiceBluedroid_h
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1008,35 +1008,32 @@ void MediaDecoder::ChangeState(PlayState
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   if (mNextState == aState) {
     mNextState = PLAY_STATE_PAUSED;
   }
 
   if (mPlayState == PLAY_STATE_SHUTDOWN) {
-    GetReentrantMonitor().NotifyAll();
     return;
   }
 
   DECODER_LOG("ChangeState %s => %s",
               ToPlayStateStr(mPlayState), ToPlayStateStr(aState));
   mPlayState = aState;
 
   if (mPlayState == PLAY_STATE_PLAYING) {
     ConstructMediaTracks();
   } else if (IsEnded()) {
     RemoveMediaTracks();
   }
 
   CancelDormantTimer();
   // Start dormant timer if necessary
   StartDormantTimer();
-
-  GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoder::UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisibility)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mShuttingDown)
     return;
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -1093,25 +1093,21 @@ protected:
   // Media duration set explicitly by JS. At present, this is only ever present
   // for MSE.
   Canonical<Maybe<double>> mExplicitDuration;
 
   // Set to one of the valid play states.
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
-  // Any change to the state on the main thread must call NotifyAll on the
-  // monitor so the decode thread can wake up.
   Canonical<PlayState> mPlayState;
 
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
-  // Any change to the state must call NotifyAll on the monitor.
-  // This can only be PLAY_STATE_PAUSED or PLAY_STATE_PLAYING.
   Canonical<PlayState> mNextState;
 
   // True if the decoder is seeking.
   Canonical<bool> mLogicallySeeking;
 
   // True if the media is same-origin with the element. Data can only be
   // passed to MediaStreams when this is true.
   Canonical<bool> mSameOriginMedia;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -655,19 +655,16 @@ MediaDecoderStateMachine::Push(VideoData
   MOZ_ASSERT(aSample);
   // TODO: Send aSample to MSG and recalculate readystate before pushing,
   // otherwise AdvanceFrame may pop the sample before we have a chance
   // to reach playing.
   aSample->mFrameID = ++mCurrentFrameID;
   VideoQueue().Push(aSample);
   UpdateNextFrameStatus();
   DispatchDecodeTasksIfNeeded();
-
-  // XXXbholley - Is this still necessary?
-  mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void
 MediaDecoderStateMachine::PushFront(VideoData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
 
@@ -689,19 +686,16 @@ MediaDecoderStateMachine::OnAudioPopped(
 void
 MediaDecoderStateMachine::OnVideoPopped(const MediaData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecoder->UpdatePlaybackOffset(aSample->mOffset);
   UpdateNextFrameStatus();
   DispatchVideoDecodeTaskIfNeeded();
-  // Notify the decode thread that the video queue's buffers may have
-  // free'd up space for more frames.
-  mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void
 MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
                                        MediaDecoderReader::NotDecodedReason aReason)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@@ -773,17 +767,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
   }
   switch (mState) {
     case DECODER_STATE_BUFFERING:
     case DECODER_STATE_DECODING: {
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       CheckIfDecodeComplete();
-      mDecoder->GetReentrantMonitor().NotifyAll();
       // Schedule the state machine to notify track ended as soon as possible.
       if (mAudioCaptured) {
         ScheduleStateMachine();
       }
       return;
     }
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeek.Exists()) {
@@ -1088,17 +1081,16 @@ void MediaDecoderStateMachine::MaybeStar
   StartAudioThread();
 
   // Tell DecodedStream to start playback with specified start time and media
   // info. This is consistent with how we create AudioSink in StartAudioThread().
   if (mAudioCaptured) {
     mDecodedStream->StartPlayback(GetMediaTime(), mInfo);
   }
 
-  mDecoder->GetReentrantMonitor().NotifyAll();
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld)", aTime);
   AssertCurrentThreadInMonitor();
@@ -1255,23 +1247,21 @@ void MediaDecoderStateMachine::SetDorman
     // Note that we do not wait for the decode task queue to go idle before
     // queuing the ReleaseMediaResources task - instead, we disconnect promises,
     // reset state, and put a ResetDecode in the decode task queue. Any tasks
     // that run after ResetDecode are supposed to run with a clean slate. We rely
     // on that in other places (i.e. seeking), so it seems reasonable to rely on
     // it here as well.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources);
     DecodeTaskQueue()->Dispatch(r.forget());
-    mDecoder->GetReentrantMonitor().NotifyAll();
   } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
     mDecodingFrozenAtStateDecoding = true;
     ScheduleStateMachine();
     mDecodingFirstFrame = true;
     SetState(DECODER_STATE_DECODING_NONE);
-    mDecoder->GetReentrantMonitor().NotifyAll();
   }
 }
 
 void MediaDecoderStateMachine::Shutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Once we've entered the shutdown state here there's no going back.
@@ -1904,20 +1894,16 @@ MediaDecoderStateMachine::DecodeError()
   }
 
   // Change state to error, which will cause the state machine to wait until
   // the MediaDecoder shuts it down.
   SetState(DECODER_STATE_ERROR);
   ScheduleStateMachine();
   DECODER_WARN("Decode error, changed state to ERROR");
 
-  // XXXbholley - Is anybody actually waiting on this monitor, or is it just
-  // a leftover from when we used to do sync dispatch for the below?
-  mDecoder->GetReentrantMonitor().NotifyAll();
-
   // MediaDecoder::DecodeError notifies the owner, and then shuts down the state
   // machine.
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
   AbstractThread::MainThread()->Dispatch(event.forget());
 }
 
 void
@@ -2351,18 +2337,16 @@ nsresult MediaDecoderStateMachine::RunSt
                     OutOfDecodedVideo(), VideoRequestStatus());
         return NS_OK;
       }
 
       DECODER_LOG("Changed state from BUFFERING to DECODING");
       DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
       StartDecoding();
 
-      // Notify to allow blocked decoder thread to continue
-      mDecoder->GetReentrantMonitor().NotifyAll();
       NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
       return NS_OK;
     }
 
     case DECODER_STATE_SEEKING: {
       if (mPendingSeek.Exists()) {
         InitiateSeek();
       }
@@ -3071,19 +3055,16 @@ void MediaDecoderStateMachine::OnAudioSi
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio.");
 
   mAudioSinkPromise.Complete();
   ResyncAudioClock();
   mAudioCompleted = true;
-
-  // Kick the decode thread; it may be sleeping waiting for this to finish.
-  mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoderStateMachine::OnAudioSinkError()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio.");
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -107,19 +107,18 @@ extern PRLogModuleInfo* gMediaDecoderLog
 extern PRLogModuleInfo* gMediaSampleLog;
 
 /*
   The state machine class. This manages the decoding and seeking in the
   MediaDecoderReader on the decode task queue, and A/V sync on the shared
   state machine thread, and controls the audio "push" thread.
 
   All internal state is synchronised via the decoder monitor. State changes
-  are either propagated by NotifyAll on the monitor (typically when state
-  changes need to be propagated to non-state machine threads) or by scheduling
-  the state machine to run another cycle on the shared state machine thread.
+  are propagated by scheduling the state machine to run another cycle on the
+  shared state machine thread.
 
   See MediaDecoder.h for more details.
 */
 class MediaDecoderStateMachine
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachine)
 public:
   typedef MediaDecoderReader::AudioDataPromise AudioDataPromise;
@@ -881,18 +880,16 @@ private:
   // Queue of audio frames. This queue is threadsafe, and is accessed from
   // the audio, decoder, state machine, and main threads.
   MediaQueue<MediaData> mAudioQueue;
   // Queue of video frames. This queue is threadsafe, and is accessed from
   // the decoder, state machine, and main threads.
   MediaQueue<MediaData> mVideoQueue;
 
   // The decoder monitor must be obtained before modifying this state.
-  // NotifyAll on the monitor must be called when the state is changed so
-  // that interested threads can wake up and alter behaviour if appropriate
   // Accessed on state machine, audio, main, and AV thread.
   Watchable<State> mState;
 
   // The task queue in which we run decode tasks. This is referred to as
   // the "decode thread", though in practise tasks can run on a different
   // thread every time they're called.
   TaskQueue* DecodeTaskQueue() const { return mReader->OwnerThread(); }
 
--- a/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -413,23 +413,22 @@ position_cluster (const hb_ot_shape_plan
 void
 _hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
 				hb_font_t *font,
 				hb_buffer_t  *buffer)
 {
   _hb_buffer_assert_gsubgpos_vars (buffer);
 
   unsigned int start = 0;
-  unsigned int last_cluster = buffer->info[0].cluster;
   unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
-    if (buffer->info[i].cluster != last_cluster) {
+    if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) {
       position_cluster (plan, font, buffer, start, i);
       start = i;
-      last_cluster = buffer->info[i].cluster;
     }
   position_cluster (plan, font, buffer, start, count);
 }
 
 
 /* Performs old-style TrueType kerning. */
 void
 _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
@@ -318,17 +318,17 @@ void
   /* First round, decompose */
 
   buffer->clear_output ();
   count = buffer->len;
   for (buffer->idx = 0; buffer->idx < count;)
   {
     unsigned int end;
     for (end = buffer->idx + 1; end < count; end++)
-      if (buffer->cur().cluster != buffer->info[end].cluster)
+      if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))))
         break;
 
     decompose_cluster (&c, end, might_short_circuit, always_short_circuit);
   }
   buffer->swap_buffers ();
 
 
   /* Second round, reorder (inplace) */
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -97,17 +97,17 @@ load 633322-1.html
 load 665218.html
 load 686190-1.html
 load 693143-1.html
 load 768079-1.html
 load 783041-1.html
 load 783041-2.html
 load 783041-3.html
 load 783041-4.html
-load 798853.html # bug 868792
+asserts-if(gtkWidget,0-1) load 798853.html # bug 868792
 asserts-if(winWidget,0-1) skip-if(B2G) load 815489.html
 load 836225-1.html
 load 839745-1.html
 load 856784-1.html
 load 893572-1.html
 load 893572-2.html
 load 893572-3.html
 load 893572-4.html
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -1563,16 +1563,18 @@ gfxHarfBuzzShaper::ShapeText(gfxContext 
     }
     hb_buffer_set_language(buffer, language);
 
     uint32_t length = aLength;
     hb_buffer_add_utf16(buffer,
                         reinterpret_cast<const uint16_t*>(aText),
                         length, 0, length);
 
+    hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
+
     hb_shape(mHBFont, buffer, features.Elements(), features.Length());
 
     if (isRightToLeft) {
         hb_buffer_reverse(buffer);
     }
 
     nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
                                    aText, buffer, aVertical);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4760,16 +4760,20 @@ CanOptimizeDenseOrUnboxedArraySetElem(JS
 
     *isAddingCaseOut = false;
     *protoDepthOut = 0;
 
     // Some initial sanity checks.
     if (initLength < oldInitLength || capacity < oldCapacity)
         return false;
 
+    // Unboxed arrays need to be able to emit floating point code.
+    if (obj->is<UnboxedArrayObject>() && !obj->runtimeFromMainThread()->jitSupportsFloatingPoint)
+        return false;
+
     Shape* shape = obj->maybeShape();
 
     // Cannot optimize if the shape changed.
     if (oldShape != shape)
         return false;
 
     // Cannot optimize if the capacity changed.
     if (oldCapacity != capacity)
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -3118,22 +3118,17 @@ GenerateSetUnboxed(JSContext* cx, MacroA
         if (unboxedType == JSVAL_TYPE_OBJECT)
             masm.callPreBarrier(address, MIRType_Object);
         else if (unboxedType == JSVAL_TYPE_STRING)
             masm.callPreBarrier(address, MIRType_String);
         else
             MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType));
     }
 
-    // If unboxed objects in this group have have never been converted to
-    // native objects then the type set check performed above ensures the value
-    // being written can be stored in the unboxed object.
-    Label* storeFailure = obj->group()->unboxedLayout().nativeGroup() ? &failure : nullptr;
-
-    masm.storeUnboxedProperty(address, unboxedType, value, storeFailure);
+    masm.storeUnboxedProperty(address, unboxedType, value, &failure);
 
     attacher.jumpRejoin(masm);
 
     masm.bind(&failurePopObject);
     masm.pop(object);
     masm.bind(&failure);
 
     attacher.jumpNextStub(masm);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -769,24 +769,23 @@ IonBuilder::inlineArrayPush(CallInfo& ca
     MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
     MDefinition* value = callInfo.getArg(0);
     if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
                                       &obj, nullptr, &value, /* canModify = */ false))
     {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return InliningStatus_NotInlined;
     }
-    MOZ_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
 
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
-    if (callInfo.thisArg()->type() != MIRType_Object)
+    if (obj->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
+    TemporaryTypeSet* thisTypes = obj->resultTypeSet();
     if (!thisTypes)
         return InliningStatus_NotInlined;
     const Class* clasp = thisTypes->getKnownClass(constraints());
     if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
                                   OBJECT_FLAG_LENGTH_OVERFLOW))
     {
@@ -803,23 +802,22 @@ IonBuilder::inlineArrayPush(CallInfo& ca
         thisTypes->convertDoubleElements(constraints());
     if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion) {
         trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
         return InliningStatus_NotInlined;
     }
 
     JSValueType unboxedType = JSVAL_TYPE_MAGIC;
     if (clasp == &UnboxedArrayObject::class_) {
-        unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr);
+        unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
         if (unboxedType == JSVAL_TYPE_MAGIC)
             return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
-    value = callInfo.getArg(0);
 
     if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
         conversion == TemporaryTypeSet::MaybeConvertToDoubles)
     {
         MInstruction* valueDouble = MToDouble::New(alloc(), value);
         current->add(valueDouble);
         value = valueDouble;
     }
@@ -934,16 +932,23 @@ IonBuilder::inlineArrayConcat(CallInfo& 
             continue;
 
         if (key->unknownProperties())
             return InliningStatus_NotInlined;
 
         HeapTypeSetKey elemTypes = key->property(JSID_VOID);
         if (!elemTypes.knownSubset(constraints(), thisElemTypes))
             return InliningStatus_NotInlined;
+
+        if (thisGroup->clasp() == &UnboxedArrayObject::class_ &&
+            !CanStoreUnboxedType(alloc(), thisGroup->unboxedLayout().elementType(),
+                                 MIRType_Value, elemTypes.maybeTypes()))
+        {
+            return InliningStatus_NotInlined;
+        }
     }
 
     // Inline the call.
     JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
     if (!templateObj || templateObj->group() != thisGroup)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1851,16 +1851,50 @@ jit::EqualTypes(MIRType type1, Temporary
         return TypeSetIncludes(typeset1, type2, nullptr);
     if (!typeset1 && typeset2)
         return TypeSetIncludes(typeset2, type1, nullptr);
 
     // Typesets should equal.
     return typeset1->equals(typeset2);
 }
 
+// Tests whether input/inputTypes can always be stored to an unboxed
+// object/array property with the given unboxed type.
+bool
+jit::CanStoreUnboxedType(TempAllocator& alloc,
+                         JSValueType unboxedType, MIRType input, TypeSet* inputTypes)
+{
+    TemporaryTypeSet types;
+
+    switch (unboxedType) {
+      case JSVAL_TYPE_BOOLEAN:
+      case JSVAL_TYPE_INT32:
+      case JSVAL_TYPE_DOUBLE:
+      case JSVAL_TYPE_STRING:
+        types.addType(TypeSet::PrimitiveType(unboxedType), alloc.lifoAlloc());
+        break;
+
+      case JSVAL_TYPE_OBJECT:
+        types.addType(TypeSet::AnyObjectType(), alloc.lifoAlloc());
+        types.addType(TypeSet::NullType(), alloc.lifoAlloc());
+        break;
+
+      default:
+        MOZ_CRASH("Bad unboxed type");
+    }
+
+    return TypeSetIncludes(&types, input, inputTypes);
+}
+
+static bool
+CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MDefinition* value)
+{
+    return CanStoreUnboxedType(alloc, unboxedType, value->type(), value->resultTypeSet());
+}
+
 bool
 MPhi::specializeType()
 {
 #ifdef DEBUG
     MOZ_ASSERT(!specialized_);
     specialized_ = true;
 #endif
 
@@ -5409,16 +5443,30 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
             // or a VM call is required. A VM call is always required if pobj
             // and pvalue cannot be modified.
             if (!canModify)
                 return true;
             success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue,
                                                 implicitType);
             break;
         }
+
+        // Perform additional filtering to make sure that any unboxed property
+        // being written can accommodate the value.
+        if (key->isGroup() && key->group()->maybeUnboxedLayout()) {
+            const UnboxedLayout& layout = key->group()->unboxedLayout();
+            if (name) {
+                const UnboxedLayout::Property* property = layout.lookup(name);
+                if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue))
+                    return true;
+            } else {
+                if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue))
+                    return true;
+            }
+        }
     }
 
     if (success)
         return false;
 
     // If all of the objects except one have property types which reflect the
     // value, and the remaining object has no types at all for the property,
     // add a guard that the object does not have that remaining object's type.
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2836,16 +2836,20 @@ MergeTypes(MIRType* ptype, TemporaryType
 
 bool
 TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);
 
 bool
 EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
            MIRType type2, TemporaryTypeSet* typeset2);
 
+bool
+CanStoreUnboxedType(TempAllocator& alloc,
+                    JSValueType unboxedType, MIRType input, TypeSet* inputTypes);
+
 #ifdef DEBUG
 bool
 IonCompilationCanUseNurseryPointers();
 #endif
 
 // Helper class to check that GC pointers embedded in MIR instructions are in
 // in the nursery only when the store buffer has been marked as needing to
 // cancel all ion compilations. Otherwise, off-thread Ion compilation and
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -124,17 +124,17 @@ class UnboxedLayout : public mozilla::Li
         js_free(traceList_);
 
         nativeGroup_.init(nullptr);
         nativeShape_.init(nullptr);
         replacementGroup_.init(nullptr);
         constructorCode_.init(nullptr);
     }
 
-    bool isArray() {
+    bool isArray() const {
         return elementType_ != JSVAL_TYPE_MAGIC;
     }
 
     void detachFromCompartment();
 
     const PropertyVector& properties() const {
         return properties_;
     }
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -392,20 +392,25 @@
  *   an array of such objects, as a global or static variable, or as the type of
  *   a new expression (unless placement new is being used). If a member of
  *   another class uses this class, or if another class inherits from this
  *   class, then it is considered to be a stack class as well, although this
  *   attribute need not be provided in such cases.
  * MOZ_NONHEAP_CLASS: Applies to all classes. Any class with this annotation is
  *   expected to live on the stack or in static storage, so it is a compile-time
  *   error to use it, or an array of such objects, as the type of a new
- *   expression (unless placement new is being used). If a member of another
- *   class uses this class, or if another class inherits from this class, then
- *   it is considered to be a non-heap class as well, although this attribute
- *   need not be provided in such cases.
+ *   expression. If a member of another class uses this class, or if another
+ *   class inherits from this class, then it is considered to be a non-heap class
+ *   as well, although this attribute need not be provided in such cases.
+ * MOZ_HEAP_CLASS: Applies to all classes. Any class with this annotation is
+ *   expected to live on the heap, so it is a compile-time error to use it, or
+ *   an array of such objects, as the type of a variable declaration, or as a
+ *   temporary object. If a member of another class uses this class, or if
+ *   another class inherits from this class, then it is considered to be a heap
+ *   class as well, although this attribute need not be provided in such cases.
  * MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS: Applies to all classes that are
  *   intended to prevent introducing static initializers.  This attribute
  *   currently makes it a compile-time error to instantiate these classes
  *   anywhere other than at the global scope, or as a static member of a class.
  * MOZ_TRIVIAL_CTOR_DTOR: Applies to all classes that must have both a trivial
  *   constructor and a trivial destructor.  Setting this attribute on a class
  *   makes it a compile-time error for that class to get a non-trivial
  *   constructor or destructor for any reason.
@@ -470,16 +475,17 @@
  *   declarations where an instance of the template should be considered, for
  *   static analysis purposes, to inherit any type annotations (such as
  *   MOZ_MUST_USE and MOZ_STACK_CLASS) from its template arguments.
  */
 #ifdef MOZ_CLANG_PLUGIN
 #  define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
 #  define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
 #  define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
+#  define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class")))
 #  define MOZ_TRIVIAL_CTOR_DTOR __attribute__((annotate("moz_trivial_ctor_dtor")))
 #  ifdef DEBUG
      /* in debug builds, these classes do have non-trivial constructors. */
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class")))
 #  else
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class"))) \
             MOZ_TRIVIAL_CTOR_DTOR
 #  endif
@@ -504,16 +510,17 @@
     _Pragma("clang diagnostic push") \
     _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
     __attribute__((annotate("moz_heap_allocator"))) \
     _Pragma("clang diagnostic pop")
 #else
 #  define MOZ_MUST_OVERRIDE /* nothing */
 #  define MOZ_STACK_CLASS /* nothing */
 #  define MOZ_NONHEAP_CLASS /* nothing */
+#  define MOZ_HEAP_CLASS /* nothing */
 #  define MOZ_TRIVIAL_CTOR_DTOR /* nothing */
 #  define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS /* nothing */
 #  define MOZ_IMPLICIT /* nothing */
 #  define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT /* nothing */
 #  define MOZ_HEAP_ALLOCATOR /* nothing */
 #  define MOZ_OWNING_REF /* nothing */
 #  define MOZ_NON_OWNING_REF /* nothing */
 #  define MOZ_UNSAFE_REF(reason) /* nothing */
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -107,14 +107,8 @@ export JS_GC_SMALL_CHUNK_SIZE=1
 
 # Enable FxAccount Avatar
 if test "$NIGHTLY_BUILD"; then
   MOZ_ANDROID_FIREFOX_ACCOUNT_PROFILES=1
 fi
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
-if test "$MOZ_OFFICIAL_BRANDING"; then
-  if test "$MOZ_UPDATE_CHANNEL" = "beta" -o \
-          "$MOZ_UPDATE_CHANNEL" = "release"; then
-    MOZ_REQUIRE_SIGNING=1
-  fi
-fi
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -1483,74 +1483,68 @@ CacheIndex::HasEntryChanged(CacheIndexEn
 
 void
 CacheIndex::ProcessPendingOperations()
 {
   LOG(("CacheIndex::ProcessPendingOperations()"));
 
   AssertOwnsLock();
 
-  mPendingUpdates.EnumerateEntries(&CacheIndex::UpdateEntryInIndex, this);
+  for (auto iter = mPendingUpdates.Iter(); !iter.Done(); iter.Next()) {
+    CacheIndexEntryUpdate* update = iter.Get();
+
+    LOG(("CacheIndex::ProcessPendingOperations() [hash=%08x%08x%08x%08x%08x]",
+         LOGSHA1(update->Hash())));
+
+    MOZ_ASSERT(update->IsFresh());
+
+    CacheIndexEntry* entry = mIndex.GetEntry(*update->Hash());
+
+    {
+      CacheIndexEntryAutoManage emng(update->Hash(), this);
+      emng.DoNotSearchInUpdates();
+
+      if (update->IsRemoved()) {
+        if (entry) {
+          if (entry->IsRemoved()) {
+            MOZ_ASSERT(entry->IsFresh());
+            MOZ_ASSERT(entry->IsDirty());
+          } else if (!entry->IsDirty() && entry->IsFileEmpty()) {
+            // Entries with empty file are not stored in index on disk. Just
+            // remove the entry, but only in case the entry is not dirty, i.e.
+            // the entry file was empty when we wrote the index.
+            mIndex.RemoveEntry(*update->Hash());
+            entry = nullptr;
+          } else {
+            entry->MarkRemoved();
+            entry->MarkDirty();
+            entry->MarkFresh();
+          }
+        }
+      } else if (entry) {
+        // Some information in mIndex can be newer than in mPendingUpdates (see
+        // bug 1074832). This will copy just those values that were really
+        // updated.
+        update->ApplyUpdate(entry);
+      } else {
+        // There is no entry in mIndex, copy all information from
+        // mPendingUpdates to mIndex.
+        entry = mIndex.PutEntry(*update->Hash());
+        *entry = *update;
+      }
+    }
+
+    iter.Remove();
+  }
 
   MOZ_ASSERT(mPendingUpdates.Count() == 0);
 
   EnsureCorrectStats();
 }
 
-// static
-PLDHashOperator
-CacheIndex::UpdateEntryInIndex(CacheIndexEntryUpdate *aEntry, void* aClosure)
-{
-  CacheIndex *index = static_cast<CacheIndex *>(aClosure);
-
-  LOG(("CacheFile::UpdateEntryInIndex() [hash=%08x%08x%08x%08x%08x]",
-       LOGSHA1(aEntry->Hash())));
-
-  MOZ_ASSERT(aEntry->IsFresh());
-
-  CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash());
-
-  CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
-  emng.DoNotSearchInUpdates();
-
-  if (aEntry->IsRemoved()) {
-    if (entry) {
-      if (entry->IsRemoved()) {
-        MOZ_ASSERT(entry->IsFresh());
-        MOZ_ASSERT(entry->IsDirty());
-      } else if (!entry->IsDirty() && entry->IsFileEmpty()) {
-        // Entries with empty file are not stored in index on disk. Just remove
-        // the entry, but only in case the entry is not dirty, i.e. the entry
-        // file was empty when we wrote the index.
-        index->mIndex.RemoveEntry(*aEntry->Hash());
-        entry = nullptr;
-      } else {
-        entry->MarkRemoved();
-        entry->MarkDirty();
-        entry->MarkFresh();
-      }
-    }
-
-    return PL_DHASH_REMOVE;
-  }
-
-  if (entry) {
-    // Some information in mIndex can be newer than in mPendingUpdates (see bug
-    // 1074832). This will copy just those values that were really updated.
-    aEntry->ApplyUpdate(entry);
-  } else {
-    // There is no entry in mIndex, copy all information from mPendingUpdates
-    // to mIndex.
-    entry = index->mIndex.PutEntry(*aEntry->Hash());
-    *entry = *aEntry;
-  }
-
-  return PL_DHASH_REMOVE;
-}
-
 bool
 CacheIndex::WriteIndexToDiskIfNeeded()
 {
   if (mState != READY || mShuttingDown) {
     return false;
   }
 
   if (!mLastDumpTime.IsNull() &&
@@ -1705,17 +1699,35 @@ CacheIndex::FinishWrite(bool aSucceeded)
   mIndexHandle = nullptr;
   mRWHash = nullptr;
   ReleaseBuffer();
 
   if (aSucceeded) {
     // Opening of the file must not be in progress if writing succeeded.
     MOZ_ASSERT(!mIndexFileOpener);
 
-    mIndex.EnumerateEntries(&CacheIndex::ApplyIndexChanges, this);
+    for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+      CacheIndexEntry* entry = iter.Get();
+
+      bool remove = false;
+      {
+        CacheIndexEntryAutoManage emng(entry->Hash(), this);
+
+        if (entry->IsRemoved()) {
+          emng.DoNotSearchInIndex();
+          remove = true;
+        } else if (entry->IsDirty()) {
+          entry->ClearDirty();
+        }
+      }
+      if (remove) {
+        iter.Remove();
+      }
+    }
+
     mIndexOnDiskIsValid = true;
   } else {
     if (mIndexFileOpener) {
       // If opening of the file is still in progress (e.g. WRITE process was
       // canceled by RemoveAll()) then we need to cancel the opener to make sure
       // that OnFileOpenedInternal() won't be called.
       mIndexFileOpener->Cancel();
       mIndexFileOpener = nullptr;
@@ -1762,36 +1774,16 @@ CacheIndex::CopyRecordsToRWBuf(CacheInde
 
   aEntry->WriteToBuf(data->mBuf);
   data->mBuf += sizeof(CacheIndexRecord);
   data->mProcessed++;
 
   return PL_DHASH_NEXT;
 }
 
-// static
-PLDHashOperator
-CacheIndex::ApplyIndexChanges(CacheIndexEntry *aEntry, void* aClosure)
-{
-  CacheIndex *index = static_cast<CacheIndex *>(aClosure);
-
-  CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
-
-  if (aEntry->IsRemoved()) {
-    emng.DoNotSearchInIndex();
-    return PL_DHASH_REMOVE;
-  }
-
-  if (aEntry->IsDirty()) {
-    aEntry->ClearDirty();
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 nsresult
 CacheIndex::GetFile(const nsACString &aName, nsIFile **_retval)
 {
   nsresult rv;
 
   nsCOMPtr<nsIFile> file;
   rv = mCacheDirectory->Clone(getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1966,17 +1958,23 @@ CacheIndex::WriteLogToDisk()
   mIndexStats.Log();
 
   PRFileDesc *fd = nullptr;
   rv = logFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
                                  0600, &fd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   WriteLogHelper wlh(fd);
-  mIndex.EnumerateEntries(&CacheIndex::WriteEntryToLog, &wlh);
+  for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+    CacheIndexEntry* entry = iter.Get();
+    if (entry->IsRemoved() || entry->IsDirty()) {
+      wlh.AddEntry(entry);
+    }
+    iter.Remove();
+  }
 
   rv = wlh.Finish();
   PR_Close(fd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = indexFile->OpenNSPRFileDesc(PR_RDWR, 0600, &fd);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1999,29 +1997,16 @@ CacheIndex::WriteLogToDisk()
   PR_Close(fd);
   if (bytesWritten != sizeof(CacheIndexHeader)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-// static
-PLDHashOperator
-CacheIndex::WriteEntryToLog(CacheIndexEntry *aEntry, void* aClosure)
-{
-  WriteLogHelper *wlh = static_cast<WriteLogHelper *>(aClosure);
-
-  if (aEntry->IsRemoved() || aEntry->IsDirty()) {
-    wlh->AddEntry(aEntry);
-  }
-
-  return PL_DHASH_REMOVE;
-}
-
 void
 CacheIndex::ReadIndexFromDisk()
 {
   LOG(("CacheIndex::ReadIndexFromDisk()"));
 
   nsresult rv;
 
   AssertOwnsLock();
@@ -2389,43 +2374,39 @@ CacheIndex::ProcessJournalEntry(CacheInd
 }
 
 void
 CacheIndex::EnsureNoFreshEntry()
 {
 #ifdef DEBUG_STATS
   CacheIndexStats debugStats;
   debugStats.DisableLogging();
-  mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats);
+  for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+    debugStats.BeforeChange(nullptr);
+    debugStats.AfterChange(iter.Get());
+  }
   MOZ_ASSERT(debugStats.Fresh() == 0);
 #endif
 }
 
 void
 CacheIndex::EnsureCorrectStats()
 {
 #ifdef DEBUG_STATS
   MOZ_ASSERT(mPendingUpdates.Count() == 0);
   CacheIndexStats debugStats;
   debugStats.DisableLogging();
-  mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats);
+  for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+    debugStats.BeforeChange(nullptr);
+    debugStats.AfterChange(iter.Get());
+  }
   MOZ_ASSERT(debugStats == mIndexStats);
 #endif
 }
 
-// static
-PLDHashOperator
-CacheIndex::SumIndexStats(CacheIndexEntry *aEntry, void* aClosure)
-{
-  CacheIndexStats *stats = static_cast<CacheIndexStats *>(aClosure);
-  stats->BeforeChange(nullptr);
-  stats->AfterChange(aEntry);
-  return PL_DHASH_NEXT;
-}
-
 void
 CacheIndex::FinishRead(bool aSucceeded)
 {
   LOG(("CacheIndex::FinishRead() [succeeded=%d]", aSucceeded));
   AssertOwnsLock();
 
   MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == READING);
 
@@ -2472,17 +2453,17 @@ CacheIndex::FinishRead(bool aSucceeded)
     return;
   }
 
   if (!mIndexOnDiskIsValid) {
     MOZ_ASSERT(mTmpJournal.Count() == 0);
     EnsureNoFreshEntry();
     ProcessPendingOperations();
     // Remove all entries that we haven't seen during this session
-    mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
+    RemoveNonFreshEntries();
     StartUpdatingIndex(true);
     return;
   }
 
   if (!mJournalReadSuccessfully) {
     mTmpJournal.Clear();
     EnsureNoFreshEntry();
     ProcessPendingOperations();
@@ -3060,44 +3041,46 @@ CacheIndex::FinishUpdate(bool aSucceeded
   if (mState == SHUTDOWN) {
     return;
   }
 
   if (mState == UPDATING && aSucceeded) {
     // If we've iterated over all entries successfully then all entries that
     // really exist on the disk are now marked as fresh. All non-fresh entries
     // don't exist anymore and must be removed from the index.
-    mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
+    RemoveNonFreshEntries();
   }
 
   // Make sure we won't start update. If the build or update failed, there is no
   // reason to believe that it will succeed next time.
   mIndexNeedsUpdate = false;
 
   ChangeState(READY);
   mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
 }
 
-// static
-PLDHashOperator
-CacheIndex::RemoveNonFreshEntries(CacheIndexEntry *aEntry, void* aClosure)
+void
+CacheIndex::RemoveNonFreshEntries()
 {
-  if (aEntry->IsFresh()) {
-    return PL_DHASH_NEXT;
+  for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+    CacheIndexEntry* entry = iter.Get();
+    if (entry->IsFresh()) {
+      continue;
+    }
+
+    LOG(("CacheIndex::RemoveNonFreshEntries() - Removing entry. "
+         "[hash=%08x%08x%08x%08x%08x]", LOGSHA1(entry->Hash())));
+
+    {
+      CacheIndexEntryAutoManage emng(entry->Hash(), this);
+      emng.DoNotSearchInIndex();
+    }
+
+    iter.Remove();
   }
-
-  LOG(("CacheFile::RemoveNonFreshEntries() - Removing entry. "
-       "[hash=%08x%08x%08x%08x%08x]", LOGSHA1(aEntry->Hash())));
-
-  CacheIndex *index = static_cast<CacheIndex *>(aClosure);
-
-  CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
-  emng.DoNotSearchInIndex();
-
-  return PL_DHASH_REMOVE;
 }
 
 // static
 char const *
 CacheIndex::StateString(EState aState)
 {
   switch (aState) {
     case INITIAL:  return "INITIAL";
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -718,18 +718,16 @@ private:
   // Checks whether any of the information about the entry has changed.
   static bool HasEntryChanged(CacheIndexEntry *aEntry,
                               const uint32_t  *aFrecency,
                               const uint32_t  *aExpirationTime,
                               const uint32_t  *aSize);
 
   // Merge all pending operations from mPendingUpdates into mIndex.
   void ProcessPendingOperations();
-  static PLDHashOperator UpdateEntryInIndex(CacheIndexEntryUpdate *aEntry,
-                                            void* aClosure);
 
   // Following methods perform writing of the index file.
   //
   // The index is written periodically, but not earlier than once in
   // kMinDumpInterval and there must be at least kMinUnwrittenChanges
   // differences between index on disk and in memory. Index is always first
   // written to a temporary file and the old index file is replaced when the
   // writing process succeeds.
@@ -742,35 +740,30 @@ private:
   // Serializes part of mIndex hashtable to the write buffer a writes the buffer
   // to the file.
   void WriteRecords();
   // Finalizes writing process.
   void FinishWrite(bool aSucceeded);
 
   static PLDHashOperator CopyRecordsToRWBuf(CacheIndexEntry *aEntry,
                                             void* aClosure);
-  static PLDHashOperator ApplyIndexChanges(CacheIndexEntry *aEntry,
-                                           void* aClosure);
 
   // Following methods perform writing of the journal during shutdown. All these
   // methods must be called only during shutdown since they write/delete files
   // directly on the main thread instead of using CacheFileIOManager that does
   // it asynchronously on IO thread. Journal contains only entries that are
   // dirty, i.e. changes that are not present in the index file on the disk.
   // When the log is written successfully, the dirty flag in index file is
   // cleared.
   nsresult GetFile(const nsACString &aName, nsIFile **_retval);
   nsresult RemoveFile(const nsACString &aName);
   void     RemoveIndexFromDisk();
   // Writes journal to the disk and clears dirty flag in index header.
   nsresult WriteLogToDisk();
 
-  static PLDHashOperator WriteEntryToLog(CacheIndexEntry *aEntry,
-                                         void* aClosure);
-
   // Following methods perform reading of the index from the disk.
   //
   // Index is read at startup just after initializing the CacheIndex. There are
   // 3 files used when manipulating with index: index file, journal file and
   // a temporary file. All files contain the hash of the data, so we can check
   // whether the content is valid and complete. Index file contains also a dirty
   // flag in the index header which is unset on a clean shutdown. During opening
   // and reading of the files we determine the status of the whole index from
@@ -815,17 +808,16 @@ private:
   // Merges entries from journal into mIndex.
   void MergeJournal();
   // In debug build this method checks that we have no fresh entry in mIndex
   // after we finish reading index and before we process pending operations.
   void EnsureNoFreshEntry();
   // In debug build this method is called after processing pending operations
   // to make sure mIndexStats contains correct information.
   void EnsureCorrectStats();
-  static PLDHashOperator SumIndexStats(CacheIndexEntry *aEntry, void* aClosure);
   // Finalizes reading process.
   void FinishRead(bool aSucceeded);
 
   static PLDHashOperator ProcessJournalEntry(CacheIndexEntry *aEntry,
                                              void* aClosure);
 
   // Following methods perform updating and building of the index.
   // Timer callback that starts update or build process.
@@ -848,18 +840,17 @@ private:
   void StartUpdatingIndex(bool aRebuild);
   // Iterates through all files in entries directory that we didn't create/open
   // during this session and theirs last modified time is newer than timestamp
   // in the index header. Parses the files and adds the entries to the index.
   void UpdateIndex();
   // Finalizes update or build process.
   void FinishUpdate(bool aSucceeded);
 
-  static PLDHashOperator RemoveNonFreshEntries(CacheIndexEntry *aEntry,
-                                               void* aClosure);
+  void RemoveNonFreshEntries();
 
   enum EState {
     // Initial state in which the index is not usable
     // Possible transitions:
     //  -> READING
     INITIAL  = 0,
 
     // Index is being read from the disk.
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -700,17 +700,17 @@ IMEHandler::ShowOnScreenKeyboard()
     wchar_t path[MAX_PATH];
     // The path to TabTip.exe is defined at the following registry key.
     // This is pulled out of the 64-bit registry hive directly.
     const wchar_t kRegKeyName[] =
       L"Software\\Classes\\CLSID\\"
       L"{054AAE20-4BEA-4347-8A35-64A533254A9D}\\LocalServer32";
     if (!WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE,
                                   kRegKeyName,
-                                  0,
+                                  nullptr,
                                   path,
                                   sizeof path)) {
       return;
     }
 
     std::wstring wstrpath(path);
     // The path provided by the registry will often contain
     // %CommonProgramFiles%, which will need to be replaced if it is present.
@@ -740,36 +740,37 @@ IMEHandler::ShowOnScreenKeyboard()
       } else {
         PWSTR path = nullptr;
         HRESULT hres =
           WinUtils::SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0,
                                          nullptr, &path);
         if (FAILED(hres) || !path) {
           return;
         }
-        commonProgramFilesPath = nsDependentString(path).get();
+        commonProgramFilesPath =
+          static_cast<const wchar_t*>(nsDependentString(path).get());
         ::CoTaskMemFree(path);
       }
       wstrpath.replace(commonProgramFilesOffset,
                        wcslen(L"%CommonProgramFiles%"),
                        commonProgramFilesPath);
     }
 
     cachedPath.Assign(wstrpath.data());
     Preferences::SetString(kOskPathPrefName, cachedPath);
   }
 
-  LPCWSTR cachedPathPtr;
+  const char16_t *cachedPathPtr;
   cachedPath.GetData(&cachedPathPtr);
-  HINSTANCE ret = ::ShellExecuteW(nullptr,
-                                  L"",
-                                  cachedPathPtr,
-                                  nullptr,
-                                  nullptr,
-                                  SW_SHOW);
+  ShellExecuteW(nullptr,
+                L"",
+                char16ptr_t(cachedPathPtr),
+                nullptr,
+                nullptr,
+                SW_SHOW);
   sShowingOnScreenKeyboard = true;
 }
 
 // Based on DismissVirtualKeyboard() in Chromium's base/win/win_util.cc.
 // static
 void
 IMEHandler::DismissOnScreenKeyboard()
 {