Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 28 Apr 2015 15:43:20 -0400
changeset 273000 ff7b5b03fece6c5d2e3fee12cfe6311ad45be934
parent 272999 e93eb477c5aa05405f43bac72566177440da2594 (current diff)
parent 272818 1ad65cbeb2f4691ad3f08ae2e69685bdd1caec90 (diff)
child 273001 2696c3d2ee5118bc1be664eb9528485584b58d8a
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.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
dom/media/omx/AudioSink.h
js/src/jit-test/tests/basic/semicolon-less-return.js
--- 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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="bdd03caf084e1b9279155bd232bc718a38c47cbc"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8c2d32bccc7061e9ca0165135457c3fd53e7107e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="4efd19d199ae52656604f794c5a77518400220fd">
     <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="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="bdd03caf084e1b9279155bd232bc718a38c47cbc"/>
   <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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="bdd03caf084e1b9279155bd232bc718a38c47cbc"/>
--- 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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="bdd03caf084e1b9279155bd232bc718a38c47cbc"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8c2d32bccc7061e9ca0165135457c3fd53e7107e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="bdd03caf084e1b9279155bd232bc718a38c47cbc"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="bdd03caf084e1b9279155bd232bc718a38c47cbc"/>
   <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="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "e5c1905b0144a855537fd857d62ec7a3393bb334", 
+        "git_revision": "6e35b0948c42a4398b8a5916015de167121683a1", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "97f0671809608b83775857fce9a7f7eba056d5d3", 
+    "revision": "14e42674734ab45c745a6b431cac067fab6f7a77", 
     "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="4efd19d199ae52656604f794c5a77518400220fd">
     <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="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="bdd03caf084e1b9279155bd232bc718a38c47cbc"/>
   <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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="e5c1905b0144a855537fd857d62ec7a3393bb334"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="6e35b0948c42a4398b8a5916015de167121683a1"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <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="bdd03caf084e1b9279155bd232bc718a38c47cbc"/>
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -729,17 +729,16 @@ BrowserGlue.prototype = {
 
 #ifndef RELEASE_BUILD
     let themeName = gBrowserBundle.GetStringFromName("deveditionTheme.name");
     let vendorShortName = gBrandBundle.GetStringFromName("vendorShortName");
 
     LightweightThemeManager.addBuiltInTheme({
       id: "firefox-devedition@mozilla.org",
       name: themeName,
-      accentcolor: "transparent",
       headerURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.header.png",
       iconURL: "resource:///chrome/browser/content/browser/defaultthemes/devedition.icon.png",
       author: vendorShortName,
     });
 #endif
 
 #ifdef MOZ_CRASHREPORTER
     TabCrashReporter.init();
--- a/browser/devtools/webconsole/webconsole.js
+++ b/browser/devtools/webconsole/webconsole.js
@@ -1410,16 +1410,18 @@ WebConsoleFrame.prototype = {
    */
   reportPageError: function WCF_reportPageError(aCategory, aScriptError)
   {
     // Warnings and legacy strict errors become warnings; other types become
     // errors.
     let severity = 'error';
     if (aScriptError.warning || aScriptError.strict) {
       severity = 'warning';
+    } else if (aScriptError.info) {
+      severity = 'log';
     }
 
     let category = 'js';
     switch(aCategory) {
       case CATEGORY_CSS:
         category = 'css';
         break;
       case CATEGORY_SECURITY:
--- a/configure.in
+++ b/configure.in
@@ -8949,21 +8949,18 @@ HAVE_SYS_MOUNT_H
 dnl ========================================================
 dnl ICU Support
 dnl ========================================================
 
 # Internationalization isn't built or exposed by default in non-desktop
 # builds.  Bugs to enable:
 #
 #   Android:  bug 864843
-#   B2G:      bug 866301
-
-if test "$MOZ_WIDGET_TOOLKIT" = "android" ||
-   test "$MOZ_BUILD_APP" = "b2g" ||
-   test "$MOZ_BUILD_APP" = "b2g/dev"; then
+
+if test "$MOZ_WIDGET_TOOLKIT" = "android"; then
     _INTL_API=no
 else
     _INTL_API=yes
 fi
 
 if test "$MOZ_WIDGET_TOOLKIT" = "cocoa"; then
     USE_ICU=1
 fi
--- a/dom/bluetooth/BluetoothCommon.h
+++ b/dom/bluetooth/BluetoothCommon.h
@@ -492,17 +492,31 @@ enum BluetoothHandsfreeVolumeType {
 
 enum BluetoothHandsfreeWbsConfig {
   HFP_WBS_NONE, /* Neither CVSD nor mSBC codec, but other optional codec.*/
   HFP_WBS_NO,   /* CVSD */
   HFP_WBS_YES   /* mSBC */
 };
 
 class BluetoothSignal;
-typedef mozilla::Observer<BluetoothSignal> BluetoothSignalObserver;
+
+class BluetoothSignalObserver : public mozilla::Observer<BluetoothSignal>
+{
+public:
+  BluetoothSignalObserver() : mSignalRegistered(false)
+  { }
+
+  void SetSignalRegistered(bool aSignalRegistered)
+  {
+    mSignalRegistered = aSignalRegistered;
+  }
+
+protected:
+  bool mSignalRegistered;
+};
 
 // Enums for object types, currently used for shared function lookups
 // (get/setproperty, etc...). Possibly discernable via dbus paths, but this
 // method is future-proofed for platform independence.
 enum BluetoothObjectType {
   TYPE_MANAGER = 0,
   TYPE_ADAPTER = 1,
   TYPE_DEVICE = 2,
--- a/dom/bluetooth/BluetoothUtils.cpp
+++ b/dom/bluetooth/BluetoothUtils.cpp
@@ -91,16 +91,44 @@ GeneratePathFromGattId(const BluetoothGa
 void
 GeneratePathFromGattId(const BluetoothGattId& aId,
                        nsAString& aPath)
 {
   nsString uuidStr;
   GeneratePathFromGattId(aId, aPath, uuidStr);
 }
 
+void
+RegisterBluetoothSignalHandler(const nsAString& aPath,
+                               BluetoothSignalObserver* aHandler)
+{
+  MOZ_ASSERT(!aPath.IsEmpty());
+  MOZ_ASSERT(aHandler);
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  bs->RegisterBluetoothSignalHandler(aPath, aHandler);
+  aHandler->SetSignalRegistered(true);
+}
+
+void
+UnregisterBluetoothSignalHandler(const nsAString& aPath,
+                                 BluetoothSignalObserver* aHandler)
+{
+  MOZ_ASSERT(!aPath.IsEmpty());
+  MOZ_ASSERT(aHandler);
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  bs->UnregisterBluetoothSignalHandler(aPath, aHandler);
+  aHandler->SetSignalRegistered(false);
+}
+
 /**
  * |SetJsObject| is an internal function used by |BroadcastSystemMessage| only
  */
 static bool
 SetJsObject(JSContext* aContext,
             const BluetoothValue& aValue,
             JS::Handle<JSObject*> aObj)
 {
--- a/dom/bluetooth/BluetoothUtils.h
+++ b/dom/bluetooth/BluetoothUtils.h
@@ -73,16 +73,42 @@ GeneratePathFromGattId(const BluetoothGa
  * @param aId   [in] GattId value to convert.
  * @param aPath [out] Bluetooth signal path generated from aId.
  */
 void
 GeneratePathFromGattId(const BluetoothGattId& aId,
                        nsAString& aPath);
 
 //
+// Register/Unregister bluetooth signal handlers
+//
+
+/**
+ * Register the bluetooth signal handler.
+ *
+ * @param aPath Path of the signal to be registered.
+ * @param aHandler The message handler object to be added into the observer
+ *                 list. Note that this function doesn't take references to it.
+ */
+void
+RegisterBluetoothSignalHandler(const nsAString& aPath,
+                               BluetoothSignalObserver* aHandler);
+
+/**
+ * Unregister the bluetooth signal handler.
+ *
+ * @param aPath Path of the signal to be unregistered.
+ * @param aHandler The message handler object to be removed from the observer
+ *                 list. Note that this function doesn't take references to it.
+ */
+void
+UnregisterBluetoothSignalHandler(const nsAString& aPath,
+                                 BluetoothSignalObserver* aHandler);
+
+//
 // Broadcast system message
 //
 
 bool
 BroadcastSystemMessage(const nsAString& aType,
                        const BluetoothValue& aData);
 
 bool
--- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -38,17 +38,17 @@ EnsureBluetoothSocketHalLoad()
 
   sBluetoothSocketInterface = btInf->GetBluetoothSocketInterface();
   NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
 
   return true;
 }
 
 class mozilla::dom::bluetooth::DroidSocketImpl : public ipc::UnixFdWatcher
-                                               , protected SocketIOBase
+                                               , protected DataSocketIO
 {
 public:
   /* The connection status in DroidSocketImpl indicates the current
    * phase of the socket connection. The initial settign should always
    * be DISCONNECTED, when no connection is present.
    *
    * To establish a connection on the server, DroidSocketImpl moves
    * to LISTENING. It now waits for incoming connection attempts by
@@ -70,17 +70,16 @@ public:
     SOCKET_IS_DISCONNECTED = 0,
     SOCKET_IS_LISTENING,
     SOCKET_IS_CONNECTING,
     SOCKET_IS_CONNECTED
   };
 
   DroidSocketImpl(MessageLoop* aIOLoop, BluetoothSocket* aConsumer)
     : ipc::UnixFdWatcher(aIOLoop)
-    , SocketIOBase(MAX_READ_SIZE)
     , mConsumer(aConsumer)
     , mShuttingDownOnIOThread(false)
     , mConnectionStatus(SOCKET_IS_DISCONNECTED)
   { }
 
   ~DroidSocketImpl()
   {
     MOZ_ASSERT(NS_IsMainThread());
@@ -130,34 +129,48 @@ public:
 
     mConnectionStatus = SOCKET_IS_CONNECTED;
 
     // Restart read & write watch on client fd
     AddWatchers(READ_WATCHER, true);
     AddWatchers(WRITE_WATCHER, false);
   }
 
-  SocketConsumerBase* GetConsumer()
+  BluetoothSocket* GetBluetoothSocket()
   {
     return mConsumer.get();
   }
 
+  DataSocket* GetDataSocket()
+  {
+    return GetBluetoothSocket();
+  }
+
   SocketBase* GetSocketBase()
   {
-    return GetConsumer();
+    return GetDataSocket();
   }
 
+  // Methods for |DataSocket|
+  //
+
+  nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer);
+  void ConsumeBuffer();
+  void DiscardBuffer();
+
   /**
    * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
    * directly from main thread. All non-main-thread accesses should happen with
    * mImpl as container.
    */
   RefPtr<BluetoothSocket> mConsumer;
 
 private:
+  class ReceiveRunnable;
+
   /**
    * libevent triggered functions that reads data from socket when available and
    * guarenteed non-blocking. Only to be called on IO thread.
    *
    * @param aFd [in] File descriptor to read from
    */
   virtual void OnFileCanReadWithoutBlocking(int aFd);
 
@@ -175,16 +188,21 @@ private:
   void OnSocketCanConnectWithoutBlocking(int aFd);
 
   /**
    * If true, do not requeue whatever task we're running
    */
   bool mShuttingDownOnIOThread;
 
   ConnectionStatus mConnectionStatus;
+
+  /**
+   * I/O buffer for received data
+   */
+  nsAutoPtr<UnixSocketRawData> mBuffer;
 };
 
 class SocketConnectTask final : public SocketIOTask<DroidSocketImpl>
 {
 public:
   SocketConnectTask(DroidSocketImpl* aDroidSocketImpl, int aFd)
   : SocketIOTask<DroidSocketImpl>(aDroidSocketImpl)
   , mFd(aFd)
@@ -487,16 +505,79 @@ DroidSocketImpl::OnSocketCanConnectWitho
   NS_DispatchToMainThread(r);
 
   AddWatchers(READ_WATCHER, true);
   if (HasPendingData()) {
     AddWatchers(WRITE_WATCHER, false);
   }
 }
 
+nsresult
+DroidSocketImpl::QueryReceiveBuffer(
+  UnixSocketIOBuffer** aBuffer)
+{
+  MOZ_ASSERT(aBuffer);
+
+  if (!mBuffer) {
+    mBuffer = new UnixSocketRawData(MAX_READ_SIZE);
+  }
+  *aBuffer = mBuffer.get();
+
+  return NS_OK;
+}
+
+/**
+ * |ReceiveRunnable| transfers data received on the I/O thread
+ * to an instance of |BluetoothSocket| on the main thread.
+ */
+class DroidSocketImpl::ReceiveRunnable final
+  : public SocketIORunnable<DroidSocketImpl>
+{
+public:
+  ReceiveRunnable(DroidSocketImpl* aIO, UnixSocketBuffer* aBuffer)
+    : SocketIORunnable<DroidSocketImpl>(aIO)
+    , mBuffer(aBuffer)
+  { }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    DroidSocketImpl* io = SocketIORunnable<DroidSocketImpl>::GetIO();
+
+    if (NS_WARN_IF(io->IsShutdownOnMainThread())) {
+      // Since we've already explicitly closed and the close
+      // happened before this, this isn't really an error.
+      return NS_OK;
+    }
+
+    BluetoothSocket* bluetoothSocket = io->GetBluetoothSocket();
+    MOZ_ASSERT(bluetoothSocket);
+
+    bluetoothSocket->ReceiveSocketData(mBuffer);
+
+    return NS_OK;
+  }
+
+private:
+  nsAutoPtr<UnixSocketBuffer> mBuffer;
+};
+
+void
+DroidSocketImpl::ConsumeBuffer()
+{
+  NS_DispatchToMainThread(new ReceiveRunnable(this, mBuffer.forget()));
+}
+
+void
+DroidSocketImpl::DiscardBuffer()
+{
+  // Nothing to do.
+}
+
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
   , mCurrentRes(nullptr)
   , mImpl(nullptr)
   , mAuth(aAuth)
--- a/dom/bluetooth/bluedroid/BluetoothSocket.h
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.h
@@ -3,25 +3,25 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_BluetoothSocket_h
 #define mozilla_dom_bluetooth_BluetoothSocket_h
 
 #include "BluetoothCommon.h"
-#include "mozilla/ipc/SocketBase.h"
+#include "mozilla/ipc/DataSocket.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocketObserver;
 class BluetoothSocketResultHandler;
 class DroidSocketImpl;
 
-class BluetoothSocket : public mozilla::ipc::SocketConsumerBase
+class BluetoothSocket : public mozilla::ipc::DataSocket
 {
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
                   bool aEncrypt);
 
   bool ConnectSocket(const nsAString& aDeviceAddress,
@@ -29,23 +29,29 @@ public:
                      int aChannel);
 
   bool ListenSocket(const nsAString& aServiceName,
                     const BluetoothUuid& aServiceUuid,
                     int aChannel);
 
   void CloseSocket() override;
 
+  /**
+   * Method to be called whenever data is received. This is only called on the
+   * main thread.
+   *
+   * @param aBuffer Data received from the socket.
+   */
+  void ReceiveSocketData(nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer);
+
   void SendSocketData(mozilla::ipc::UnixSocketIOBuffer* aBuffer) override;
 
   virtual void OnConnectSuccess() override;
   virtual void OnConnectError() override;
   virtual void OnDisconnect() override;
-  virtual void ReceiveSocketData(
-    nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer) override;
 
   inline void GetAddress(nsAString& aDeviceAddress)
   {
     aDeviceAddress = mDeviceAddress;
   }
 
   inline void SetAddress(const nsAString& aDeviceAddress)
   {
--- a/dom/bluetooth/bluetooth1/ipc/BluetoothParent.h
+++ b/dom/bluetooth/bluetooth1/ipc/BluetoothParent.h
@@ -32,18 +32,18 @@ class ContentParent;
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothService;
 
 /*******************************************************************************
  * BluetoothParent
  ******************************************************************************/
 
-class BluetoothParent : public PBluetoothParent,
-                        public mozilla::Observer<BluetoothSignal>
+class BluetoothParent : public PBluetoothParent
+                      , public BluetoothSignalObserver
 {
   friend class mozilla::dom::ContentParent;
 
   enum ShutdownState
   {
     Running = 0,
     SentBeginShutdown,
     ReceivedStopNotifying,
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
+#include "BluetoothUtils.h"
 #include "DOMRequest.h"
 #include "nsIDocument.h"
 #include "nsIPrincipal.h"
 #include "nsTArrayHelpers.h"
 
 #include "mozilla/dom/BluetoothAdapter2Binding.h"
 #include "mozilla/dom/BluetoothAttributeEvent.h"
 #include "mozilla/dom/BluetoothStatusChangedEvent.h"
@@ -24,19 +25,40 @@
 #include "mozilla/dom/bluetooth/BluetoothPairingListener.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothAdapter, DOMEventTargetHelper,
-                                   mDevices, mDiscoveryHandleInUse,
-                                   mPairingReqs)
+NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothAdapter)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothAdapter,
+                                                DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDevices)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDiscoveryHandleInUse)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPairingReqs)
+
+  /**
+   * Unregister the bluetooth signal handler after unlinked.
+   *
+   * This is needed to avoid ending up with exposing a deleted object to JS or
+   * accessing deleted objects while receiving signals from parent process
+   * after unlinked. Please see Bug 1138267 for detail informations.
+   */
+  UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), tmp);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothAdapter,
+                                                  DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDevices)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDiscoveryHandleInUse)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPairingReqs)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 // QueryInterface implementation for BluetoothAdapter
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothAdapter)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothAdapter, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothAdapter, DOMEventTargetHelper)
 
@@ -205,37 +227,30 @@ BluetoothAdapter::BluetoothAdapter(nsPID
   }
 
   const InfallibleTArray<BluetoothNamedValue>& values =
     aValue.get_ArrayOfBluetoothNamedValue();
   for (uint32_t i = 0; i < values.Length(); ++i) {
     SetPropertyByValue(values[i]);
   }
 
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
+  RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
 }
 
 BluetoothAdapter::~BluetoothAdapter()
 {
-  BluetoothService* bs = BluetoothService::Get();
-  // We can be null on shutdown, where this might happen
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
+  UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
 }
 
 void
 BluetoothAdapter::DisconnectFromOwner()
 {
   DOMEventTargetHelper::DisconnectFromOwner();
 
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
+  UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
 }
 
 void
 BluetoothAdapter::GetPairedDeviceProperties(
   const nsTArray<nsString>& aDeviceAddresses)
 {
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
@@ -316,19 +331,18 @@ BluetoothAdapter::Create(nsPIDOMWindow* 
 
   nsRefPtr<BluetoothAdapter> adapter = new BluetoothAdapter(aWindow, aValue);
   return adapter.forget();
 }
 
 void
 BluetoothAdapter::Notify(const BluetoothSignal& aData)
 {
-  InfallibleTArray<BluetoothNamedValue> arr;
-
   BT_LOGD("[A] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+  NS_ENSURE_TRUE_VOID(mSignalRegistered);
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("PropertyChanged")) {
     HandlePropertyChanged(v);
   } else if (aData.name().EqualsLiteral("DeviceFound")) {
     /*
      * DeviceFound signal will be distributed to all existing adapters while
      * doing discovery operations.
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.h
@@ -179,17 +179,17 @@ public:
    */
   void SetDiscoveryHandleInUse(BluetoothDiscoveryHandle* aDiscoveryHandle);
 
 private:
   BluetoothAdapter(nsPIDOMWindow* aOwner, const BluetoothValue& aValue);
   ~BluetoothAdapter();
 
   /**
-   * Set adapter properties according to properties array
+   * Set adapter properties according to properties array.
    *
    * @param aValue [in] Properties array to set with
    */
   void SetPropertyByValue(const BluetoothNamedValue& aValue);
 
   /**
    * Set adapter state and fire BluetoothAttributeEvent if state changed.
    *
--- a/dom/bluetooth/bluetooth2/BluetoothDevice.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothDevice.cpp
@@ -16,20 +16,39 @@
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/Promise.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothDevice,
-                                   DOMEventTargetHelper,
-                                   mCod,
-                                   mGatt)
+NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothDevice)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothDevice,
+                                                DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCod)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mGatt)
+
+  /**
+   * Unregister the bluetooth signal handler after unlinked.
+   *
+   * This is needed to avoid ending up with exposing a deleted object to JS or
+   * accessing deleted objects while receiving signals from parent process
+   * after unlinked. Please see Bug 1138267 for detail informations.
+   */
+  UnregisterBluetoothSignalHandler(tmp->mAddress, tmp);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothDevice,
+                                                  DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCod)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGatt)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothDevice, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothDevice, DOMEventTargetHelper)
 
 class FetchUuidsTask final : public BluetoothReplyRunnable
 {
@@ -86,37 +105,29 @@ BluetoothDevice::BluetoothDevice(nsPIDOM
   mCod = BluetoothClassOfDevice::Create(aWindow);
 
   const InfallibleTArray<BluetoothNamedValue>& values =
     aValue.get_ArrayOfBluetoothNamedValue();
   for (uint32_t i = 0; i < values.Length(); ++i) {
     SetPropertyByValue(values[i]);
   }
 
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->RegisterBluetoothSignalHandler(mAddress, this);
+  RegisterBluetoothSignalHandler(mAddress, this);
 }
 
 BluetoothDevice::~BluetoothDevice()
 {
-  BluetoothService* bs = BluetoothService::Get();
-  // bs can be null on shutdown, where destruction might happen.
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->UnregisterBluetoothSignalHandler(mAddress, this);
+  UnregisterBluetoothSignalHandler(mAddress, this);
 }
 
 void
 BluetoothDevice::DisconnectFromOwner()
 {
   DOMEventTargetHelper::DisconnectFromOwner();
-
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->UnregisterBluetoothSignalHandler(mAddress, this);
+  UnregisterBluetoothSignalHandler(mAddress, this);
 }
 
 BluetoothDeviceType
 BluetoothDevice::ConvertUint32ToDeviceType(const uint32_t aValue)
 {
   static const BluetoothDeviceType sDeviceType[] = {
     CONVERT(TYPE_OF_DEVICE_BREDR, BluetoothDeviceType::Classic),
     CONVERT(TYPE_OF_DEVICE_BLE, BluetoothDeviceType::Le),
@@ -194,16 +205,17 @@ BluetoothDevice::Create(nsPIDOMWindow* a
   nsRefPtr<BluetoothDevice> device = new BluetoothDevice(aWindow, aValue);
   return device.forget();
 }
 
 void
 BluetoothDevice::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+  NS_ENSURE_TRUE_VOID(mSignalRegistered);
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("PropertyChanged")) {
     HandlePropertyChanged(v);
   } else {
     BT_WARNING("Not handling device signal: %s",
                NS_ConvertUTF16toUTF8(aData.name()).get());
   }
--- a/dom/bluetooth/bluetooth2/BluetoothGatt.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothGatt.cpp
@@ -16,19 +16,36 @@
 #include "nsIUUIDGenerator.h"
 #include "nsServiceManagerUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothGatt,
-                                   DOMEventTargetHelper,
-                                   mServices)
+NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothGatt)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothGatt,
+                                                DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mServices)
+
+  /**
+   * Unregister the bluetooth signal handler after unlinked.
+   *
+   * This is needed to avoid ending up with exposing a deleted object to JS or
+   * accessing deleted objects while receiving signals from parent process
+   * after unlinked. Please see Bug 1138267 for detail informations.
+   */
+  UnregisterBluetoothSignalHandler(tmp->mAppUuid, tmp);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothGatt,
+                                                  DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServices)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothGatt)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothGatt, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothGatt, DOMEventTargetHelper)
 
 BluetoothGatt::BluetoothGatt(nsPIDOMWindow* aWindow,
@@ -51,17 +68,17 @@ BluetoothGatt::~BluetoothGatt()
   NS_ENSURE_TRUE_VOID(bs);
 
   if (mClientIf > 0) {
     nsRefPtr<BluetoothVoidReplyRunnable> result =
       new BluetoothVoidReplyRunnable(nullptr);
     bs->UnregisterGattClientInternal(mClientIf, result);
   }
 
-  bs->UnregisterBluetoothSignalHandler(mAppUuid, this);
+  UnregisterBluetoothSignalHandler(mAppUuid, this);
 }
 
 void
 BluetoothGatt::GenerateUuid(nsAString &aUuidString)
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
@@ -89,17 +106,17 @@ BluetoothGatt::DisconnectFromOwner()
   NS_ENSURE_TRUE_VOID(bs);
 
   if (mClientIf > 0) {
     nsRefPtr<BluetoothVoidReplyRunnable> result =
       new BluetoothVoidReplyRunnable(nullptr);
     bs->UnregisterGattClientInternal(mClientIf, result);
   }
 
-  bs->UnregisterBluetoothSignalHandler(mAppUuid, this);
+  UnregisterBluetoothSignalHandler(mAppUuid, this);
 }
 
 already_AddRefed<Promise>
 BluetoothGatt::Connect(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     aRv.Throw(NS_ERROR_FAILURE);
@@ -117,17 +134,17 @@ BluetoothGatt::Connect(ErrorResult& aRv)
   BluetoothService* bs = BluetoothService::Get();
   BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
 
   if (mAppUuid.IsEmpty()) {
     GenerateUuid(mAppUuid);
     BT_ENSURE_TRUE_REJECT(!mAppUuid.IsEmpty(),
                           promise,
                           NS_ERROR_DOM_OPERATION_ERR);
-    bs->RegisterBluetoothSignalHandler(mAppUuid, this);
+    RegisterBluetoothSignalHandler(mAppUuid, this);
   }
 
   UpdateConnectionState(BluetoothConnectionState::Connecting);
   nsRefPtr<BluetoothReplyRunnable> result =
     new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
                                    promise,
                                    NS_LITERAL_STRING("ConnectGattClient"));
   bs->ConnectGattClientInternal(mAppUuid,
@@ -318,16 +335,17 @@ BluetoothGatt::HandleCharacteristicChang
 
   DispatchTrustedEvent(event);
 }
 
 void
 BluetoothGatt::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+  NS_ENSURE_TRUE_VOID(mSignalRegistered);
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("ClientRegistered")) {
     MOZ_ASSERT(v.type() == BluetoothValue::Tuint32_t);
     mClientIf = v.get_uint32_t();
   } else if (aData.name().EqualsLiteral("ClientUnregistered")) {
     mClientIf = 0;
   } else if (aData.name().EqualsLiteral(GATT_CONNECTION_STATE_CHANGED_ID)) {
--- a/dom/bluetooth/bluetooth2/BluetoothGattCharacteristic.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothGattCharacteristic.cpp
@@ -15,18 +15,44 @@
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/Promise.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
-  BluetoothGattCharacteristic, mOwner, mService, mDescriptors)
+NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothGattCharacteristic)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BluetoothGattCharacteristic)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mService)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDescriptors)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+
+  /**
+   * Unregister the bluetooth signal handler after unlinked.
+   *
+   * This is needed to avoid ending up with exposing a deleted object to JS or
+   * accessing deleted objects while receiving signals from parent process
+   * after unlinked. Please see Bug 1138267 for detail informations.
+   */
+  nsString path;
+  GeneratePathFromGattId(tmp->mCharId, path);
+  UnregisterBluetoothSignalHandler(path, tmp);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BluetoothGattCharacteristic)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mService)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDescriptors)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BluetoothGattCharacteristic)
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattCharacteristic)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattCharacteristic)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattCharacteristic)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 BluetoothGattCharacteristic::BluetoothGattCharacteristic(
@@ -37,34 +63,28 @@ BluetoothGattCharacteristic::BluetoothGa
   , mService(aService)
   , mCharId(aChar.mId)
   , mProperties(aChar.mProperties)
   , mWriteType(aChar.mWriteType)
 {
   MOZ_ASSERT(aOwner);
   MOZ_ASSERT(mService);
 
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
   // Generate bluetooth signal path and a string representation to provide uuid
   // of this characteristic to applications
   nsString path;
   GeneratePathFromGattId(mCharId, path, mUuidStr);
-  bs->RegisterBluetoothSignalHandler(path, this);
+  RegisterBluetoothSignalHandler(path, this);
 }
 
 BluetoothGattCharacteristic::~BluetoothGattCharacteristic()
 {
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
   nsString path;
   GeneratePathFromGattId(mCharId, path);
-  bs->UnregisterBluetoothSignalHandler(path, this);
+  UnregisterBluetoothSignalHandler(path, this);
 }
 
 already_AddRefed<Promise>
 BluetoothGattCharacteristic::StartNotifications(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     aRv.Throw(NS_ERROR_FAILURE);
@@ -145,16 +165,17 @@ BluetoothGattCharacteristic::HandleChara
 
   mValue = aValue.get_ArrayOfuint8_t();
 }
 
 void
 BluetoothGattCharacteristic::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+  NS_ENSURE_TRUE_VOID(mSignalRegistered);
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("DescriptorsDiscovered")) {
     HandleDescriptorsDiscovered(v);
   } else if (aData.name().EqualsLiteral("CharacteristicValueUpdated")) {
     HandleCharacteristicValueUpdated(v);
   } else {
     BT_WARNING("Not handling GATT Characteristic signal: %s",
--- a/dom/bluetooth/bluetooth2/BluetoothGattDescriptor.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothGattDescriptor.cpp
@@ -14,18 +14,42 @@
 #include "mozilla/dom/bluetooth/BluetoothGattService.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
-  BluetoothGattDescriptor, mOwner, mCharacteristic)
+NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothGattDescriptor)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BluetoothGattDescriptor)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCharacteristic)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+
+  /**
+   * Unregister the bluetooth signal handler after unlinked.
+   *
+   * This is needed to avoid ending up with exposing a deleted object to JS or
+   * accessing deleted objects while receiving signals from parent process
+   * after unlinked. Please see Bug 1138267 for detail informations.
+   */
+  nsString path;
+  GeneratePathFromGattId(tmp->mDescriptorId, path);
+  UnregisterBluetoothSignalHandler(path, tmp);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BluetoothGattDescriptor)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCharacteristic)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BluetoothGattDescriptor)
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattDescriptor)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattDescriptor)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattDescriptor)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 BluetoothGattDescriptor::BluetoothGattDescriptor(
@@ -34,49 +58,44 @@ BluetoothGattDescriptor::BluetoothGattDe
   const BluetoothGattId& aDescriptorId)
   : mOwner(aOwner)
   , mCharacteristic(aCharacteristic)
   , mDescriptorId(aDescriptorId)
 {
   MOZ_ASSERT(aOwner);
   MOZ_ASSERT(aCharacteristic);
 
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
   // Generate bluetooth signal path and a string representation to provide uuid
   // of this descriptor to applications
   nsString path;
   GeneratePathFromGattId(mDescriptorId, path, mUuidStr);
-  bs->RegisterBluetoothSignalHandler(path, this);
+  RegisterBluetoothSignalHandler(path, this);
 }
 
 BluetoothGattDescriptor::~BluetoothGattDescriptor()
 {
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
   nsString path;
   GeneratePathFromGattId(mDescriptorId, path);
-  bs->UnregisterBluetoothSignalHandler(path, this);
+  UnregisterBluetoothSignalHandler(path, this);
 }
 
 void
 BluetoothGattDescriptor::HandleDescriptorValueUpdated(
   const BluetoothValue& aValue)
 {
   MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfuint8_t);
 
   mValue = aValue.get_ArrayOfuint8_t();
 }
 
 void
 BluetoothGattDescriptor::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+  NS_ENSURE_TRUE_VOID(mSignalRegistered);
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("DescriptorValueUpdated")) {
     HandleDescriptorValueUpdated(v);
   } else {
     BT_WARNING("Not handling GATT Descriptor signal: %s",
                NS_ConvertUTF16toUTF8(aData.name()).get());
   }
--- a/dom/bluetooth/bluetooth2/BluetoothGattService.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothGattService.cpp
@@ -12,18 +12,44 @@
 #include "mozilla/dom/bluetooth/BluetoothGattService.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
-  BluetoothGattService, mOwner, mIncludedServices, mCharacteristics)
+NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothGattService)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BluetoothGattService)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncludedServices)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCharacteristics)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+
+  /**
+   * Unregister the bluetooth signal handler after unlinked.
+   *
+   * This is needed to avoid ending up with exposing a deleted object to JS or
+   * accessing deleted objects while receiving signals from parent process
+   * after unlinked. Please see Bug 1138267 for detail informations.
+   */
+  nsString path;
+  GeneratePathFromGattId(tmp->mServiceId.mId, path);
+  UnregisterBluetoothSignalHandler(path, tmp);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BluetoothGattService)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncludedServices)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCharacteristics)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(BluetoothGattService)
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattService)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattService)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattService)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 BluetoothGattService::BluetoothGattService(
@@ -31,34 +57,28 @@ BluetoothGattService::BluetoothGattServi
   const BluetoothGattServiceId& aServiceId)
   : mOwner(aOwner)
   , mAppUuid(aAppUuid)
   , mServiceId(aServiceId)
 {
   MOZ_ASSERT(aOwner);
   MOZ_ASSERT(!mAppUuid.IsEmpty());
 
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
   // Generate bluetooth signal path and a string representation to provide
   // uuid of this service to applications
   nsString path;
   GeneratePathFromGattId(mServiceId.mId, path, mUuidStr);
-  bs->RegisterBluetoothSignalHandler(path, this);
+  RegisterBluetoothSignalHandler(path, this);
 }
 
 BluetoothGattService::~BluetoothGattService()
 {
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
   nsString path;
   GeneratePathFromGattId(mServiceId.mId, path);
-  bs->UnregisterBluetoothSignalHandler(path, this);
+  UnregisterBluetoothSignalHandler(path, this);
 }
 
 void
 BluetoothGattService::HandleIncludedServicesDiscovered(
   const BluetoothValue& aValue)
 {
   MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattServiceId);
 
@@ -90,16 +110,17 @@ BluetoothGattService::HandleCharacterist
 
   BluetoothGattServiceBinding::ClearCachedCharacteristicsValue(this);
 }
 
 void
 BluetoothGattService::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+  NS_ENSURE_TRUE_VOID(mSignalRegistered);
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("IncludedServicesDiscovered")) {
     HandleIncludedServicesDiscovered(v);
   } else if (aData.name().EqualsLiteral("CharacteristicsDiscovered")) {
     HandleCharacteristicsDiscovered(v);
   } else {
     BT_WARNING("Not handling GATT Service signal: %s",
--- a/dom/bluetooth/bluetooth2/BluetoothManager.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothManager.cpp
@@ -1,33 +1,54 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
-#include "BluetoothManager.h"
-#include "BluetoothAdapter.h"
+#include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
-#include "BluetoothReplyRunnable.h"
+#include "BluetoothUtils.h"
 
+#include "mozilla/dom/bluetooth/BluetoothAdapter.h"
+#include "mozilla/dom/bluetooth/BluetoothManager.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/BluetoothManager2Binding.h"
 #include "mozilla/Services.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsIPermissionManager.h"
 #include "nsThreadUtils.h"
 
 using namespace mozilla;
 
 USING_BLUETOOTH_NAMESPACE
 
-// QueryInterface implementation for BluetoothManager
+NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothManager)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothManager,
+                                                DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAdapters)
+
+  /**
+   * Unregister the bluetooth signal handler after unlinked.
+   *
+   * This is needed to avoid ending up with exposing a deleted object to JS or
+   * accessing deleted objects while receiving signals from parent process
+   * after unlinked. Please see Bug 1138267 for detail informations.
+   */
+  UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), tmp);
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothManager,
+                                                  DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAdapters)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothManager)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothManager, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothManager, DOMEventTargetHelper)
 
 class GetAdaptersTask : public BluetoothReplyRunnable
 {
@@ -87,50 +108,37 @@ private:
 };
 
 BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow)
   : DOMEventTargetHelper(aWindow)
   , mDefaultAdapterIndex(-1)
 {
   MOZ_ASSERT(aWindow);
 
-  ListenToBluetoothSignal(true);
+  RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this);
   BT_API2_LOGR("aWindow %p", aWindow);
 
   // Query adapters list from bluetooth backend
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   nsRefPtr<BluetoothReplyRunnable> result = new GetAdaptersTask(this);
   NS_ENSURE_SUCCESS_VOID(bs->GetAdaptersInternal(result));
 }
 
 BluetoothManager::~BluetoothManager()
 {
-  ListenToBluetoothSignal(false);
+  UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this);
 }
 
 void
 BluetoothManager::DisconnectFromOwner()
 {
   DOMEventTargetHelper::DisconnectFromOwner();
-  ListenToBluetoothSignal(false);
-}
-
-void
-BluetoothManager::ListenToBluetoothSignal(bool aStart)
-{
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-
-  if (aStart) {
-    bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this);
-  } else {
-    bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this);
-  }
+  UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_MANAGER), this);
 }
 
 BluetoothAdapter*
 BluetoothManager::GetDefaultAdapter()
 {
   BT_API2_LOGR("mDefaultAdapterIndex: %d", mDefaultAdapterIndex);
 
   return DefaultAdapterExists() ? mAdapters[mDefaultAdapterIndex] : nullptr;
@@ -260,16 +268,17 @@ BluetoothManager::DispatchAttributeEvent
 
   DispatchTrustedEvent(event);
 }
 
 void
 BluetoothManager::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[M] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+  NS_ENSURE_TRUE_VOID(mSignalRegistered);
 
   if (aData.name().EqualsLiteral("AdapterAdded")) {
     HandleAdapterAdded(aData.value());
   } else if (aData.name().EqualsLiteral("AdapterRemoved")) {
     HandleAdapterRemoved(aData.value());
   } else {
     BT_WARNING("Not handling manager signal: %s",
                NS_ConvertUTF16toUTF8(aData.name()).get());
--- a/dom/bluetooth/bluetooth2/BluetoothManager.h
+++ b/dom/bluetooth/bluetooth2/BluetoothManager.h
@@ -20,16 +20,18 @@ BEGIN_BLUETOOTH_NAMESPACE
 class BluetoothAdapter;
 class BluetoothValue;
 
 class BluetoothManager final : public DOMEventTargetHelper
                              , public BluetoothSignalObserver
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothManager,
+                                           DOMEventTargetHelper)
 
   /****************************************************************************
    * Event Handlers
    ***************************************************************************/
   IMPL_EVENT_HANDLER(attributechanged);
   IMPL_EVENT_HANDLER(adapteradded);
   IMPL_EVENT_HANDLER(adapterremoved);
 
@@ -75,23 +77,16 @@ public:
    */
   void AppendAdapter(const BluetoothValue& aValue);
 
 private:
   BluetoothManager(nsPIDOMWindow* aWindow);
   ~BluetoothManager();
 
   /**
-   * Start/Stop listening to bluetooth signal.
-   *
-   * @param aStart [in] Whether to start or stop listening to bluetooth signal
-   */
-  void ListenToBluetoothSignal(bool aStart);
-
-  /**
    * Check whether default adapter exists.
    */
   bool DefaultAdapterExists()
   {
     return (mDefaultAdapterIndex >= 0);
   }
 
   /**
--- a/dom/bluetooth/bluetooth2/BluetoothPairingListener.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothPairingListener.cpp
@@ -1,20 +1,21 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
 #include "mozilla/dom/bluetooth/BluetoothPairingListener.h"
 #include "mozilla/dom/bluetooth/BluetoothPairingHandle.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/BluetoothPairingEvent.h"
 #include "mozilla/dom/BluetoothPairingListenerBinding.h"
-#include "BluetoothService.h"
 
 USING_BLUETOOTH_NAMESPACE
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothPairingListener)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothPairingListener, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothPairingListener, DOMEventTargetHelper)
@@ -37,21 +38,18 @@ BluetoothPairingListener::Create(nsPIDOM
   nsRefPtr<BluetoothPairingListener> handle =
     new BluetoothPairingListener(aWindow);
 
   return handle.forget();
 }
 
 BluetoothPairingListener::~BluetoothPairingListener()
 {
-  BluetoothService* bs = BluetoothService::Get();
-  // It can be nullptr on shutdown.
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
-                                       this);
+  UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
+                                   this);
 }
 
 void
 BluetoothPairingListener::DispatchPairingEvent(const nsAString& aName,
                                                const nsAString& aAddress,
                                                const nsAString& aPasskey,
                                                const nsAString& aType)
 {
@@ -113,21 +111,18 @@ BluetoothPairingListener::WrapObject(JSC
 {
   return BluetoothPairingListenerBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 BluetoothPairingListener::DisconnectFromOwner()
 {
   DOMEventTargetHelper::DisconnectFromOwner();
-
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
-                                       this);
+  UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
+                                   this);
 }
 
 void
 BluetoothPairingListener::EventListenerAdded(nsIAtom* aType)
 {
   DOMEventTargetHelper::EventListenerAdded(aType);
 
   TryListeningToBluetoothSignal();
@@ -148,15 +143,13 @@ BluetoothPairingListener::TryListeningTo
       !HasListenersFor(nsGkAtoms::onenterpincodereq) ||
       !HasListenersFor(nsGkAtoms::onpairingconfirmationreq) ||
       !HasListenersFor(nsGkAtoms::onpairingconsentreq)) {
     BT_LOGR("Pairing listener is not ready to handle pairing requests!");
     return;
   }
 
   // Start listening to bluetooth signal to handle pairing requests
-  BluetoothService* bs = BluetoothService::Get();
-  NS_ENSURE_TRUE_VOID(bs);
-  bs->RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
-                                     this);
+  RegisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_PAIRING_LISTENER),
+                                 this);
 
   mHasListenedToSignal = true;
 }
--- a/dom/bluetooth/bluetooth2/ipc/BluetoothParent.h
+++ b/dom/bluetooth/bluetooth2/ipc/BluetoothParent.h
@@ -32,18 +32,18 @@ class ContentParent;
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothService;
 
 /*******************************************************************************
  * BluetoothParent
  ******************************************************************************/
 
-class BluetoothParent : public PBluetoothParent,
-                        public mozilla::Observer<BluetoothSignal>
+class BluetoothParent : public PBluetoothParent
+                      , public BluetoothSignalObserver
 {
   friend class mozilla::dom::ContentParent;
 
   enum ShutdownState
   {
     Running = 0,
     SentBeginShutdown,
     ReceivedStopNotifying,
--- a/dom/bluetooth/bluez/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluez/BluetoothSocket.cpp
@@ -20,28 +20,30 @@ BEGIN_BLUETOOTH_NAMESPACE
 static const size_t MAX_READ_SIZE = 1 << 16;
 
 //
 // BluetoothSocketIO
 //
 
 class BluetoothSocket::BluetoothSocketIO final
   : public UnixSocketWatcher
-  , protected SocketIOBase
+  , protected DataSocketIO
 {
 public:
   BluetoothSocketIO(MessageLoop* mIOLoop,
                     BluetoothSocket* aConsumer,
                     UnixSocketConnector* aConnector,
                     const nsACString& aAddress);
   ~BluetoothSocketIO();
 
-  void                GetSocketAddr(nsAString& aAddrStr) const;
-  SocketConsumerBase* GetConsumer();
-  SocketBase*         GetSocketBase();
+  void        GetSocketAddr(nsAString& aAddrStr) const;
+
+  BluetoothSocket* GetBluetoothSocket();
+  DataSocket* GetDataSocket();
+  SocketBase* GetSocketBase();
 
   // Shutdown state
   //
 
   bool IsShutdownOnMainThread() const;
   void ShutdownOnMainThread();
 
   bool IsShutdownOnIOThread() const;
@@ -75,17 +77,26 @@ public:
   void OnAccepted(int aFd, const sockaddr_any* aAddr,
                   socklen_t aAddrLen) override;
   void OnConnected() override;
   void OnError(const char* aFunction, int aErrno) override;
   void OnListening() override;
   void OnSocketCanReceiveWithoutBlocking() override;
   void OnSocketCanSendWithoutBlocking() override;
 
+  // Methods for |DataSocket|
+  //
+
+  nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer);
+  void ConsumeBuffer();
+  void DiscardBuffer();
+
 private:
+  class ReceiveRunnable;
+
   void FireSocketError();
 
   // Set up flags on file descriptor.
   static bool SetSocketFlags(int aFd);
 
   /**
    * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
    * directly from main thread. All non-main-thread accesses should happen with
@@ -117,25 +128,29 @@ private:
    * Address struct of the socket currently in use
    */
   sockaddr_any mAddr;
 
   /**
    * Task member for delayed connect task. Should only be access on main thread.
    */
   CancelableTask* mDelayedConnectTask;
+
+  /**
+   * I/O buffer for received data
+   */
+  nsAutoPtr<UnixSocketRawData> mBuffer;
 };
 
 BluetoothSocket::BluetoothSocketIO::BluetoothSocketIO(
   MessageLoop* mIOLoop,
   BluetoothSocket* aConsumer,
   UnixSocketConnector* aConnector,
   const nsACString& aAddress)
   : UnixSocketWatcher(mIOLoop)
-  , SocketIOBase(MAX_READ_SIZE)
   , mConsumer(aConsumer)
   , mConnector(aConnector)
   , mShuttingDownOnIOThread(false)
   , mAddress(aAddress)
   , mDelayedConnectTask(nullptr)
 {
   MOZ_ASSERT(mConsumer);
   MOZ_ASSERT(mConnector);
@@ -153,26 +168,32 @@ BluetoothSocket::BluetoothSocketIO::GetS
   if (!mConnector) {
     NS_WARNING("No connector to get socket address from!");
     aAddrStr.Truncate();
     return;
   }
   mConnector->GetSocketAddr(mAddr, aAddrStr);
 }
 
-SocketConsumerBase*
-BluetoothSocket::BluetoothSocketIO::GetConsumer()
+BluetoothSocket*
+BluetoothSocket::BluetoothSocketIO::GetBluetoothSocket()
 {
   return mConsumer.get();
 }
 
+DataSocket*
+BluetoothSocket::BluetoothSocketIO::GetDataSocket()
+{
+  return GetBluetoothSocket();
+}
+
 SocketBase*
 BluetoothSocket::BluetoothSocketIO::GetSocketBase()
 {
-  return GetConsumer();
+  return GetDataSocket();
 }
 
 bool
 BluetoothSocket::BluetoothSocketIO::IsShutdownOnMainThread() const
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   return mConsumer == nullptr;
@@ -470,16 +491,79 @@ BluetoothSocket::BluetoothSocketIO::SetS
   flags |= O_NONBLOCK;
   if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags))) {
     return false;
   }
 
   return true;
 }
 
+nsresult
+BluetoothSocket::BluetoothSocketIO::QueryReceiveBuffer(
+  UnixSocketIOBuffer** aBuffer)
+{
+  MOZ_ASSERT(aBuffer);
+
+  if (!mBuffer) {
+    mBuffer = new UnixSocketRawData(MAX_READ_SIZE);
+  }
+  *aBuffer = mBuffer.get();
+
+  return NS_OK;
+}
+
+/**
+ * |ReceiveRunnable| transfers data received on the I/O thread
+ * to an instance of |BluetoothSocket| on the main thread.
+ */
+class BluetoothSocket::BluetoothSocketIO::ReceiveRunnable final
+  : public SocketIORunnable<BluetoothSocketIO>
+{
+public:
+  ReceiveRunnable(BluetoothSocketIO* aIO, UnixSocketBuffer* aBuffer)
+    : SocketIORunnable<BluetoothSocketIO>(aIO)
+    , mBuffer(aBuffer)
+  { }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    BluetoothSocketIO* io = SocketIORunnable<BluetoothSocketIO>::GetIO();
+
+    if (NS_WARN_IF(io->IsShutdownOnMainThread())) {
+      // Since we've already explicitly closed and the close
+      // happened before this, this isn't really an error.
+      return NS_OK;
+    }
+
+    BluetoothSocket* bluetoothSocket = io->GetBluetoothSocket();
+    MOZ_ASSERT(bluetoothSocket);
+
+    bluetoothSocket->ReceiveSocketData(mBuffer);
+
+    return NS_OK;
+  }
+
+private:
+  nsAutoPtr<UnixSocketBuffer> mBuffer;
+};
+
+void
+BluetoothSocket::BluetoothSocketIO::ConsumeBuffer()
+{
+  NS_DispatchToMainThread(new ReceiveRunnable(this, mBuffer.forget()));
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::DiscardBuffer()
+{
+  // Nothing to do.
+}
+
 //
 // Socket tasks
 //
 
 class BluetoothSocket::ListenTask final
   : public SocketIOTask<BluetoothSocketIO>
 {
 public:
--- a/dom/bluetooth/bluez/BluetoothSocket.h
+++ b/dom/bluetooth/bluez/BluetoothSocket.h
@@ -4,29 +4,29 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_BluetoothSocket_h
 #define mozilla_dom_bluetooth_BluetoothSocket_h
 
 #include "BluetoothCommon.h"
 #include <stdlib.h>
-#include "mozilla/ipc/SocketBase.h"
+#include "mozilla/ipc/DataSocket.h"
 #include "mozilla/ipc/UnixSocketWatcher.h"
 #include "mozilla/RefPtr.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocketObserver;
 class BluetoothUnixSocketConnector;
 
-class BluetoothSocket final : public mozilla::ipc::SocketConsumerBase
+class BluetoothSocket final : public mozilla::ipc::DataSocket
 {
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
                   bool aEncrypt);
   ~BluetoothSocket();
 
@@ -39,25 +39,31 @@ public:
   inline void Disconnect()
   {
     CloseSocket();
   }
 
   virtual void OnConnectSuccess() override;
   virtual void OnConnectError() override;
   virtual void OnDisconnect() override;
-  virtual void ReceiveSocketData(
-    nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer) override;
 
   inline void GetAddress(nsAString& aDeviceAddress)
   {
     GetSocketAddr(aDeviceAddress);
   }
 
   /**
+   * Method to be called whenever data is received. This is only called on the
+   * main thread.
+   *
+   * @param aBuffer Data received from the socket.
+   */
+  void ReceiveSocketData(nsAutoPtr<mozilla::ipc::UnixSocketBuffer>& aBuffer);
+
+  /**
    * Queue data to be sent to the socket on the IO thread. Can only be called on
    * originating thread.
    *
    * @param aBuffer Data to be sent to socket
    */
   void SendSocketData(mozilla::ipc::UnixSocketIOBuffer* aBuffer) override;
 
   /**
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -63,17 +63,18 @@ ProcessURL(nsAString& aUrl, bool* aSchem
   aRv = urlParser->ParseURL(url, flatURL.Length(), &schemePos, &schemeLen,
                             nullptr, nullptr,       // ignore authority
                             &pathPos, &pathLen);
   if (NS_WARN_IF(aRv.Failed())) { return; }
 
   if (aSchemeValidOut) {
     nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
     *aSchemeValidOut = scheme.LowerCaseEqualsLiteral("http") ||
-                       scheme.LowerCaseEqualsLiteral("https");
+                       scheme.LowerCaseEqualsLiteral("https") ||
+                       scheme.LowerCaseEqualsLiteral("app");
   }
 
   uint32_t queryPos;
   int32_t queryLen;
   uint32_t refPos;
   int32_t refLen;
 
   aRv = urlParser->ParsePath(url + pathPos, flatURL.Length() - pathPos,
--- a/dom/fmradio/FMRadio.cpp
+++ b/dom/fmradio/FMRadio.cpp
@@ -499,17 +499,17 @@ FMRadio::EnableAudioChannelAgent()
   SetCanPlay(playingState == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
 
   mAudioChannelAgentEnabled = true;
 }
 
 NS_IMETHODIMP
 FMRadio::CanPlayChanged(int32_t aCanPlay)
 {
-  SetCanPlay(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
+  SetCanPlay(!(aCanPlay == AudioChannelState::AUDIO_CHANNEL_STATE_MUTED));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FMRadio::WindowVolumeChanged()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/dom/icc/IccManager.cpp
+++ b/dom/icc/IccManager.cpp
@@ -8,19 +8,16 @@
 #include "IccListener.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/IccChangeEvent.h"
 #include "mozilla/Preferences.h"
 #include "nsIIccInfo.h"
 // Service instantiation
 #include "ipc/IccIPCService.h"
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-// TODO: Bug 815526, deprecate RILContentHelper.
-#include "nsIRadioInterfaceLayer.h"
-#include "nsRadioInterfaceLayer.h"
 #include "nsIGonkIccService.h"
 #endif
 #include "nsXULAppAPI.h" // For XRE_GetProcessType()
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IccManager)
 
@@ -143,20 +140,14 @@ already_AddRefed<nsIIccService>
 NS_CreateIccService()
 {
   nsCOMPtr<nsIIccService> service;
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     service = new mozilla::dom::icc::IccIPCService();
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
   } else {
-    // TODO: Bug 815526, deprecate RILContentHelper.
-    nsCOMPtr <nsIRadioInterfaceLayer> ril =
-      do_GetService(NS_RADIOINTERFACELAYER_CONTRACTID);
-    nsCOMPtr <nsIRadioInterfaceLayer_new> ril_new(do_QueryInterface(ril));
-
-    service = (ril_new) ? do_GetService(GONK_ICC_SERVICE_CONTRACTID)
-                        : do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
+    service = do_GetService(GONK_ICC_SERVICE_CONTRACTID);
 #endif
   }
 
   return service.forget();
 }
--- a/dom/icc/gonk/IccService.js
+++ b/dom/icc/gonk/IccService.js
@@ -20,16 +20,20 @@ const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID 
 
 const kPrefRilDebuggingEnabled = "ril.debugging.enabled";
 const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
 
 XPCOMUtils.defineLazyServiceGetter(this, "gRadioInterfaceLayer",
                                    "@mozilla.org/ril;1",
                                    "nsIRadioInterfaceLayer");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
+                                   "@mozilla.org/mobileconnection/mobileconnectionservice;1",
+                                   "nsIGonkMobileConnectionService");
+
 let DEBUG = RIL.DEBUG_RIL;
 function debug(s) {
   dump("IccService: " + s);
 }
 
 function IccInfo() {}
 IccInfo.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
@@ -68,17 +72,17 @@ CdmaIccInfo.prototype = {
   prlVersion: 0
 };
 
 function IccService() {
   this._iccs = [];
 
   let numClients = gRadioInterfaceLayer.numRadioInterfaces;
   for (let i = 0; i < numClients; i++) {
-    this._iccs.push(new Icc(gRadioInterfaceLayer.getRadioInterface(i)));
+    this._iccs.push(new Icc(i));
   }
 
   this._updateDebugFlag();
 
   Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
   Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 }
 IccService.prototype = {
@@ -140,17 +144,17 @@ IccService.prototype = {
 
   notifyImsiChanged: function(aServiceId, aImsi) {
     if (DEBUG) {
       debug("notifyImsiChanged for service Id: " + aServiceId +
             ", Imsi: " + aImsi);
     }
 
     let icc = this.getIccByServiceId(aServiceId);
-    icc._imsi = aImsi;
+    icc.imsi = aImsi || null;
   },
 
   /**
    * nsIObserver interface.
    */
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID:
@@ -161,25 +165,26 @@ IccService.prototype = {
       case NS_XPCOM_SHUTDOWN_OBSERVER_ID:
         Services.prefs.removeObserver(kPrefRilDebuggingEnabled, this);
         Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
         break;
     }
   }
 };
 
-function Icc(aRadioInterface) {
-  this._radioInterface = aRadioInterface;
+function Icc(aClientId) {
+  this._clientId = aClientId;
+  this._radioInterface = gRadioInterfaceLayer.getRadioInterface(aClientId);
   this._listeners = [];
 }
 Icc.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIIcc]),
 
+  _clientId: 0,
   _radioInterface: null,
-  _imsi: null,
   _listeners: null,
 
   _updateCardState: function(aCardState) {
     if (this.cardState != aCardState) {
       this.cardState = aCardState;
     }
 
     this._deliverListenerEvent("notifyCardStateChanged");
@@ -188,33 +193,61 @@ Icc.prototype = {
   // An utility function to copy objects.
   _updateInfo: function(aSrcInfo, aDestInfo) {
     for (let key in aSrcInfo) {
       aDestInfo[key] = aSrcInfo[key];
     }
   },
 
   /**
+   * A utility function to compare objects. The srcInfo may contain
+   * "rilMessageType", should ignore it.
+   */
+  _isInfoChanged: function(srcInfo, destInfo) {
+    if (!destInfo) {
+      return true;
+    }
+
+    for (let key in srcInfo) {
+      if (key === "rilMessageType") {
+        continue;
+      }
+      if (srcInfo[key] !== destInfo[key]) {
+        return true;
+      }
+    }
+
+    return false;
+  },
+
+  /**
    * We need to consider below cases when update iccInfo:
    * 1. Should clear iccInfo to null if there is no card detected.
    * 2. Need to create corresponding object based on iccType.
    */
   _updateIccInfo: function(aIccInfo) {
+    let oldSpn = this.iccInfo ? this.iccInfo.spn : null;
+
     // Card is not detected, clear iccInfo to null.
     if (!aIccInfo || !aIccInfo.iccid) {
       if (this.iccInfo) {
         if (DEBUG) {
           debug("Card is not detected, clear iccInfo to null.");
         }
+        this.imsi = null;
         this.iccInfo = null;
         this._deliverListenerEvent("notifyIccInfoChanged");
       }
       return;
     }
 
+    if (!this._isInfoChanged(aIccInfo, this.iccInfo)) {
+      return;
+    }
+
     // If iccInfo is null, new corresponding object based on iccType.
     if (!this.iccInfo ||
         this.iccInfo.iccType != aIccInfo.iccType) {
       if (aIccInfo.iccType === "ruim" || aIccInfo.iccType === "csim") {
         this.iccInfo = new CdmaIccInfo();
       } else if (aIccInfo.iccType === "sim" || aIccInfo.iccType === "usim") {
         this.iccInfo = new GsmIccInfo();
       } else {
@@ -228,16 +261,33 @@ Icc.prototype = {
 
     // Update lastKnownSimMcc.
     if (aIccInfo.mcc) {
       try {
         Services.prefs.setCharPref("ril.lastKnownSimMcc",
                                    aIccInfo.mcc.toString());
       } catch (e) {}
     }
+
+    // Update lastKnownHomeNetwork.
+    if (aIccInfo.mcc && aIccInfo.mnc) {
+      let lastKnownHomeNetwork = aIccInfo.mcc + "-" + aIccInfo.mnc;
+      // Append spn information if available.
+      if (aIccInfo.spn) {
+        lastKnownHomeNetwork += "-" + aIccInfo.spn;
+      }
+
+      gMobileConnectionService.notifyLastHomeNetworkChanged(this._clientId,
+                                                            lastKnownHomeNetwork);
+    }
+
+    // If spn becomes available, we should check roaming again.
+    if (!oldSpn && aIccInfo.spn) {
+      gMobileConnectionService.notifySpnAvailable(this._clientId);
+    }
   },
 
   _deliverListenerEvent: function(aName, aArgs) {
     let listeners = this._listeners.slice();
     for (let listener of listeners) {
       if (this._listeners.indexOf(listener) === -1) {
         continue;
       }
@@ -300,16 +350,17 @@ Icc.prototype = {
     return true;
   },
 
   /**
    * nsIIcc interface.
    */
   iccInfo: null,
   cardState: Ci.nsIIcc.CARD_STATE_UNKNOWN,
+  imsi: null,
 
   registerListener: function(aListener) {
     if (this._listeners.indexOf(aListener) >= 0) {
       throw Cr.NS_ERROR_UNEXPECTED;
     }
 
     this._listeners.push(aListener);
   },
@@ -374,17 +425,17 @@ Icc.prototype = {
   matchMvno: function(aMvnoType, aMvnoData, aCallback) {
     if (!aMvnoData) {
       aCallback.notifyError(RIL.GECKO_ERROR_INVALID_PARAMETER);
       return;
     }
 
     switch (aMvnoType) {
       case Ci.nsIIcc.CARD_MVNO_TYPE_IMSI:
-        let imsi = this._imsi;
+        let imsi = this.imsi;
         if (!imsi) {
           aCallback.notifyError(RIL.GECKO_ERROR_GENERIC_FAILURE);
           break;
         }
         aCallback.notifySuccessWithBoolean(
           this._isImsiMatches(aMvnoData, imsi));
         break;
       case Ci.nsIIcc.CARD_MVNO_TYPE_SPN:
--- a/dom/icc/interfaces/nsIIccService.idl
+++ b/dom/icc/interfaces/nsIIccService.idl
@@ -92,17 +92,17 @@ interface nsIIccService : nsISupports
 %{C++
 already_AddRefed<nsIIccService>
 NS_CreateIccService();
 %}
 
 /**
  * XPCOM component that provides the access to the selected ICC.
  */
-[scriptable, uuid(38a5bbe2-add6-11e4-ba9e-e390d1d19195)]
+[scriptable, uuid(20a99186-e4cb-11e4-a5f9-938abcf7c826)]
 interface nsIIcc : nsISupports
 {
   /**
    * Card State Constants
    *
    * Note: MUST be matched with enum IccCardState in MozIcc.webidl!
    */
   const unsigned long CARD_STATE_UNKNOWN = 0;
@@ -210,16 +210,21 @@ interface nsIIcc : nsISupports
   /**
    * Indicates the state of this ICC.
    *
    * One of the CARD_STATE_* values.
    */
   readonly attribute unsigned long cardState;
 
   /**
+   * IMSI of this ICC.
+   */
+  readonly attribute DOMString imsi;
+
+  /**
    * Get the status of an ICC lock (e.g. the PIN lock).
    *
    * @param aLockType
    *        One of the CARD_LOCK_TYPE_* values.
    * @param aCallback
    *        An instance of nsIIccCallback:
    *        nsIIccCallback::notifySuccessWithBoolean() if success.
    *        nsIIccCallback::notifyError(), otherwise.
--- a/dom/icc/ipc/IccChild.cpp
+++ b/dom/icc/ipc/IccChild.cpp
@@ -178,16 +178,23 @@ IccChild::GetIccInfo(nsIIccInfo** aIccIn
 NS_IMETHODIMP
 IccChild::GetCardState(uint32_t* aCardState)
 {
   *aCardState = mCardState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+IccChild::GetImsi(nsAString & aImsi)
+{
+  NS_WARNING("IMSI shall not directly be fetched in child process.");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 IccChild::GetCardLockEnabled(uint32_t aLockType,
                              nsIIccCallback* aRequestReply)
 {
   return SendRequest(GetCardLockEnabledRequest(aLockType), aRequestReply)
     ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -235,32 +235,31 @@ namespace mozilla {
 namespace dom {
 
 class MemoryReportRequestChild : public PMemoryReportRequestChild,
                                  public nsIRunnable
 {
 public:
     NS_DECL_ISUPPORTS
 
-    MemoryReportRequestChild(uint32_t aGeneration, bool aAnonymize,
+    MemoryReportRequestChild(bool aAnonymize,
                              const MaybeFileDesc& aDMDFile);
     NS_IMETHOD Run() override;
 private:
     virtual ~MemoryReportRequestChild();
 
-    uint32_t mGeneration;
     bool     mAnonymize;
     FileDescriptor mDMDFile;
 };
 
 NS_IMPL_ISUPPORTS(MemoryReportRequestChild, nsIRunnable)
 
 MemoryReportRequestChild::MemoryReportRequestChild(
-    uint32_t aGeneration, bool aAnonymize, const MaybeFileDesc& aDMDFile)
-  : mGeneration(aGeneration), mAnonymize(aAnonymize)
+    bool aAnonymize, const MaybeFileDesc& aDMDFile)
+  : mAnonymize(aAnonymize)
 {
     MOZ_COUNT_CTOR(MemoryReportRequestChild);
     if (aDMDFile.type() == MaybeFileDesc::TFileDescriptor) {
         mDMDFile = aDMDFile.get_FileDescriptor();
     }
 }
 
 MemoryReportRequestChild::~MemoryReportRequestChild()
@@ -862,58 +861,47 @@ ContentChild::DeallocPDocAccessibleChild
 
 PMemoryReportRequestChild*
 ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
                                              const bool &aAnonymize,
                                              const bool &aMinimizeMemoryUsage,
                                              const MaybeFileDesc& aDMDFile)
 {
     MemoryReportRequestChild *actor =
-        new MemoryReportRequestChild(aGeneration, aAnonymize, aDMDFile);
+        new MemoryReportRequestChild(aAnonymize, aDMDFile);
     actor->AddRef();
     return actor;
 }
 
-// This is just a wrapper for InfallibleTArray<MemoryReport> that implements
-// nsISupports, so it can be passed to nsIMemoryReporter::CollectReports.
-class MemoryReportsWrapper final : public nsISupports {
-    ~MemoryReportsWrapper() {}
-public:
-    NS_DECL_ISUPPORTS
-    explicit MemoryReportsWrapper(InfallibleTArray<MemoryReport>* r) : mReports(r) { }
-    InfallibleTArray<MemoryReport> *mReports;
-};
-NS_IMPL_ISUPPORTS0(MemoryReportsWrapper)
-
 class MemoryReportCallback final : public nsIMemoryReporterCallback
 {
 public:
     NS_DECL_ISUPPORTS
 
-    explicit MemoryReportCallback(const nsACString& aProcess)
-    : mProcess(aProcess)
+    explicit MemoryReportCallback(MemoryReportRequestChild* aActor,
+                                  const nsACString& aProcess)
+    : mActor(aActor)
+    , mProcess(aProcess)
     {
     }
 
     NS_IMETHOD Callback(const nsACString& aProcess, const nsACString &aPath,
                         int32_t aKind, int32_t aUnits, int64_t aAmount,
                         const nsACString& aDescription,
-                        nsISupports* aiWrappedReports) override
+                        nsISupports* aUnused) override
     {
-        MemoryReportsWrapper *wrappedReports =
-            static_cast<MemoryReportsWrapper *>(aiWrappedReports);
-
         MemoryReport memreport(mProcess, nsCString(aPath), aKind, aUnits,
                                aAmount, nsCString(aDescription));
-        wrappedReports->mReports->AppendElement(memreport);
+        mActor->SendReport(memreport);
         return NS_OK;
     }
 private:
     ~MemoryReportCallback() {}
 
+    nsRefPtr<MemoryReportRequestChild> mActor;
     const nsCString mProcess;
 };
 NS_IMPL_ISUPPORTS(
   MemoryReportCallback
 , nsIMemoryReporterCallback
 )
 
 bool
@@ -939,31 +927,28 @@ ContentChild::RecvPMemoryReportRequestCo
     return !NS_WARN_IF(NS_FAILED(rv));
 }
 
 NS_IMETHODIMP MemoryReportRequestChild::Run()
 {
     ContentChild *child = static_cast<ContentChild*>(Manager());
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
 
-    InfallibleTArray<MemoryReport> reports;
-
     nsCString process;
     child->GetProcessName(process);
     child->AppendProcessId(process);
 
     // Run the reporters.  The callback will turn each measurement into a
     // MemoryReport.
-    nsRefPtr<MemoryReportsWrapper> wrappedReports =
-        new MemoryReportsWrapper(&reports);
-    nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
-    mgr->GetReportsForThisProcessExtended(cb, wrappedReports, mAnonymize,
+    nsRefPtr<MemoryReportCallback> cb =
+        new MemoryReportCallback(this, process);
+    mgr->GetReportsForThisProcessExtended(cb, nullptr, mAnonymize,
                                           FileDescriptorToFILE(mDMDFile, "wb"));
 
-    bool sent = Send__delete__(this, mGeneration, reports);
+    bool sent = Send__delete__(this);
     return sent ? NS_OK : NS_ERROR_FAILURE;
 }
 
 bool
 ContentChild::RecvAudioChannelNotify()
 {
     nsRefPtr<AudioChannelService> service =
         AudioChannelService::GetAudioChannelService();
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -400,55 +400,75 @@ int32_t ContentParent::sNuwaPid = 0;
 bool ContentParent::sNuwaReady = false;
 #endif
 
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
 class MemoryReportRequestParent : public PMemoryReportRequestParent
 {
 public:
-    MemoryReportRequestParent();
+    explicit MemoryReportRequestParent(uint32_t aGeneration);
+
     virtual ~MemoryReportRequestParent();
 
     virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
-    virtual bool Recv__delete__(const uint32_t& aGeneration, InfallibleTArray<MemoryReport>&& aReport) override;
+    virtual bool RecvReport(const MemoryReport& aReport) override;
+    virtual bool Recv__delete__() override;
 
 private:
+    const uint32_t mGeneration;
+    // Non-null if we haven't yet called EndChildReport() on it.
+    nsRefPtr<nsMemoryReporterManager> mReporterManager;
+
     ContentParent* Owner()
     {
         return static_cast<ContentParent*>(Manager());
     }
 };
 
-MemoryReportRequestParent::MemoryReportRequestParent()
+MemoryReportRequestParent::MemoryReportRequestParent(uint32_t aGeneration)
+    : mGeneration(aGeneration)
 {
     MOZ_COUNT_CTOR(MemoryReportRequestParent);
+    mReporterManager = nsMemoryReporterManager::GetOrCreate();
+    NS_WARN_IF(!mReporterManager);
+}
+
+bool
+MemoryReportRequestParent::RecvReport(const MemoryReport& aReport)
+{
+    if (mReporterManager) {
+        mReporterManager->HandleChildReport(mGeneration, aReport);
+    }
+    return true;
+}
+
+bool
+MemoryReportRequestParent::Recv__delete__()
+{
+    // Notifying the reporter manager is done in ActorDestroy, because
+    // it needs to happen even if the child process exits mid-report.
+    // (The reporter manager will time out eventually, but let's avoid
+    // that if possible.)
+    return true;
 }
 
 void
 MemoryReportRequestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
-  // Implement me! Bug 1005154
-}
-
-bool
-MemoryReportRequestParent::Recv__delete__(const uint32_t& generation,
-                                          nsTArray<MemoryReport>&& childReports)
-{
-    nsRefPtr<nsMemoryReporterManager> mgr =
-        nsMemoryReporterManager::GetOrCreate();
-    if (mgr) {
-        mgr->HandleChildReports(generation, childReports);
-    }
-    return true;
+    if (mReporterManager) {
+        mReporterManager->EndChildReport(mGeneration, aWhy == Deletion);
+        mReporterManager = nullptr;
+    }
 }
 
 MemoryReportRequestParent::~MemoryReportRequestParent()
 {
+    MOZ_ASSERT(!mReporterManager);
     MOZ_COUNT_DTOR(MemoryReportRequestParent);
 }
 
 // IPC receiver for remote GC/CC logging.
 class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent
 {
 public:
     ~CycleCollectWithLogsParent()
@@ -3544,17 +3564,18 @@ ContentParent::DeallocPIccParent(PIccPar
 }
 
 PMemoryReportRequestParent*
 ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
                                                const bool &aAnonymize,
                                                const bool &aMinimizeMemoryUsage,
                                                const MaybeFileDesc &aDMDFile)
 {
-    MemoryReportRequestParent* parent = new MemoryReportRequestParent();
+    MemoryReportRequestParent* parent =
+        new MemoryReportRequestParent(aGeneration);
     return parent;
 }
 
 bool
 ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor)
 {
     delete actor;
     return true;
--- a/dom/ipc/PMemoryReportRequest.ipdl
+++ b/dom/ipc/PMemoryReportRequest.ipdl
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
 
 namespace mozilla {
 namespace dom {
@@ -15,14 +15,15 @@ struct MemoryReport {
   int32_t units;
   int64_t amount;
   nsCString desc;
 };
 
 protocol PMemoryReportRequest {
   manager PContent;
 
-  parent:
-    __delete__(uint32_t generation, MemoryReport[] report);
+parent:
+  Report(MemoryReport aReport);
+  __delete__();
 };
 
 }
 }
--- a/dom/media/omx/AudioOffloadPlayer.cpp
+++ b/dom/media/omx/AudioOffloadPlayer.cpp
@@ -46,17 +46,17 @@ namespace mozilla {
 PRLogModuleInfo* gAudioOffloadPlayerLog;
 #define AUDIO_OFFLOAD_LOG(type, msg) \
   PR_LOG(gAudioOffloadPlayerLog, type, msg)
 #else
 #define AUDIO_OFFLOAD_LOG(type, msg)
 #endif
 
 // maximum time in paused state when offloading audio decompression.
-// When elapsed, the AudioSink is destroyed to allow the audio DSP to power down.
+// When elapsed, the GonkAudioSink is destroyed to allow the audio DSP to power down.
 static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll;
 
 AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxCommonDecoder* aObserver) :
   mStarted(false),
   mPlaying(false),
   mReachedEOS(false),
   mIsElementVisible(true),
   mSampleRate(0),
@@ -468,38 +468,38 @@ void AudioOffloadPlayer::NotifyAudioTear
     mSeekPromise.Resolve(val, __func__);
   }
   nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
       &MediaOmxCommonDecoder::AudioOffloadTearDown);
   NS_DispatchToMainThread(nsEvent);
 }
 
 // static
-size_t AudioOffloadPlayer::AudioSinkCallback(AudioSink* aAudioSink,
+size_t AudioOffloadPlayer::AudioSinkCallback(GonkAudioSink* aAudioSink,
                                              void* aBuffer,
                                              size_t aSize,
                                              void* aCookie,
-                                             AudioSink::cb_event_t aEvent)
+                                             GonkAudioSink::cb_event_t aEvent)
 {
   AudioOffloadPlayer* me = (AudioOffloadPlayer*) aCookie;
 
   switch (aEvent) {
 
-    case AudioSink::CB_EVENT_FILL_BUFFER:
+    case GonkAudioSink::CB_EVENT_FILL_BUFFER:
       AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Audio position changed"));
       me->NotifyPositionChanged();
       return me->FillBuffer(aBuffer, aSize);
 
-    case AudioSink::CB_EVENT_STREAM_END:
+    case GonkAudioSink::CB_EVENT_STREAM_END:
       AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Audio EOS"));
       me->mReachedEOS = true;
       me->NotifyAudioEOS();
       break;
 
-    case AudioSink::CB_EVENT_TEAR_DOWN:
+    case GonkAudioSink::CB_EVENT_TEAR_DOWN:
       AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Tear down event"));
       me->NotifyAudioTearDown();
       break;
 
     default:
       AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Unknown event %d from audio sink",
           aEvent));
       break;
@@ -692,17 +692,17 @@ MediaDecoderOwner::NextFrameStatus Audio
     return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING;
   } else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) {
     return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
   } else {
     return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
   }
 }
 
-void AudioOffloadPlayer::SendMetaDataToHal(sp<AudioSink>& aSink,
+void AudioOffloadPlayer::SendMetaDataToHal(sp<GonkAudioSink>& aSink,
                                            const sp<MetaData>& aMeta)
 {
   int32_t sampleRate = 0;
   int32_t bitRate = 0;
   int32_t channelMask = 0;
   int32_t delaySamples = 0;
   int32_t paddingSamples = 0;
   CHECK(aSink.get());
--- a/dom/media/omx/AudioOffloadPlayer.h
+++ b/dom/media/omx/AudioOffloadPlayer.h
@@ -45,21 +45,21 @@ class WakeLock;
  *
  * This depends on offloading capability provided by Android KK AudioTrack class
  *
  * Audio playback is based on pull mechanism, whenever audio sink needs
  * data, FillBuffer() will read data from compressed audio source and provide
  * it to the sink
  *
  * Also this class passes state changes (play/pause/seek) from
- * MediaOmxCommonDecoder to AudioSink as well as provide AudioSink status
+ * MediaOmxCommonDecoder to GonkAudioSink as well as provide GonkAudioSink status
  * (position changed, playback ended, seek complete, audio tear down) back to
  * MediaOmxCommonDecoder
  *
- * It acts as a bridge between MediaOmxCommonDecoder and AudioSink during
+ * It acts as a bridge between MediaOmxCommonDecoder and GonkAudioSink during
  * offload playback
  */
 
 class AudioOffloadPlayer : public AudioOffloadPlayerBase
 {
   typedef android::Mutex Mutex;
   typedef android::MetaData MetaData;
   typedef android::status_t status_t;
@@ -75,17 +75,17 @@ public:
 
   AudioOffloadPlayer(MediaOmxCommonDecoder* aDecoder = nullptr);
 
   ~AudioOffloadPlayer();
 
   // Caller retains ownership of "aSource".
   virtual void SetSource(const android::sp<MediaSource> &aSource) override;
 
-  // Start the source if it's not already started and open the AudioSink to
+  // Start the source if it's not already started and open the GonkAudioSink to
   // create an offloaded audio track
   virtual status_t Start(bool aSourceAlreadyStarted = false) override;
 
   virtual status_t ChangeState(MediaDecoder::PlayState aState) override;
 
   virtual void SetVolume(double aVolume) override;
 
   virtual double GetMediaTimeSecs() override;
@@ -101,17 +101,17 @@ public:
   virtual nsRefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget) override;
 
   void TimeUpdate();
 
   // Close the audio sink, stop time updates, frees the input buffers
   void Reset();
 
 private:
-  // Set when audio source is started and audioSink is initialized
+  // Set when audio source is started and GonkAudioSink is initialized
   // Used only in main thread
   bool mStarted;
 
   // Set when audio sink is started. i.e. playback started
   // Used only in main thread
   bool mPlaying;
 
   // Once playback reached end of stream (last ~100ms), position provided by DSP
@@ -165,30 +165,30 @@ private:
 
   // Compressed audio source.
   // Used in main thread first and later in offload callback thread
   android::sp<MediaSource> mSource;
 
   // Audio sink wrapper to access offloaded audio tracks
   // Used in main thread and offload callback thread
   // Race conditions are protected in underlying Android::AudioTrack class
-  android::sp<AudioSink> mAudioSink;
+  android::sp<GonkAudioSink> mAudioSink;
 
   // Buffer used to get date from audio source. Used in offload callback thread
   MediaBuffer* mInputBuffer;
 
   // MediaOmxCommonDecoder object used mainly to notify the audio sink status
   MediaOmxCommonDecoder* mObserver;
 
   TimeStamp mLastFireUpdateTime;
 
   // Timer to trigger position changed events
   nsCOMPtr<nsITimer> mTimeUpdateTimer;
 
-  // Timer to reset AudioSink when audio is paused for OFFLOAD_PAUSE_MAX_USECS.
+  // Timer to reset GonkAudioSink when audio is paused for OFFLOAD_PAUSE_MAX_USECS.
   // It is triggered in Pause() and canceled when there is a Play() within
   // OFFLOAD_PAUSE_MAX_USECS. Used only from main thread so no lock is needed.
   nsCOMPtr<nsITimer> mResetTimer;
 
   // To avoid device suspend when mResetTimer is going to be triggered.
   // Used only from main thread so no lock is needed.
   nsRefPtr<mozilla::dom::WakeLock> mWakeLock;
 
@@ -198,22 +198,22 @@ private:
   // frames played by audio track
   int64_t GetOutputPlayPositionUs_l() const;
 
   // Fill the buffer given by audio sink with data from compressed audio
   // source. Also handles the seek by seeking audio source and stop the sink in
   // case of error
   size_t FillBuffer(void *aData, size_t aSize);
 
-  // Called by AudioSink when it needs data, to notify EOS or tear down event
-  static size_t AudioSinkCallback(AudioSink *aAudioSink,
+  // Called by GonkAudioSink when it needs data, to notify EOS or tear down event
+  static size_t AudioSinkCallback(GonkAudioSink *aAudioSink,
                                   void *aData,
                                   size_t aSize,
                                   void *aMe,
-                                  AudioSink::cb_event_t aEvent);
+                                  GonkAudioSink::cb_event_t aEvent);
 
   bool IsSeeking();
 
   // Set mSeekTarget to the given position and restart the sink. Actual seek
   // happens in FillBuffer(). If mSeekPromise is not empty, send
   // SeekingStarted event always and SeekingStopped event when the play state is
   // paused to MediaDecoder.
   // When decoding and playing happens separately, if there is a seek during
@@ -249,18 +249,18 @@ private:
   // Notify position changed event by sending PlaybackPositionChanged event to
   // observer
   void NotifyPositionChanged();
 
   // Offloaded audio track is invalidated due to usecase change. Notify
   // MediaDecoder to re-evaluate offloading options
   void NotifyAudioTearDown();
 
-  // Send information from MetaData to the HAL via AudioSink
-  void SendMetaDataToHal(android::sp<AudioSink>& aSink,
+  // Send information from MetaData to the HAL via GonkAudioSink
+  void SendMetaDataToHal(android::sp<GonkAudioSink>& aSink,
                          const android::sp<MetaData>& aMeta);
 
   AudioOffloadPlayer(const AudioOffloadPlayer &);
   AudioOffloadPlayer &operator=(const AudioOffloadPlayer &);
 };
 
 } // namespace mozilla
 
--- a/dom/media/omx/AudioOutput.h
+++ b/dom/media/omx/AudioOutput.h
@@ -19,27 +19,27 @@
 
 #ifndef AUDIOOUTPUT_H_
 #define AUDIOOUTPUT_H_
 
 #include <stagefright/foundation/ABase.h>
 #include <utils/Mutex.h>
 #include <AudioTrack.h>
 
-#include "AudioSink.h"
+#include "GonkAudioSink.h"
 
 namespace mozilla {
 
 /**
  * Stripped version of Android KK MediaPlayerService::AudioOutput class
  * Android MediaPlayer uses AudioOutput as a wrapper to handle
  * Android::AudioTrack
  * Similarly to ease handling offloaded tracks, part of AudioOutput is used here
  */
-class AudioOutput : public AudioSink
+class AudioOutput : public GonkAudioSink
 {
   typedef android::Mutex Mutex;
   typedef android::String8 String8;
   typedef android::status_t status_t;
   typedef android::AudioTrack AudioTrack;
 
   class CallbackData;
 
rename from dom/media/omx/AudioSink.h
rename to dom/media/omx/GonkAudioSink.h
--- a/dom/media/omx/AudioSink.h
+++ b/dom/media/omx/GonkAudioSink.h
@@ -12,18 +12,18 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#ifndef AUDIO_SINK_H_
-#define AUDIO_SINK_H_
+#ifndef GONK_AUDIO_SINK_H_
+#define GONK_AUDIO_SINK_H_
 
 #include <utils/Errors.h>
 #include <utils/String8.h>
 #include <system/audio.h>
 
 #define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
 #define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
 #define DEFAULT_AUDIOSINK_SAMPLERATE 44100
@@ -34,37 +34,37 @@
 
 namespace mozilla {
 
 /**
  * AudioSink: abstraction layer for audio output
  * Stripped version of Android KK MediaPlayerBase::AudioSink class
  */
 
-class AudioSink : public android::RefBase
+class GonkAudioSink : public android::RefBase
 {
   typedef android::String8 String8;
   typedef android::status_t status_t;
 
 public:
   enum cb_event_t {
     CB_EVENT_FILL_BUFFER,   // Request to write more data to buffer.
     CB_EVENT_STREAM_END,    // Sent after all the buffers queued in AF and HW
                             // are played back (after stop is called)
     CB_EVENT_TEAR_DOWN      // The AudioTrack was invalidated due to usecase
                             // change. Need to re-evaluate offloading options
   };
 
   // Callback returns the number of bytes actually written to the buffer.
-  typedef size_t (*AudioCallback)(AudioSink* aAudioSink,
+  typedef size_t (*AudioCallback)(GonkAudioSink* aAudioSink,
                                   void* aBuffer,
                                   size_t aSize,
                                   void* aCookie,
                                   cb_event_t aEvent);
-  virtual ~AudioSink() {}
+  virtual ~GonkAudioSink() {}
   virtual ssize_t FrameSize() const = 0;
   virtual status_t GetPosition(uint32_t* aPosition) const = 0;
   virtual status_t SetVolume(float aVolume) const = 0;
   virtual status_t SetParameters(const String8& aKeyValuePairs)
   {
     return android::NO_ERROR;
   }
 
@@ -81,9 +81,9 @@ public:
   virtual void Stop() = 0;
   virtual void Flush() = 0;
   virtual void Pause() = 0;
   virtual void Close() = 0;
 };
 
 } // namespace mozilla
 
-#endif // AUDIO_SINK_H_
+#endif // GONK_AUDIO_SINK_H_
--- a/dom/media/omx/moz.build
+++ b/dom/media/omx/moz.build
@@ -21,17 +21,17 @@ SOURCES += [
     'OMXCodecProxy.cpp',
     'OmxDecoder.cpp',
 ]
 
 if CONFIG['MOZ_AUDIO_OFFLOAD']:
     EXPORTS += [
         'AudioOffloadPlayer.h',
         'AudioOutput.h',
-        'AudioSink.h',
+        'GonkAudioSink.h',
     ]
     SOURCES += [
         'AudioOffloadPlayer.cpp',
         'AudioOutput.cpp',
     ]
 
 if CONFIG['MOZ_OMX_ENCODER']:
     EXPORTS += [
--- a/dom/mobileconnection/gonk/MobileConnectionService.js
+++ b/dom/mobileconnection/gonk/MobileConnectionService.js
@@ -51,16 +51,20 @@ const UNKNOWN_RSSI = 99;
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionMessenger",
                                    "@mozilla.org/ril/system-messenger-helper;1",
                                    "nsIMobileConnectionMessenger");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
+
 XPCOMUtils.defineLazyGetter(this, "gRadioInterfaceLayer", function() {
   let ril = { numRadioInterfaces: 0 };
   try {
     ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
   } catch(e) {}
   return ril;
 });
 
@@ -382,19 +386,18 @@ MobileConnectionProvider.prototype = {
     }
   },
 
   /**
    * Fix the roaming. RIL can report roaming in some case it is not
    * really the case. See bug 787967
    */
   _checkRoamingBetweenOperators: function(aNetworkInfo) {
-    // TODO: Bug 864489 - B2G RIL: use ipdl as IPC in MozIccManager
-    // Should get iccInfo from GonkIccProvider.
-    let iccInfo = this._radioInterface.rilContext.iccInfo;
+    let icc = gIccService.getIccByServiceId(this._clientId);
+    let iccInfo = icc ? icc.iccInfo : null;
     let operator = aNetworkInfo.network;
     let state = aNetworkInfo.state;
 
     if (!iccInfo || !operator ||
         state !== RIL.GECKO_MOBILE_CONNECTION_STATE_REGISTERED) {
       return false;
     }
 
--- a/dom/mobilemessage/gonk/MmsService.js
+++ b/dom/mobilemessage/gonk/MmsService.js
@@ -129,16 +129,20 @@ const PREF_RETRIEVAL_RETRY_INTERVALS = (
 
 const kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
 const kPrefDefaultServiceId = "dom.mms.defaultServiceId";
 
 XPCOMUtils.defineLazyServiceGetter(this, "gpps",
                                    "@mozilla.org/network/protocol-proxy-service;1",
                                    "nsIProtocolProxyService");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
+
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
                                    "@mozilla.org/mobilemessage/gonkmobilemessagedatabaseservice;1",
                                    "nsIGonkMobileMessageDatabaseService");
 
@@ -342,17 +346,17 @@ MmsConnection.prototype = {
    * Otherwise, the phone number is in mdn.
    * @see nsICdmaIccInfo
    */
   getPhoneNumber: function() {
     let number;
     // Get the proper IccInfo based on the current card type.
     try {
       let iccInfo = null;
-      let baseIccInfo = this.radioInterface.rilContext.iccInfo;
+      let baseIccInfo = this.getIccInfo();
       if (baseIccInfo.iccType === 'ruim' || baseIccInfo.iccType === 'csim') {
         iccInfo = baseIccInfo.QueryInterface(Ci.nsICdmaIccInfo);
         number = iccInfo.mdn;
       } else {
         iccInfo = baseIccInfo.QueryInterface(Ci.nsIGsmIccInfo);
         number = iccInfo.msisdn;
       }
     } catch (e) {
@@ -361,20 +365,36 @@ MmsConnection.prototype = {
       }
       return null;
     }
 
     return number;
   },
 
   /**
+   * A utility function to get IccInfo of the SIM card (if installed).
+   */
+  getIccInfo: function() {
+    let icc = gIccService.getIccByServiceId(this.serviceId);
+    return icc ? icc.iccInfo : null;
+  },
+
+  /**
+   * A utility function to get CardState of the SIM card (if installed).
+   */
+  getCardState: function() {
+    let icc = gIccService.getIccByServiceId(this.serviceId);
+    return icc ? icc.cardState : Ci.nsIIcc.CARD_STATE_UNKNOWN;
+  },
+
+  /**
   * A utility function to get the ICC ID of the SIM card (if installed).
   */
   getIccId: function() {
-    let iccInfo = this.radioInterface.rilContext.iccInfo;
+    let iccInfo = this.getIccInfo();
 
     if (!iccInfo) {
       return null;
     }
 
     return iccInfo.iccid;
   },
 
@@ -399,18 +419,17 @@ MmsConnection.prototype = {
     // MMS request and try to setup the MMS network first.
     if (!this.connected) {
       this.pendingCallbacks.push(callback);
 
       let errorStatus;
       if (getRadioDisabledState()) {
         if (DEBUG) debug("Error! Radio is disabled when sending MMS.");
         errorStatus = _HTTP_STATUS_RADIO_DISABLED;
-      } else if (this.radioInterface.rilContext.cardState !=
-                 Ci.nsIIcc.CARD_STATE_READY) {
+      } else if (this.getCardState() != Ci.nsIIcc.CARD_STATE_READY) {
         if (DEBUG) debug("Error! SIM card is not ready when sending MMS.");
         errorStatus = _HTTP_STATUS_NO_SIM_CARD;
       }
       if (errorStatus != null) {
         this.flushPendingCallbacks(errorStatus);
         return true;
       }
 
--- a/dom/mobilemessage/gonk/SmsService.js
+++ b/dom/mobilemessage/gonk/SmsService.js
@@ -71,19 +71,23 @@ XPCOMUtils.defineLazyGetter(this, "gPhon
 
 XPCOMUtils.defineLazyGetter(this, "gWAP", function() {
   let ns = {};
   Cu.import("resource://gre/modules/WapPushManager.js", ns);
   return ns;
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "gCellBroadcastService",
-                                   "@mozilla.org/cellbroadcast/gonkservice;1",
+                                   "@mozilla.org/cellbroadcast/cellbroadcastservice;1",
                                    "nsIGonkCellBroadcastService");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
+
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
                                    "@mozilla.org/mobileconnection/mobileconnectionservice;1",
                                    "nsIMobileConnectionService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService",
                                    "@mozilla.org/mobilemessage/gonkmobilemessagedatabaseservice;1",
                                    "nsIGonkMobileMessageDatabaseService");
 
@@ -153,17 +157,17 @@ SmsService.prototype = {
     return id;
   },
 
   _getPhoneNumber: function(aServiceId) {
     let number;
     // Get the proper IccInfo based on the current card type.
     try {
       let iccInfo = null;
-      let baseIccInfo = gRadioInterfaces[aServiceId].rilContext.iccInfo;
+      let baseIccInfo = this._getIccInfo(aServiceId);
       if (baseIccInfo.iccType === 'ruim' || baseIccInfo.iccType === 'csim') {
         iccInfo = baseIccInfo.QueryInterface(Ci.nsICdmaIccInfo);
         number = iccInfo.mdn;
       } else {
         iccInfo = baseIccInfo.QueryInterface(Ci.nsIGsmIccInfo);
         number = iccInfo.msisdn;
       }
     } catch (e) {
@@ -171,18 +175,28 @@ SmsService.prototype = {
        debug("Exception - QueryInterface failed on iccinfo for GSM/CDMA info");
       }
       return null;
     }
 
     return number;
   },
 
+  _getIccInfo: function(aServiceId) {
+    let icc = gIccService.getIccByServiceId(aServiceId);
+    return icc ? icc.iccInfo : null;
+  },
+
+  _getCardState: function(aServiceId) {
+    let icc = gIccService.getIccByServiceId(aServiceId);
+    return icc ? icc.cardState : Ci.nsIIcc.CARD_STATE_UNKNOWN;
+  },
+
   _getIccId: function(aServiceId) {
-    let iccInfo = gRadioInterfaces[aServiceId].rilContext.iccInfo;
+    let iccInfo = this._getIccInfo(aServiceId);
 
     if (!iccInfo) {
       return null;
     }
 
     return iccInfo.iccid;
   },
 
@@ -882,18 +896,17 @@ SmsService.prototype = {
       let radioState = connection && connection.radioState;
       if (!gPhoneNumberUtils.isPlainPhoneNumber(options.number)) {
         if (DEBUG) debug("Error! Address is invalid when sending SMS: " + options.number);
         errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR;
       } else if (radioState == Ci.nsIMobileConnection.MOBILE_RADIO_STATE_UNKNOWN ||
                  radioState == Ci.nsIMobileConnection.MOBILE_RADIO_STATE_DISABLED) {
         if (DEBUG) debug("Error! Radio is disabled when sending SMS.");
         errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR;
-      } else if (gRadioInterfaces[aServiceId].rilContext.cardState !=
-                 Ci.nsIIcc.CARD_STATE_READY) {
+      } else if (this._getCardState(aServiceId) != Ci.nsIIcc.CARD_STATE_READY) {
         if (DEBUG) debug("Error! SIM card is not ready when sending SMS.");
         errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR;
       }
       if (errorCode) {
         if (aSilent) {
           aRequest.notifySendMessageFailed(errorCode, aSendingMessage);
           return;
         }
--- a/dom/network/NetworkStatsService.jsm
+++ b/dom/network/NetworkStatsService.jsm
@@ -64,16 +64,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
                                    "@mozilla.org/settingsService;1",
                                    "nsISettingsService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "messenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
+
 this.NetworkStatsService = {
   init: function() {
     debug("Service started");
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.obs.addObserver(this, TOPIC_CONNECTION_STATE_CHANGED, false);
     Services.obs.addObserver(this, TOPIC_BANDWIDTH_CONTROL, false);
     Services.obs.addObserver(this, "profile-after-change", false);
@@ -248,21 +252,22 @@ this.NetworkStatsService = {
 
   /*
    * nsINetworkStatsService
    */
   getRilNetworks: function() {
     let networks = {};
     let numRadioInterfaces = gRil.numRadioInterfaces;
     for (let i = 0; i < numRadioInterfaces; i++) {
+      let icc = gIccService.getIccByServiceId(i);
       let radioInterface = gRil.getRadioInterface(i);
-      if (radioInterface.rilContext.iccInfo) {
-        let netId = this.getNetworkId(radioInterface.rilContext.iccInfo.iccid,
+      if (icc && icc.iccInfo) {
+        let netId = this.getNetworkId(icc.iccInfo.iccid,
                                       NET_TYPE_MOBILE);
-        networks[netId] = { id : radioInterface.rilContext.iccInfo.iccid,
+        networks[netId] = { id : icc.iccInfo.iccid,
                             type: NET_TYPE_MOBILE };
       }
     }
     return networks;
   },
 
   convertNetworkInterface: function(aNetwork) {
     if (aNetwork.type != NET_TYPE_MOBILE &&
--- a/dom/svg/DOMSVGLength.cpp
+++ b/dom/svg/DOMSVGLength.cpp
@@ -366,17 +366,17 @@ void
 DOMSVGLength::SetValueAsString(const nsAString& aValue, ErrorResult& aRv)
 {
   if (mIsAnimValItem) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (mVal) {
-    mVal->SetBaseValueString(aValue, mSVGElement, true);
+    aRv = mVal->SetBaseValueString(aValue, mSVGElement, true);
     return;
   }
 
   SVGLength value;
   if (!value.SetValueFromString(aValue)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
--- a/dom/svg/test/test_valueAsString.xhtml
+++ b/dom/svg/test/test_valueAsString.xhtml
@@ -1,14 +1,14 @@
 <html xmlns="http://www.w3.org/1999/xhtml">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=539697 
 -->
 <head>
-  <title>Test SVGTransform behavior</title>
+  <title>Test valueAsString behavior</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=539697">Mozilla Bug 539697</a>
 <p id="display"></p>
 <div id="content">
 
@@ -30,27 +30,29 @@ function run()
   var m = document.getElementById('m');
 
   is(SVGLength.SVG_LENGTHTYPE_EMS, c.r.baseVal.unitType, 'unexpected units');
   c.r.baseVal.valueAsString = '2px';
   is(SVGLength.SVG_LENGTHTYPE_PX, c.r.baseVal.unitType, 'unexpected units');
 
   try {
     c.r.baseVal.valueAsString = 'rubbish';
+    ok(false, 'setting a length to rubbish should fail');
   } catch (e) {
     is(e.name, 'SyntaxError', 'syntax error expected');
     is(e.code, DOMException.SYNTAX_ERR, 'syntax error expected');
   }
 
   is(SVGAngle.SVG_ANGLETYPE_RAD, m.orientAngle.baseVal.unitType, 'unexpected units');
   m.orientAngle.baseVal.valueAsString = '2grad';
   is(SVGAngle.SVG_ANGLETYPE_GRAD, m.orientAngle.baseVal.unitType, 'unexpected units');
 
   try {
     m.orientAngle.baseVal.valueAsString = 'rubbish';
+    ok(false, 'setting an angle to rubbish should fail');
   } catch (e) {
     is(e.name, 'SyntaxError', 'syntax error expected');
     is(e.code, DOMException.SYNTAX_ERR, 'syntax error expected');
   }
 
   SimpleTest.finish();
 }
 
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -38,16 +38,17 @@
 
 #ifdef MOZ_B2G_RIL
 #include "nsIIccInfo.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileCellInfo.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsIRadioInterfaceLayer.h"
+#include "nsIIccService.h"
 #endif
 
 #ifdef AGPS_TYPE_INVALID
 #define AGPS_HAVE_DUAL_APN
 #endif
 
 #define FLUSH_AIDE_DATA 0
 
@@ -471,41 +472,45 @@ GonkGPSGeolocationProvider::RequestSetID
 
   if (!mRadioInterface ||
       !mAGpsInterface) {
     return;
   }
 
   AGpsSetIDType type = AGPS_SETID_TYPE_NONE;
 
-  nsCOMPtr<nsIRilContext> rilCtx;
-  mRadioInterface->GetRilContext(getter_AddRefs(rilCtx));
+  nsCOMPtr<nsIIccService> iccService =
+    do_GetService(ICC_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE_VOID(iccService);
 
-  if (rilCtx) {
-    nsAutoString id;
-    if (flags & AGPS_RIL_REQUEST_SETID_IMSI) {
-      type = AGPS_SETID_TYPE_IMSI;
-      rilCtx->GetImsi(id);
-    }
+  nsCOMPtr<nsIIcc> icc;
+  iccService->GetIccByServiceId(mRilDataServiceId, getter_AddRefs(icc));
+  NS_ENSURE_TRUE_VOID(icc);
+
+  nsAutoString id;
 
-    if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) {
-      nsCOMPtr<nsIIccInfo> iccInfo;
-      rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
-      if (iccInfo) {
-        nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
-        if (gsmIccInfo) {
-          type = AGPS_SETID_TYPE_MSISDN;
-          gsmIccInfo->GetMsisdn(id);
-        }
+  if (flags & AGPS_RIL_REQUEST_SETID_IMSI) {
+    type = AGPS_SETID_TYPE_IMSI;
+    icc->GetImsi(id);
+  }
+
+  if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) {
+    nsCOMPtr<nsIIccInfo> iccInfo;
+    icc->GetIccInfo(getter_AddRefs(iccInfo));
+    if (iccInfo) {
+      nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+      if (gsmIccInfo) {
+        type = AGPS_SETID_TYPE_MSISDN;
+        gsmIccInfo->GetMsisdn(id);
       }
     }
+  }
 
-    NS_ConvertUTF16toUTF8 idBytes(id);
-    mAGpsRilInterface->set_set_id(type, idBytes.get());
-  }
+  NS_ConvertUTF16toUTF8 idBytes(id);
+  mAGpsRilInterface->set_set_id(type, idBytes.get());
 }
 
 void
 GonkGPSGeolocationProvider::SetReferenceLocation()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mRadioInterface ||
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -39,30 +39,23 @@ let DEBUG;
 function debug(s) {
   dump("-*- RILContentHelper: " + s + "\n");
 }
 
 const RILCONTENTHELPER_CID =
   Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
 
 const RIL_IPC_MSG_NAMES = [
-  "RIL:CardStateChanged",
-  "RIL:IccInfoChanged",
-  "RIL:GetCardLockResult",
-  "RIL:SetUnlockCardLockResult",
-  "RIL:CardLockRetryCount",
   "RIL:StkCommand",
   "RIL:StkSessionEnd",
   "RIL:IccOpenChannel",
   "RIL:IccCloseChannel",
   "RIL:IccExchangeAPDU",
   "RIL:ReadIccContacts",
   "RIL:UpdateIccContact",
-  "RIL:MatchMvno",
-  "RIL:GetServiceState"
 ];
 
 /* global cpmm */
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsISyncMessageSender");
 
 /* global UUIDGenerator */
@@ -82,166 +75,52 @@ XPCOMUtils.defineLazyGetter(this, "gNumR
       ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer);
     } catch(e) {}
     return ril.numRadioInterfaces;
   }
 
   return Services.prefs.getIntPref(kPrefRilNumRadioInterfaces);
 });
 
-function IccInfo() {}
-IccInfo.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
-
-  // nsIIccInfo
-
-  iccType: null,
-  iccid: null,
-  mcc: null,
-  mnc: null,
-  spn: null,
-  isDisplayNetworkNameRequired: false,
-  isDisplaySpnRequired: false
-};
-
-function GsmIccInfo() {}
-GsmIccInfo.prototype = {
-  __proto__: IccInfo.prototype,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
-                                         Ci.nsIIccInfo]),
-
-  // nsIGsmIccInfo
-
-  msisdn: null
-};
-
-function CdmaIccInfo() {}
-CdmaIccInfo.prototype = {
-  __proto__: IccInfo.prototype,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
-                                         Ci.nsIIccInfo]),
-
-  // nsICdmaIccInfo
-
-  mdn: null,
-  prlVersion: 0
-};
-
 function RILContentHelper() {
   this.updateDebugFlag();
 
   this.numClients = gNumRadioInterfaces;
   if (DEBUG) debug("Number of clients: " + this.numClients);
 
-  this._iccs = [];
-  this.rilContexts = [];
-  for (let clientId = 0; clientId < this.numClients; clientId++) {
-    this._iccs.push(new Icc(this, clientId));
-    this.rilContexts[clientId] = {
-      cardState: Ci.nsIIcc.CARD_STATE_UNKNOWN,
-      iccInfo: null
-    };
-  }
-
   this.initDOMRequestHelper(/* aWindow */ null, RIL_IPC_MSG_NAMES);
   this._windowsMap = [];
-  this._requestMap = [];
   this._iccListeners = [];
   this._iccChannelCallback = [];
 
   Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 
   Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
 }
 
 RILContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccProvider,
-                                         Ci.nsIIccService,
                                          Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
   classID:   RILCONTENTHELPER_CID,
   classInfo: XPCOMUtils.generateCI({classID: RILCONTENTHELPER_CID,
                                     classDescription: "RILContentHelper",
-                                    interfaces: [Ci.nsIIccProvider,
-                                                 Ci.nsIIccService]}),
+                                    interfaces: [Ci.nsIIccProvider]}),
 
   updateDebugFlag: function() {
     try {
       DEBUG = RIL.DEBUG_CONTENT_HELPER ||
               Services.prefs.getBoolPref(kPrefRilDebuggingEnabled);
     } catch (e) {}
   },
 
-  // An utility function to copy objects.
-  updateInfo: function(srcInfo, destInfo) {
-    for (let key in srcInfo) {
-      destInfo[key] = srcInfo[key];
-    }
-  },
-
-  /**
-   * We need to consider below cases when update iccInfo:
-   * 1. Should clear iccInfo to null if there is no card detected.
-   * 2. Need to create corresponding object based on iccType.
-   */
-  updateIccInfo: function(clientId, newInfo) {
-    let rilContext = this.rilContexts[clientId];
-
-    // Card is not detected, clear iccInfo to null.
-    if (!newInfo || !newInfo.iccid) {
-      if (rilContext.iccInfo) {
-        rilContext.iccInfo = null;
-      }
-      return;
-    }
-
-    // If iccInfo is null, new corresponding object based on iccType.
-    if (!rilContext.iccInfo) {
-      if (newInfo.iccType === "ruim" || newInfo.iccType === "csim") {
-        rilContext.iccInfo = new CdmaIccInfo();
-      } else if (newInfo.iccType === "sim" || newInfo.iccType === "usim") {
-        rilContext.iccInfo = new GsmIccInfo();
-      } else {
-        rilContext.iccInfo = new IccInfo();
-      }
-    }
-
-    this.updateInfo(newInfo, rilContext.iccInfo);
-  },
-
   _windowsMap: null,
 
-  _requestMap: null,
-
-  rilContexts: null,
-
-  getRilContext: function(clientId) {
-    // Update ril contexts by sending IPC message to chrome only when the first
-    // time we require it. The information will be updated by following info
-    // changed messages.
-    this.getRilContext = function getRilContext(clientId) {
-      return this.rilContexts[clientId];
-    };
-
-    for (let cId = 0; cId < this.numClients; cId++) {
-      let rilContext =
-        cpmm.sendSyncMessage("RIL:GetRilContext", {clientId: cId})[0];
-      if (!rilContext) {
-        if (DEBUG) debug("Received null rilContext from chrome process.");
-        continue;
-      }
-      this.rilContexts[cId].cardState = rilContext.cardState;
-      this.updateIccInfo(cId, rilContext.iccInfo);
-    }
-
-    return this.rilContexts[clientId];
-  },
-
   /**
    * nsIIccProvider
    */
 
   sendStkResponse: function(clientId, window, command, response) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
@@ -563,71 +442,16 @@ RILContentHelper.prototype = {
     let request;
     if (DEBUG) {
       debug("Received message '" + msg.name + "': " + JSON.stringify(msg.json));
     }
 
     let data = msg.json.data;
     let clientId = msg.json.clientId;
     switch (msg.name) {
-      case "RIL:CardStateChanged":
-        if (this.rilContexts[clientId].cardState != data.cardState) {
-          this.rilContexts[clientId].cardState = data.cardState;
-          this._deliverIccEvent(clientId,
-                                "notifyCardStateChanged",
-                                null);
-        }
-        break;
-      case "RIL:IccInfoChanged":
-        this.updateIccInfo(clientId, data);
-        this._deliverIccEvent(clientId,
-                              "notifyIccInfoChanged",
-                              null);
-        break;
-      case "RIL:GetCardLockResult": {
-        let requestId = data.requestId;
-        let callback = this._requestMap[requestId];
-        delete this._requestMap[requestId];
-
-        if (data.errorMsg) {
-          callback.notifyError(data.errorMsg);
-          break;
-        }
-
-        callback.notifySuccessWithBoolean(data.enabled);
-        break;
-      }
-      case "RIL:SetUnlockCardLockResult": {
-        let requestId = data.requestId;
-        let callback = this._requestMap[requestId];
-        delete this._requestMap[requestId];
-
-        if (data.errorMsg) {
-          let retryCount =
-            (data.retryCount !== undefined) ? data.retryCount : -1;
-          callback.notifyCardLockError(data.errorMsg, retryCount);
-          break;
-        }
-
-        callback.notifySuccess();
-        break;
-      }
-      case "RIL:CardLockRetryCount": {
-        let requestId = data.requestId;
-        let callback = this._requestMap[requestId];
-        delete this._requestMap[requestId];
-
-        if (data.errorMsg) {
-          callback.notifyError(data.errorMsg);
-          break;
-        }
-
-        callback.notifyGetCardLockRetryCount(data.retryCount);
-        break;
-      }
       case "RIL:StkCommand":
         this._deliverEvent(clientId, "_iccListeners", "notifyStkCommand",
                            [JSON.stringify(data)]);
         break;
       case "RIL:StkSessionEnd":
         this._deliverEvent(clientId, "_iccListeners", "notifyStkSessionEnd", null);
         break;
       case "RIL:IccOpenChannel":
@@ -640,40 +464,16 @@ RILContentHelper.prototype = {
         this.handleIccExchangeAPDU(data);
         break;
       case "RIL:ReadIccContacts":
         this.handleReadIccContacts(data);
         break;
       case "RIL:UpdateIccContact":
         this.handleUpdateIccContact(data);
         break;
-      case "RIL:MatchMvno": {
-        let requestId = data.requestId;
-        let callback = this._requestMap[requestId];
-        delete this._requestMap[requestId];
-
-        if (data.errorMsg) {
-          callback.notifyError(data.errorMsg);
-          break;
-        }
-        callback.notifySuccessWithBoolean(data.result);
-        break;
-      }
-      case "RIL:GetServiceState": {
-        let requestId = data.requestId;
-        let callback = this._requestMap[requestId];
-        delete this._requestMap[requestId];
-
-        if (data.errorMsg) {
-          callback.notifyError(data.errorMsg);
-          break;
-        }
-        callback.notifySuccessWithBoolean(data.result);
-        break;
-      }
     }
   },
 
   handleSimpleRequest: function(requestId, errorMsg, result) {
     if (errorMsg) {
       this.fireRequestError(requestId, errorMsg);
     } else {
       this.fireRequestSuccess(requestId, result);
@@ -790,241 +590,12 @@ RILContentHelper.prototype = {
         throw new Error("No handler for " + name);
       }
       try {
         handler.apply(listener, args);
       } catch (e) {
         if (DEBUG) debug("listener for " + name + " threw an exception: " + e);
       }
     }
-  },
-
-  /**
-   * nsIIccService interface.
-   */
-
-  _iccs: null, // An array of Icc instances.
-
-  getIccByServiceId: function(serviceId) {
-    let icc = this._iccs[serviceId];
-    if (!icc) {
-      throw Cr.NS_ERROR_UNEXPECTED;
-    }
-
-    return icc;
-  },
-
-  /**
-   * Bridge APIs from nsIIccService to nsIIccProvider
-   */
-
-  _deliverIccEvent: function(clientId, name, args) {
-    let icc = this._iccs[clientId];
-    if (!icc) {
-      if (DEBUG) debug("_deliverIccEvent: Invalid clientId: " + clientId);
-      return;
-    }
-
-    icc.deliverListenerEvent(name, args);
-  },
-
-  getIccInfo: function(clientId) {
-    let context = this.getRilContext(clientId);
-    return context && context.iccInfo;
-  },
-
-  getCardState: function(clientId) {
-    let context = this.getRilContext(clientId);
-    return context && context.cardState;
-  },
-
-  matchMvno: function(clientId, mvnoType, mvnoData, callback) {
-    let requestId = UUIDGenerator.generateUUID().toString();
-    this._requestMap[requestId] = callback;
-
-    cpmm.sendAsyncMessage("RIL:MatchMvno", {
-      clientId: clientId,
-      data: {
-        requestId: requestId,
-        mvnoType: mvnoType,
-        mvnoData: mvnoData
-      }
-    });
-  },
-
-  getCardLockEnabled: function(clientId, lockType, callback) {
-    let requestId = UUIDGenerator.generateUUID().toString();
-    this._requestMap[requestId] = callback;
-
-    cpmm.sendAsyncMessage("RIL:GetCardLockEnabled", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        requestId: requestId
-      }
-    });
-  },
-
-  unlockCardLock: function(clientId, lockType, password, newPin, callback) {
-    let requestId = UUIDGenerator.generateUUID().toString();
-    this._requestMap[requestId] = callback;
-
-    cpmm.sendAsyncMessage("RIL:UnlockCardLock", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        password: password,
-        newPin: newPin,
-        requestId: requestId
-      }
-    });
-  },
-
-  setCardLockEnabled: function(clientId, lockType, password, enabled, callback) {
-    let requestId = UUIDGenerator.generateUUID().toString();
-    this._requestMap[requestId] = callback;
-
-    cpmm.sendAsyncMessage("RIL:SetCardLockEnabled", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        password: password,
-        enabled: enabled,
-        requestId: requestId
-      }
-    });
-  },
-
-  changeCardLockPassword: function(clientId, lockType, password, newPassword,
-                                   callback) {
-    let requestId = UUIDGenerator.generateUUID().toString();
-    this._requestMap[requestId] = callback;
-
-    cpmm.sendAsyncMessage("RIL:ChangeCardLockPassword", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        password: password,
-        newPassword: newPassword,
-        requestId: requestId
-      }
-    });
-  },
-
-  getCardLockRetryCount: function(clientId, lockType, callback) {
-    let requestId = UUIDGenerator.generateUUID().toString();
-    this._requestMap[requestId] = callback;
-
-    cpmm.sendAsyncMessage("RIL:GetCardLockRetryCount", {
-      clientId: clientId,
-      data: {
-        lockType: lockType,
-        requestId: requestId
-      }
-    });
-  },
-
-  getServiceStateEnabled: function(clientId, service, callback) {
-    let requestId = UUIDGenerator.generateUUID().toString();
-    this._requestMap[requestId] = callback;
-
-    cpmm.sendAsyncMessage("RIL:GetServiceState", {
-      clientId: clientId,
-      data: {
-        requestId: requestId,
-        service: service
-      }
-    });
-  }
-};
-
-function Icc(aIccProvider, aClientId) {
-  this._iccProvider = aIccProvider;
-  this._clientId = aClientId;
-  this._listeners = [];
-}
-Icc.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIcc]),
-
-  _iccProvider: null,
-  _clientId: -1,
-  _listeners: null,
-
-  deliverListenerEvent: function(aName, aArgs) {
-    let listeners = this._listeners.slice();
-    for (let listener of listeners) {
-      if (this._listeners.indexOf(listener) === -1) {
-        continue;
-      }
-      let handler = listener[aName];
-      if (typeof handler != "function") {
-        throw new Error("No handler for " + aName);
-      }
-      try {
-        handler.apply(listener, aArgs);
-      } catch (e) {
-        if (DEBUG) {
-          debug("listener for " + aName + " threw an exception: " + e);
-        }
-      }
-    }
-  },
-
-  /**
-   * nsIIcc interface.
-   */
-  registerListener: function(aListener) {
-    if (this._listeners.indexOf(aListener) >= 0) {
-      throw Cr.NS_ERROR_UNEXPECTED;
-    }
-
-    this._listeners.push(aListener);
-    cpmm.sendAsyncMessage("RIL:RegisterIccMsg");
-  },
-
-  unregisterListener: function(aListener) {
-    let index = this._listeners.indexOf(aListener);
-    if (index >= 0) {
-      this._listeners.splice(index, 1);
-    }
-  },
-
-  get iccInfo() {
-    return this._iccProvider.getIccInfo(this._clientId);
-  },
-
-  get cardState() {
-    return this._iccProvider.getCardState(this._clientId);
-  },
-
-  getCardLockEnabled: function(aLockType, aCallback) {
-    this._iccProvider.getCardLockEnabled(this._clientId, aLockType, aCallback);
-  },
-
-  unlockCardLock: function(aLockType, aPassword, aNewPin, aCallback) {
-    this._iccProvider.unlockCardLock(this._clientId, aLockType,
-                                     aPassword, aNewPin, aCallback);
-  },
-
-  setCardLockEnabled: function(aLockType, aPassword, aEnabled, aCallback) {
-    this._iccProvider.setCardLockEnabled(this._clientId, aLockType,
-                                         aPassword, aEnabled, aCallback);
-  },
-
-  changeCardLockPassword: function(aLockType, aPassword, aNewPassword, aCallback) {
-    this._iccProvider.changeCardLockPassword(this._clientId, aLockType,
-                                             aPassword, aNewPassword, aCallback);
-  },
-
-  getCardLockRetryCount: function(aLockType, aCallback) {
-    this._iccProvider.getCardLockRetryCount(this._clientId, aLockType, aCallback);
-  },
-
-  matchMvno: function(aMvnoType, aMvnoData, aCallback) {
-    this._iccProvider.matchMvno(this._clientId, aMvnoType, aMvnoData, aCallback);
-  },
-
-  getServiceStateEnabled: function(aService, aCallback) {
-    this._iccProvider.getServiceStateEnabled(this._clientId, aService, aCallback);
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RILContentHelper]);
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -84,34 +84,26 @@ const NETWORK_TYPE_WIFI        = Ci.nsIN
 const NETWORK_TYPE_MOBILE      = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
 const NETWORK_TYPE_MOBILE_MMS  = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS;
 const NETWORK_TYPE_MOBILE_SUPL = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL;
 const NETWORK_TYPE_MOBILE_IMS  = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS;
 const NETWORK_TYPE_MOBILE_DUN  = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN;
 
 // TODO: Bug 815526, deprecate RILContentHelper.
 const RIL_IPC_ICCMANAGER_MSG_NAMES = [
-  "RIL:GetRilContext",
   "RIL:SendStkResponse",
   "RIL:SendStkMenuSelection",
   "RIL:SendStkTimerExpiration",
   "RIL:SendStkEventDownload",
-  "RIL:GetCardLockEnabled",
-  "RIL:UnlockCardLock",
-  "RIL:SetCardLockEnabled",
-  "RIL:ChangeCardLockPassword",
-  "RIL:GetCardLockRetryCount",
   "RIL:IccOpenChannel",
   "RIL:IccExchangeAPDU",
   "RIL:IccCloseChannel",
   "RIL:ReadIccContacts",
   "RIL:UpdateIccContact",
   "RIL:RegisterIccMsg",
-  "RIL:MatchMvno",
-  "RIL:GetServiceState"
 ];
 
 // set to true in ril_consts.js to see debug messages
 var DEBUG = RIL.DEBUG_RIL;
 
 function updateDebugFlag() {
   // Read debug setting from pref
   let debugPref;
@@ -164,17 +156,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/telephony/telephonyservice;1",
                                    "nsIGonkTelephonyService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
                                    "@mozilla.org/mobileconnection/mobileconnectionservice;1",
                                    "nsIGonkMobileConnectionService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gCellBroadcastService",
-                                   "@mozilla.org/cellbroadcast/gonkservice;1",
+                                   "@mozilla.org/cellbroadcast/cellbroadcastservice;1",
                                    "nsIGonkCellBroadcastService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gIccMessenger",
                                    "@mozilla.org/ril/system-messenger-helper;1",
                                    "nsIIccMessenger");
 
 XPCOMUtils.defineLazyGetter(this, "gStkCmdFactory", function() {
   let stk = {};
@@ -876,54 +868,16 @@ try {
         return num;
       }
     } catch (e) {}
 
     return 1;
   })());
 } catch (e) {}
 
-function IccInfo() {}
-IccInfo.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
-
-  // nsIIccInfo
-
-  iccType: null,
-  iccid: null,
-  mcc: null,
-  mnc: null,
-  spn: null,
-  isDisplayNetworkNameRequired: false,
-  isDisplaySpnRequired: false
-};
-
-function GsmIccInfo() {}
-GsmIccInfo.prototype = {
-  __proto__: IccInfo.prototype,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
-                                         Ci.nsIIccInfo]),
-
-  // nsIGsmIccInfo
-
-  msisdn: null
-};
-
-function CdmaIccInfo() {}
-CdmaIccInfo.prototype = {
-  __proto__: IccInfo.prototype,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
-                                         Ci.nsIIccInfo]),
-
-  // nsICdmaIccInfo
-
-  mdn: null,
-  prlVersion: 0
-};
-
 function DataConnectionHandler(clientId, radioInterface) {
   // Initial owning attributes.
   this.clientId = clientId;
   this.radioInterface = radioInterface;
   this.dataCallSettings = {
     oldEnabled: false,
     enabled: false,
     roamingEnabled: false
@@ -1436,17 +1390,16 @@ function RadioInterfaceLayer() {
 RadioInterfaceLayer.prototype = {
 
   classID:   RADIOINTERFACELAYER_CID,
   classInfo: XPCOMUtils.generateCI({classID: RADIOINTERFACELAYER_CID,
                                     classDescription: "RadioInterfaceLayer",
                                     interfaces: [Ci.nsIRadioInterfaceLayer]}),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterfaceLayer,
-                                         Ci.nsIRadioInterfaceLayer_new, // TODO: Bug 815526, deprecate RILContentHelper.
                                          Ci.nsIObserver]),
 
   /**
    * nsIObserver interface methods.
    */
 
   observe: function(subject, topic, data) {
     switch (topic) {
@@ -1684,22 +1637,16 @@ function RadioInterface(aClientId, aWork
   this.workerMessenger = {
     send: aWorkerMessenger.send.bind(aWorkerMessenger, aClientId),
     // TODO: Bug 815526, deprecate RILContentHelper.
     sendWithIPCMessage:
       aWorkerMessenger.sendWithIPCMessage.bind(aWorkerMessenger, aClientId),
   };
   aWorkerMessenger.registerClient(aClientId, this);
 
-  this.rilContext = {
-    cardState:      Ci.nsIIcc.CARD_STATE_UNKNOWN,
-    iccInfo:        null,
-    imsi:           null
-  };
-
   this.operatorInfo = {};
 
   let lock = gSettingsService.createLock();
 
   // Read the "time.clock.automatic-update.enabled" setting to see if
   // we need to adjust the system clock time by NITZ or SNTP.
   lock.get(kSettingsClockAutoUpdateEnabled, this);
 
@@ -1747,86 +1694,35 @@ RadioInterface.prototype = {
 
   shutdown: function() {
     Services.obs.removeObserver(this, kMozSettingsChangedObserverTopic);
     Services.obs.removeObserver(this, kSysClockChangeObserverTopic);
     Services.obs.removeObserver(this, kScreenStateChangedTopic);
     Services.obs.removeObserver(this, kNetworkConnStateChangedTopic);
   },
 
-  /**
-   * A utility function to copy objects. The srcInfo may contain
-   * "rilMessageType", should ignore it.
-   */
-  updateInfo: function(srcInfo, destInfo) {
-    for (let key in srcInfo) {
-      if (key === "rilMessageType") {
-        continue;
-      }
-      destInfo[key] = srcInfo[key];
-    }
-  },
-
-  /**
-   * A utility function to compare objects. The srcInfo may contain
-   * "rilMessageType", should ignore it.
-   */
-  isInfoChanged: function(srcInfo, destInfo) {
-    if (!destInfo) {
-      return true;
-    }
-
-    for (let key in srcInfo) {
-      if (key === "rilMessageType") {
-        continue;
-      }
-      if (srcInfo[key] !== destInfo[key]) {
-        return true;
-      }
-    }
-
-    return false;
+  getIccInfo: function() {
+    let icc = gIccService.getIccByServiceId(this.clientId);
+    return icc ? icc.iccInfo : null;
   },
 
   isCardPresent: function() {
-    let cardState = this.rilContext.cardState;
+    let icc = gIccService.getIccByServiceId(this.clientId);
+    let cardState = icc ? icc.cardState : Ci.nsIIcc.CARD_STATE_UNKNOWN;
     return cardState !== Ci.nsIIcc.CARD_STATE_UNDETECTED &&
       cardState !== Ci.nsIIcc.CARD_STATE_UNKNOWN;
   },
 
   /**
    * Process a message from the content process.
    *
    * TODO: Bug 815526, deprecate RILContentHelper
    */
   receiveMessage: function(msg) {
     switch (msg.name) {
-      case "RIL:GetRilContext":
-        // This message is sync.
-        return this.rilContext;
-      case "RIL:GetCardLockEnabled":
-        this.workerMessenger.sendWithIPCMessage(msg, "iccGetCardLockEnabled",
-                                                "RIL:GetCardLockResult");
-        break;
-      case "RIL:UnlockCardLock":
-        this.workerMessenger.sendWithIPCMessage(msg, "iccUnlockCardLock",
-                                                "RIL:SetUnlockCardLockResult");
-        break;
-      case "RIL:SetCardLockEnabled":
-        this.workerMessenger.sendWithIPCMessage(msg, "iccSetCardLockEnabled",
-                                                "RIL:SetUnlockCardLockResult");
-        break;
-      case "RIL:ChangeCardLockPassword":
-        this.workerMessenger.sendWithIPCMessage(msg, "iccChangeCardLockPassword",
-                                                "RIL:SetUnlockCardLockResult");
-        break;
-      case "RIL:GetCardLockRetryCount":
-        this.workerMessenger.sendWithIPCMessage(msg, "iccGetCardLockRetryCount",
-                                                "RIL:CardLockRetryCount");
-        break;
       case "RIL:SendStkResponse":
         this.workerMessenger.send("sendStkTerminalResponse", msg.json.data);
         break;
       case "RIL:SendStkMenuSelection":
         this.workerMessenger.send("sendStkMenuSelection", msg.json.data);
         break;
       case "RIL:SendStkTimerExpiration":
         this.workerMessenger.send("sendStkTimerExpiration", msg.json.data);
@@ -1844,22 +1740,16 @@ RadioInterface.prototype = {
         this.workerMessenger.sendWithIPCMessage(msg, "iccExchangeAPDU");
         break;
       case "RIL:ReadIccContacts":
         this.workerMessenger.sendWithIPCMessage(msg, "readICCContacts");
         break;
       case "RIL:UpdateIccContact":
         this.workerMessenger.sendWithIPCMessage(msg, "updateICCContact");
         break;
-      case "RIL:MatchMvno":
-        this.matchMvno(msg.target, msg.json.data);
-        break;
-      case "RIL:GetServiceState":
-        this.workerMessenger.sendWithIPCMessage(msg, "getIccServiceState");
-        break;
     }
     return null;
   },
 
   handleUnsolicitedWorkerMessage: function(message) {
     let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
     switch (message.rilMessageType) {
       case "callRing":
@@ -1919,39 +1809,35 @@ RadioInterface.prototype = {
         break;
       case "radiostatechange":
         // gRadioEnabledController should know the radio state for each client,
         // so notify gRadioEnabledController here.
         gRadioEnabledController.notifyRadioStateChanged(this.clientId,
                                                         message.radioState);
         break;
       case "cardstatechange":
-        this.rilContext.cardState = message.cardState;
+        gIccService.notifyCardStateChanged(this.clientId,
+                                           message.cardState);
         gRadioEnabledController.receiveCardState(this.clientId);
-        gIccService.notifyCardStateChanged(this.clientId,
-                                           this.rilContext.cardState);
-        // TODO: Bug 815526, deprecate RILContentHelper.
-        gMessageManager.sendIccMessage("RIL:CardStateChanged",
-                                       this.clientId, message);
         break;
       case "sms-received":
         this.handleSmsReceived(message);
         break;
       case "cellbroadcast-received":
         this.handleCellbroadcastMessageReceived(message);
         break;
       case "nitzTime":
         this.handleNitzTime(message);
         break;
       case "iccinfochange":
-        this.handleIccInfoChange(message);
+        gIccService.notifyIccInfoChanged(this.clientId,
+                                         message.iccid ? message : null);
         break;
       case "iccimsi":
-        this.rilContext.imsi = message.imsi;
-        gIccService.notifyImsiChanged(this.clientId, this.rilContext.imsi);
+        gIccService.notifyImsiChanged(this.clientId, message.imsi);
         break;
       case "iccmbdn":
         this.handleIccMbdn(message);
         break;
       case "iccmwis":
         this.handleIccMwis(message.mwi);
         break;
       case "stkcommand":
@@ -1965,95 +1851,16 @@ RadioInterface.prototype = {
         this.handleCdmaInformationRecords(message.records);
         break;
       default:
         throw new Error("Don't know about this message type: " +
                         message.rilMessageType);
     }
   },
 
-  // Matches the mvnoData pattern with imsi. Characters 'x' and 'X' are skipped
-  // and not compared. E.g., if the mvnoData passed is '310260x10xxxxxx',
-  // then the function returns true only if imsi has the same first 6 digits,
-  // 8th and 9th digit.
-  // TODO: Bug 815526, deprecate RILContentHelper.
-  isImsiMatches: function(mvnoData) {
-    let imsi = this.rilContext.imsi;
-
-    // This should not be an error, but a mismatch.
-    if (mvnoData.length > imsi.length) {
-      return false;
-    }
-
-    for (let i = 0; i < mvnoData.length; i++) {
-      let c = mvnoData[i];
-      if ((c !== 'x') && (c !== 'X') && (c !== imsi[i])) {
-        return false;
-      }
-    }
-    return true;
-  },
-
-  // TODO: Bug 815526, deprecate RILContentHelper.
-  matchMvno: function(target, message) {
-    if (DEBUG) this.debug("matchMvno: " + JSON.stringify(message));
-
-    if (!message || !message.mvnoData) {
-      message.errorMsg = RIL.GECKO_ERROR_INVALID_PARAMETER;
-    }
-
-    if (!message.errorMsg) {
-      switch (message.mvnoType) {
-        case RIL.GECKO_CARDMVNO_TYPE_IMSI:
-          if (!this.rilContext.imsi) {
-            message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE;
-            break;
-          }
-          message.result = this.isImsiMatches(message.mvnoData);
-          break;
-        case RIL.GECKO_CARDMVNO_TYPE_SPN:
-          let spn = this.rilContext.iccInfo && this.rilContext.iccInfo.spn;
-          if (!spn) {
-            message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE;
-            break;
-          }
-          message.result = spn == message.mvnoData;
-          break;
-        case RIL.GECKO_CARDMVNO_TYPE_GID:
-          this.workerMessenger.send("getGID1", null, (function(response) {
-            let gid = response.gid1;
-            let mvnoDataLength = message.mvnoData.length;
-
-            if (!gid) {
-              message.errorMsg = RIL.GECKO_ERROR_GENERIC_FAILURE;
-            } else if (mvnoDataLength > gid.length) {
-              message.result = false;
-            } else {
-              message.result =
-                gid.substring(0, mvnoDataLength).toLowerCase() ==
-                message.mvnoData.toLowerCase();
-            }
-
-            target.sendAsyncMessage("RIL:MatchMvno", {
-              clientId: this.clientId,
-              data: message
-            });
-          }).bind(this));
-          return;
-        default:
-          message.errorMsg = RIL.GECKO_ERROR_MODE_NOT_SUPPORTED;
-      }
-    }
-
-    target.sendAsyncMessage("RIL:MatchMvno", {
-      clientId: this.clientId,
-      data: message
-    });
-  },
-
   setDataRegistration: function(attach) {
     let deferred = Promise.defer();
     this.workerMessenger.send("setDataRegistration",
                               {attach: attach},
                               (function(response) {
       // Always resolve to proceed with the following steps.
       deferred.resolve(response.errorMsg ? response.errorMsg : null);
     }).bind(this));
@@ -2222,81 +2029,22 @@ RadioInterface.prototype = {
   handleIccMwis: function(mwi) {
     let service = Cc["@mozilla.org/voicemail/voicemailservice;1"]
                   .getService(Ci.nsIGonkVoicemailService);
     // Note: returnNumber and returnMessage is not available from UICC.
     service.notifyStatusChanged(this.clientId, mwi.active, mwi.msgCount,
                                 null, null);
   },
 
-  handleIccInfoChange: function(message) {
-    let oldSpn = this.rilContext.iccInfo ? this.rilContext.iccInfo.spn : null;
-
-    // TODO: Bug 815526, deprecate RILContentHelper:
-    //       Move the logic of updating iccInfo to IccService.js.
-    if (!message || !message.iccid) {
-      // If iccInfo is already `null`, don't have to clear it and send
-      // RIL:IccInfoChanged.
-      if (!this.rilContext.iccInfo) {
-        return;
-      }
-
-      // Card is not detected, clear iccInfo to null.
-      this.rilContext.iccInfo = null;
-    } else {
-      if (!this.rilContext.iccInfo) {
-        if (message.iccType === "ruim" || message.iccType === "csim") {
-          this.rilContext.iccInfo = new CdmaIccInfo();
-        } else if (message.iccType === "sim" || message.iccType === "usim") {
-          this.rilContext.iccInfo = new GsmIccInfo();
-        } else {
-          this.rilContext.iccInfo = new IccInfo();
-        }
-      }
-
-      if (!this.isInfoChanged(message, this.rilContext.iccInfo)) {
-        return;
-      }
-
-      this.updateInfo(message, this.rilContext.iccInfo);
-    }
-
-    // RIL:IccInfoChanged corresponds to a DOM event that gets fired only
-    // when iccInfo has changed.
-    // TODO: Bug 815526, deprecate RILContentHelper.
-    gMessageManager.sendIccMessage("RIL:IccInfoChanged",
-                                   this.clientId,
-                                   message.iccid ? message : null);
-    gIccService.notifyIccInfoChanged(this.clientId,
-                                     message.iccid ? message : null);
-
-    // Update lastKnownHomeNetwork.
-    if (message.mcc && message.mnc) {
-      let lastKnownHomeNetwork = message.mcc + "-" + message.mnc;
-      // Append spn information if available.
-      if (message.spn) {
-        lastKnownHomeNetwork += "-" + message.spn;
-      }
-
-      gMobileConnectionService.notifyLastHomeNetworkChanged(this.clientId,
-                                                            lastKnownHomeNetwork);
-    }
-
-    // If spn becomes available, we should check roaming again.
-    if (!oldSpn && message.spn) {
-      gMobileConnectionService.notifySpnAvailable(this.clientId);
-    }
-  },
-
   handleStkProactiveCommand: function(message) {
     if (DEBUG) this.debug("handleStkProactiveCommand " + JSON.stringify(message));
-    let iccId = this.rilContext.iccInfo && this.rilContext.iccInfo.iccid;
-    if (iccId) {
+    let iccInfo = this.getIccInfo();
+    if (iccInfo && iccInfo.iccid) {
       gIccMessenger
-        .notifyStkProactiveCommand(iccId,
+        .notifyStkProactiveCommand(iccInfo.iccid,
                                    gStkCmdFactory.createCommand(message));
     }
     // TODO: Bug 815526, deprecate RILContentHelper.
     gMessageManager.sendIccMessage("RIL:StkCommand", this.clientId, message);
   },
 
   _convertCbGsmGeographicalScope: function(aGeographicalScope) {
     return (aGeographicalScope != null)
@@ -2584,18 +2332,16 @@ RadioInterface.prototype = {
   handleError: function(aErrorMessage) {
     if (DEBUG) {
       this.debug("There was an error while reading RIL settings.");
     }
   },
 
   // nsIRadioInterface
 
-  rilContext: null,
-
   // TODO: Bug 928861 - B2G NetworkManager: Provide a more generic function
   //                    for connecting
   setupDataCallByType: function(networkType) {
     let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
     connHandler.setupDataCallByType(networkType);
   },
 
   // TODO: Bug 928861 - B2G NetworkManager: Provide a more generic function
@@ -3221,18 +2967,18 @@ RILNetworkInterface.prototype = {
    * nsIRilNetworkInterface Implementation
    */
 
   get serviceId() {
     return this.dataConnectionHandler.clientId;
   },
 
   get iccId() {
-    let iccInfo = this.dataConnectionHandler.radioInterface.rilContext.iccInfo;
-    return iccInfo && iccInfo.iccid;
+    let iccInfo = this.dataConnectionHandler.radioInterface.getIccInfo();
+    return iccInfo ? iccInfo.iccid : null;
   },
 
   get mmsc() {
     if (this.type != NETWORK_TYPE_MOBILE_MMS) {
       if (DEBUG) this.debug("Error! Only MMS network can get MMSC.");
       throw Cr.NS_ERROR_UNEXPECTED;
     }
 
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -16,40 +16,25 @@ interface nsIRilNetworkInterface : nsINe
   readonly attribute DOMString iccId;
 
   /* The following attributes are for MMS proxy settings. */
   readonly attribute DOMString mmsc;     // Empty string if not set.
   readonly attribute DOMString mmsProxy; // Empty string if not set.
   readonly attribute long      mmsPort;  // -1 if not set.
 };
 
-[scriptable, uuid(4441e660-4ad0-11e4-916c-0800200c9a66)]
-interface nsIRilContext : nsISupports
-{
-  /**
-   * One of the nsIIcc.CARD_STATE_* values.
-   */
-  readonly attribute unsigned long cardState;
-
-  readonly attribute DOMString imsi;
-
-  readonly attribute nsIIccInfo iccInfo;
-};
-
 [scriptable, function, uuid(3bc96351-53b0-47a1-a888-c74c64b60f25)]
 interface nsIRilSendWorkerMessageCallback : nsISupports
 {
   boolean handleResponse(in jsval response);
 };
 
-[scriptable, uuid(fe01c648-867a-11e4-915f-033b36e8177b)]
+[scriptable, uuid(1a3ef88a-e4d1-11e4-8512-176220f2b32b)]
 interface nsIRadioInterface : nsISupports
 {
-  readonly attribute nsIRilContext rilContext;
-
   /**
    * PDP APIs
    *
    * @param networkType
    *        Mobile network type, that is, nsINetworkInterface.NETWORK_TYPE_MOBILE
    *        or one of the nsINetworkInterface.NETWORK_TYPE_MOBILE_* values.
    */
   void setupDataCallByType(in long networkType);
@@ -74,18 +59,8 @@ interface nsIRadioInterfaceLayer : nsISu
    * Select a proper client for dialing emergency call.
    *
    * @return clientId or -1 if none of the clients are avaialble.
    */
   unsigned long getClientIdForEmergencyCall();
 
   void setMicrophoneMuted(in boolean muted);
 };
-
-
-/**
- * Helper Interface to define new APIs of nsIRadioInterfaceLayer during
- * ril-interfaces frozen phase.
- */
-[scriptable, uuid(f8ec63da-c22e-11e4-89f3-b767dae42a13)]
-interface nsIRadioInterfaceLayer_new : nsIRadioInterfaceLayer
-{
-};
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -56,17 +56,17 @@ var ecmaGlobals =
     // NB: We haven't bothered to resolve constants like Infinity and NaN on
     // Xrayed windows (which are seen from the XBL scope). We could support
     // this if needed with some refactoring.
     {name: "Infinity", xbl: false},
     "Int16Array",
     "Int32Array",
     "Int8Array",
     "InternalError",
-    {name: "Intl", desktop: true},
+    {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     {name: "NaN", xbl: false},
     "Number",
     "Object",
     "Proxy",
@@ -1386,16 +1386,17 @@ var interfaceNamesInGlobalScope =
 
 function createInterfaceMap(isXBLScope) {
   var prefs = SpecialPowers.Services.prefs;
   var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version;
   var isNightly = version.endsWith("a1");
   var isRelease = !version.contains("a");
   var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
   var isB2G = !isDesktop && !navigator.userAgent.contains("Android");
+  var isAndroid = navigator.userAgent.contains("Android");
   var hasPermission = function (aPermissions) {
     var result = false;
     for (var p of aPermissions) {
       result = result || SpecialPowers.hasPermission(p, window.document);
     }
     return result;
   };
 
@@ -1405,16 +1406,17 @@ function createInterfaceMap(isXBLScope) 
   {
     for (var entry of interfaces) {
       if (typeof(entry) === "string") {
         interfaceMap[entry] = true;
       } else if ((entry.nightly === !isNightly) ||
                  (entry.xbl === !isXBLScope) ||
                  (entry.desktop === !isDesktop) ||
                  (entry.b2g === !isB2G) ||
+                 (entry.android === !isAndroid) ||
                  (entry.release === !isRelease) ||
                  (entry.pref && !prefs.getBoolPref(entry.pref))  ||
                  (entry.permission && !hasPermission(entry.permission))) {
         interfaceMap[entry.name] = false;
       } else {
         interfaceMap[entry.name] = true;
       }
     }
--- a/dom/wappush/gonk/WapPushManager.js
+++ b/dom/wappush/gonk/WapPushManager.js
@@ -32,19 +32,19 @@ XPCOMUtils.defineLazyGetter(this, "CP", 
   let CP = {};
   Cu.import("resource://gre/modules/CpPduHelper.jsm", CP);
   return CP;
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger",
                                    "@mozilla.org/system-message-internal;1",
                                    "nsISystemMessagesInternal");
-XPCOMUtils.defineLazyServiceGetter(this, "gRIL",
-                                   "@mozilla.org/ril;1",
-                                   "nsIRadioInterfaceLayer");
+XPCOMUtils.defineLazyServiceGetter(this, "gIccService",
+                                   "@mozilla.org/icc/iccservice;1",
+                                   "nsIIccService");
 
 /**
  * Helpers for WAP PDU processing.
  */
 this.WapPushManager = {
 
   /**
    * Parse raw PDU data and deliver to a proper target.
@@ -99,17 +99,18 @@ this.WapPushManager = {
                contentType === "application/vnd.wap.connectivity-wbxml") {
       // Apply HMAC authentication on WBXML encoded CP message.
       if (contentType === "application/vnd.wap.connectivity-wbxml") {
         let params = options.headers["content-type"].params;
         let sec = params && params.sec;
         let mac = params && params.mac;
         authInfo = CP.Authenticator.check(data.array.subarray(data.offset),
                                           sec, mac, function getNetworkPin() {
-          let imsi = gRIL.getRadioInterface(options.serviceId).rilContext.imsi;
+          let icc = gIccService.getIccByServiceId(options.serviceId);
+          let imsi = icc ? icc.imsi : null;
           return CP.Authenticator.formatImsi(imsi);
         });
       }
 
       msg = CP.PduHelper.parse(data, contentType);
     } else {
       // Unsupported type, provide raw data.
       msg = {
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -413,31 +413,57 @@ CompareNetwork::OnStreamComplete(nsIStre
   nsCOMPtr<nsIRequest> request;
   nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mManager->NetworkFinished(rv);
     return NS_OK;
   }
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
-  if (!httpChannel) {
-    mManager->NetworkFinished(NS_ERROR_FAILURE);
-    return NS_OK;
-  }
+  if (httpChannel) {
+    bool requestSucceeded;
+    rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mManager->NetworkFinished(rv);
+      return NS_OK;
+    }
 
-  bool requestSucceeded;
-  rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    mManager->NetworkFinished(rv);
-    return NS_OK;
+    if (!requestSucceeded) {
+      mManager->NetworkFinished(NS_ERROR_FAILURE);
+      return NS_OK;
+    }
   }
+  else {
+    // The only supported request schemes are http, https, and app.
+    // Above, we check to ensure that the request is http or https
+    // based on the channel qi.  Here we test the scheme to ensure
+    // that it is app.  Otherwise, bail.
+    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
+    if (NS_WARN_IF(!channel)) {
+      mManager->NetworkFinished(NS_ERROR_FAILURE);
+      return NS_OK;
+    }
+    nsCOMPtr<nsIURI> uri;
+    rv = channel->GetURI(getter_AddRefs(uri));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mManager->NetworkFinished(rv);
+      return NS_OK;
+    }
 
-  if (!requestSucceeded) {
-    mManager->NetworkFinished(NS_ERROR_FAILURE);
-    return NS_OK;
+    nsAutoCString scheme;
+    rv = uri->GetScheme(scheme);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mManager->NetworkFinished(rv);
+      return NS_OK;
+    }
+
+    if (!scheme.LowerCaseEqualsLiteral("app")) {
+      mManager->NetworkFinished(NS_ERROR_FAILURE);
+      return NS_OK;      
+    }
   }
 
   // FIXME(nsm): "Extract mime type..."
 
   char16_t* buffer = nullptr;
   size_t len = 0;
 
   rv = nsScriptLoader::ConvertToUTF16(httpChannel, aString, aLen,
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -30,17 +30,17 @@ var ecmaGlobals =
     "Float32Array",
     "Float64Array",
     "Function",
     "Infinity",
     "Int16Array",
     "Int32Array",
     "Int8Array",
     "InternalError",
-    {name: "Intl", b2g: false, android: false},
+    {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     "NaN",
     "Number",
     "Object",
     "Proxy",
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -30,17 +30,17 @@ var ecmaGlobals =
     "Float32Array",
     "Float64Array",
     "Function",
     "Infinity",
     "Int16Array",
     "Int32Array",
     "Int8Array",
     "InternalError",
-    {name: "Intl", b2g: false, android: false},
+    {name: "Intl", android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     "NaN",
     "Number",
     "Object",
     "Proxy",
--- a/ipc/bluetooth/BluetoothDaemonConnection.cpp
+++ b/ipc/bluetooth/BluetoothDaemonConnection.cpp
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothDaemonConnection.h"
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/un.h>
 #include <unistd.h>
+#include "mozilla/ipc/DataSocket.h"
 #include "mozilla/ipc/UnixSocketWatcher.h"
 #include "nsTArray.h"
 #include "nsXULAppAPI.h"
 
 #ifdef CHROMIUM_LOG
 #undef CHROMIUM_LOG
 #endif
 
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -1833,22 +1833,25 @@ class _GenerateProtocolCode(ipdl.ast.Vis
                          init=ExprVar('mozilla::ipc::Trigger::Send')))
         if userecv:
             transitionfunc.addstmt(
                 StmtDecl(Decl(Type('int32_t', const=1), recvvar.name),
                          init=ExprVar('mozilla::ipc::Trigger::Recv')))
         if usesend or userecv:
             transitionfunc.addstmt(Whitespace.NL)
 
-        transitionfunc.addstmts([
-            fromswitch,
-            # all --> Error transitions break to here
-            StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())),
-            StmtReturn(ExprLiteral.FALSE)
-        ])
+        transitionfunc.addstmt(fromswitch)
+        # all --> Error transitions break to here.  But only insert this
+        # block if there is any possibility of such transitions.
+        if self.protocol.transitionStmts:
+            transitionfunc.addstmts([
+                StmtExpr(ExprAssn(ExprDeref(nextvar), _errorState())),
+                StmtReturn(ExprLiteral.FALSE),
+            ])
+
         return transitionfunc
 
 ##--------------------------------------------------
 
 def _generateMessageClass(clsname, msgid, priority, prettyName, compress):
     cls = Class(name=clsname, inherits=[ Inherit(Type('IPC::Message')) ])
     cls.addstmt(Label.PUBLIC)
 
new file mode 100644
--- /dev/null
+++ b/ipc/unixsocket/DataSocket.cpp
@@ -0,0 +1,48 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "mozilla/ipc/DataSocket.h"
+
+namespace mozilla {
+namespace ipc {
+
+//
+// DataSocketIO
+//
+
+DataSocketIO::~DataSocketIO()
+{ }
+
+void
+DataSocketIO::EnqueueData(UnixSocketIOBuffer* aBuffer)
+{
+  if (!aBuffer->GetSize()) {
+    delete aBuffer; // delete empty data immediately
+    return;
+  }
+  mOutgoingQ.AppendElement(aBuffer);
+}
+
+bool
+DataSocketIO::HasPendingData() const
+{
+  return !mOutgoingQ.IsEmpty();
+}
+
+DataSocketIO::DataSocketIO()
+{ }
+
+//
+// DataSocket
+//
+
+DataSocket::~DataSocket()
+{ }
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/ipc/unixsocket/DataSocket.h
@@ -0,0 +1,204 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef mozilla_ipc_datasocket_h
+#define mozilla_ipc_datasocket_h
+
+#include "mozilla/ipc/SocketBase.h"
+
+namespace mozilla {
+namespace ipc {
+
+//
+// DataSocket
+//
+
+/**
+ * |DataSocket| represents a socket that can send or receive data. This
+ * can be a stream-based socket, a datagram-based socket, or any other
+ * socket that transfers data.
+ */
+class DataSocket : public SocketBase
+{
+public:
+  virtual ~DataSocket();
+
+  /**
+   * Queue data to be sent to the socket on the IO thread. Can only be called on
+   * originating thread.
+   *
+   * @param aBuffer Data to be sent to socket
+   */
+  virtual void SendSocketData(UnixSocketIOBuffer* aBuffer) = 0;
+};
+
+//
+// DataSocketIO
+//
+
+/**
+ * |DataSocketIO| is a base class for Socket I/O classes that
+ * transfer data on the I/O thread. It provides methods for the
+ * most common read and write scenarios.
+ */
+class DataSocketIO : public SocketIOBase
+{
+public:
+  virtual ~DataSocketIO();
+
+  /**
+   * Allocates a buffer for receiving data from the socket. The method
+   * shall return the buffer in the arguments. The buffer is owned by the
+   * I/O class. |DataSocketIO| will never ask for more than one buffer
+   * at a time, so I/O classes can handout the same buffer on each invokation
+   * of this method. I/O-thread only.
+   *
+   *  @param[out] aBuffer returns a pointer to the I/O buffer
+   *  @return NS_OK on success, or an error code otherwise
+   */
+  virtual nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer) = 0;
+
+  /**
+   * Marks the current socket buffer to by consumed by the I/O class. The
+   * class is resonsible for releasing the buffer afterwards. I/O-thread
+   * only.
+   *
+   *  @param aIndex the socket's index
+   *  @param[out] aBuffer the receive buffer
+   *  @param[out] aSize the receive buffer's size
+   */
+  virtual void ConsumeBuffer() = 0;
+
+  /**
+   * Marks the current socket buffer to be discarded. The I/O class is
+   * resonsible for releasing the buffer's memory. I/O-thread only.
+   *
+   *  @param aIndex the socket's index
+   */
+  virtual void DiscardBuffer() = 0;
+
+  void EnqueueData(UnixSocketIOBuffer* aBuffer);
+  bool HasPendingData() const;
+
+  template <typename T>
+  ssize_t ReceiveData(int aFd, T* aIO)
+  {
+    MOZ_ASSERT(aFd >= 0);
+    MOZ_ASSERT(aIO);
+
+    UnixSocketIOBuffer* incoming;
+    nsresult rv = QueryReceiveBuffer(&incoming);
+    if (NS_FAILED(rv)) {
+      /* an error occured */
+      nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
+      NS_DispatchToMainThread(r);
+      return -1;
+    }
+
+    ssize_t res = incoming->Receive(aFd);
+    if (res < 0) {
+      /* an I/O error occured */
+      DiscardBuffer();
+      nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
+      NS_DispatchToMainThread(r);
+      return -1;
+    } else if (!res) {
+      /* EOF or peer shut down sending */
+      DiscardBuffer();
+      nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
+      NS_DispatchToMainThread(r);
+      return 0;
+    }
+
+#ifdef MOZ_TASK_TRACER
+    // Make unix socket creation events to be the source events of TaskTracer,
+    // and originate the rest correlation tasks from here.
+    AutoSourceEvent taskTracerEvent(SourceEventType::Unixsocket);
+#endif
+
+    ConsumeBuffer();
+
+    return res;
+  }
+
+  template <typename T>
+  nsresult SendPendingData(int aFd, T* aIO)
+  {
+    MOZ_ASSERT(aFd >= 0);
+    MOZ_ASSERT(aIO);
+
+    while (HasPendingData()) {
+      UnixSocketIOBuffer* outgoing = mOutgoingQ.ElementAt(0);
+
+      ssize_t res = outgoing->Send(aFd);
+      if (res < 0) {
+        /* an I/O error occured */
+        nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
+        NS_DispatchToMainThread(r);
+        return NS_ERROR_FAILURE;
+      } else if (!res && outgoing->GetSize()) {
+        /* I/O is currently blocked; try again later */
+        return NS_OK;
+      }
+      if (!outgoing->GetSize()) {
+        mOutgoingQ.RemoveElementAt(0);
+        delete outgoing;
+      }
+    }
+
+    return NS_OK;
+  }
+
+protected:
+  DataSocketIO();
+
+private:
+  /**
+   * Raw data queue. Must be pushed/popped from I/O thread only.
+   */
+  nsTArray<UnixSocketIOBuffer*> mOutgoingQ;
+};
+
+//
+// Tasks
+//
+
+/* |SocketIOSendTask| transfers an instance of |Tdata|, such as
+ * |UnixSocketRawData|, to the I/O thread and queues it up for
+ * sending the contained data.
+ */
+template<typename Tio, typename Tdata>
+class SocketIOSendTask final : public SocketIOTask<Tio>
+{
+public:
+  SocketIOSendTask(Tio* aIO, Tdata* aData)
+  : SocketIOTask<Tio>(aIO)
+  , mData(aData)
+  {
+    MOZ_ASSERT(aData);
+  }
+
+  void Run() override
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(!SocketIOTask<Tio>::IsCanceled());
+
+    Tio* io = SocketIOTask<Tio>::GetIO();
+    MOZ_ASSERT(!io->IsShutdownOnIOThread());
+
+    io->Send(mData);
+  }
+
+private:
+  Tdata* mData;
+};
+
+}
+}
+
+#endif
--- a/ipc/unixsocket/ListenSocket.cpp
+++ b/ipc/unixsocket/ListenSocket.cpp
@@ -2,22 +2,21 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ListenSocket.h"
 #include <fcntl.h>
 #include "ConnectionOrientedSocket.h"
+#include "DataSocket.h"
 #include "mozilla/RefPtr.h"
 #include "nsXULAppAPI.h"
 #include "UnixSocketConnector.h"
 
-static const size_t MAX_READ_SIZE = 1; /* any small constant */
-
 namespace mozilla {
 namespace ipc {
 
 //
 // ListenSocketIO
 //
 
 class ListenSocketIO final : public UnixSocketWatcher
@@ -27,19 +26,19 @@ public:
   class ListenTask;
 
   ListenSocketIO(MessageLoop* mIOLoop,
                  ListenSocket* aListenSocket,
                  UnixSocketConnector* aConnector,
                  const nsACString& aAddress);
   ~ListenSocketIO();
 
-  void                GetSocketAddr(nsAString& aAddrStr) const;
-  SocketConsumerBase* GetConsumer();
-  SocketBase*         GetSocketBase();
+  void        GetSocketAddr(nsAString& aAddrStr) const;
+  DataSocket* GetDataSocket();
+  SocketBase* GetSocketBase();
 
   // Shutdown state
   //
 
   bool IsShutdownOnMainThread() const;
   void ShutdownOnMainThread();
 
   bool IsShutdownOnIOThread() const;
@@ -102,23 +101,23 @@ private:
 
   ConnectionOrientedSocketIO* mCOSocketIO;
 };
 
 ListenSocketIO::ListenSocketIO(MessageLoop* mIOLoop,
                                ListenSocket* aListenSocket,
                                UnixSocketConnector* aConnector,
                                const nsACString& aAddress)
-: UnixSocketWatcher(mIOLoop)
-, SocketIOBase(MAX_READ_SIZE)
-, mListenSocket(aListenSocket)
-, mConnector(aConnector)
-, mShuttingDownOnIOThread(false)
-, mAddress(aAddress)
-, mCOSocketIO(nullptr)
+  : UnixSocketWatcher(mIOLoop)
+  , SocketIOBase()
+  , mListenSocket(aListenSocket)
+  , mConnector(aConnector)
+  , mShuttingDownOnIOThread(false)
+  , mAddress(aAddress)
+  , mCOSocketIO(nullptr)
 {
   MOZ_ASSERT(mListenSocket);
   MOZ_ASSERT(mConnector);
 }
 
 ListenSocketIO::~ListenSocketIO()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -131,16 +130,24 @@ ListenSocketIO::GetSocketAddr(nsAString&
   if (!mConnector) {
     NS_WARNING("No connector to get socket address from!");
     aAddrStr.Truncate();
     return;
   }
   mConnector->GetSocketAddr(mAddr, aAddrStr);
 }
 
+DataSocket*
+ListenSocketIO::GetDataSocket()
+{
+  MOZ_CRASH("Listen sockets cannot transfer data");
+
+  return nullptr;
+}
+
 SocketBase*
 ListenSocketIO::GetSocketBase()
 {
   return mListenSocket.get();
 }
 
 bool
 ListenSocketIO::IsShutdownOnMainThread() const
--- a/ipc/unixsocket/SocketBase.cpp
+++ b/ipc/unixsocket/SocketBase.cpp
@@ -264,45 +264,19 @@ SocketBase::~SocketBase()
 
 void
 SocketBase::SetConnectionStatus(SocketConnectionStatus aConnectionStatus)
 {
   mConnectionStatus = aConnectionStatus;
 }
 
 //
-// SocketConsumerBase
+// SocketIOBase
 //
 
-SocketConsumerBase::~SocketConsumerBase()
+SocketIOBase::SocketIOBase()
 { }
 
-//
-// SocketIOBase
-//
-
 SocketIOBase::~SocketIOBase()
 { }
 
-void
-SocketIOBase::EnqueueData(UnixSocketIOBuffer* aBuffer)
-{
-  if (!aBuffer->GetSize()) {
-    delete aBuffer; // delete empty data immediately
-    return;
-  }
-  mOutgoingQ.AppendElement(aBuffer);
-}
-
-bool
-SocketIOBase::HasPendingData() const
-{
-  return !mOutgoingQ.IsEmpty();
-}
-
-SocketIOBase::SocketIOBase(size_t aMaxReadSize)
-  : mMaxReadSize(aMaxReadSize)
-{
-  MOZ_ASSERT(mMaxReadSize);
-}
-
 }
 }
--- a/ipc/unixsocket/SocketBase.h
+++ b/ipc/unixsocket/SocketBase.h
@@ -306,42 +306,16 @@ private:
   uint32_t CalculateConnectDelayMs() const;
 
   SocketConnectionStatus mConnectionStatus;
   PRIntervalTime mConnectTimestamp;
   uint32_t mConnectDelayMs;
 };
 
 //
-// SocketConsumerBase
-//
-
-class SocketConsumerBase : public SocketBase
-{
-public:
-  virtual ~SocketConsumerBase();
-
-  /**
-   * Function to be called whenever data is received. This is only called on the
-   * main thread.
-   *
-   * @param aBuffer Data received from the socket.
-   */
-  virtual void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer) = 0;
-
-  /**
-   * Queue data to be sent to the socket on the IO thread. Can only be called on
-   * originating thread.
-   *
-   * @param aBuffer Data to be sent to socket
-   */
-  virtual void SendSocketData(UnixSocketIOBuffer* aBuffer) = 0;
-};
-
-//
 // Socket I/O runnables
 //
 
 /* |SocketIORunnable| is a runnable for sending a message from
  * the I/O thread to the main thread.
  */
 template <typename T>
 class SocketIORunnable : public nsRunnable
@@ -410,53 +384,16 @@ public:
 
     return NS_OK;
   }
 
 private:
   SocketEvent mEvent;
 };
 
-/* |SocketReceiveRunnable| transfers data received on the I/O thread
- * to the consumer on the main thread.
- */
-template <typename T>
-class SocketIOReceiveRunnable final : public SocketIORunnable<T>
-{
-public:
-  SocketIOReceiveRunnable(T* aIO, UnixSocketBuffer* aBuffer)
-    : SocketIORunnable<T>(aIO)
-    , mBuffer(aBuffer)
-  { }
-
-  NS_IMETHOD Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    T* io = SocketIORunnable<T>::GetIO();
-
-    if (io->IsShutdownOnMainThread()) {
-      NS_WARNING("mConsumer is null, aborting receive!");
-      // Since we've already explicitly closed and the close happened before
-      // this, this isn't really an error. Since we've warned, return OK.
-      return NS_OK;
-    }
-
-    SocketConsumerBase* consumer = io->GetConsumer();
-    MOZ_ASSERT(consumer);
-
-    consumer->ReceiveSocketData(mBuffer);
-
-    return NS_OK;
-  }
-
-private:
-  nsAutoPtr<UnixSocketBuffer> mBuffer;
-};
-
 template <typename T>
 class SocketIORequestClosingRunnable final : public SocketIORunnable<T>
 {
 public:
   SocketIORequestClosingRunnable(T* aImpl)
   : SocketIORunnable<T>(aImpl)
   { }
 
@@ -502,101 +439,27 @@ public:
 private:
   nsAutoPtr<T> mInstance;
 };
 
 //
 // SocketIOBase
 //
 
-/* |SocketIOBase| is a base class for Socket I/O classes that
- * perform operations on the I/O thread. It provides methods
- * for the most common read and write scenarios.
+/**
+ * |SocketIOBase| is a base class for Socket I/O classes that
+ * perform operations on the I/O thread.
  */
 class SocketIOBase
 {
 public:
   virtual ~SocketIOBase();
 
-  void EnqueueData(UnixSocketIOBuffer* aBuffer);
-  bool HasPendingData() const;
-
-  template <typename T>
-  ssize_t ReceiveData(int aFd, T* aIO)
-  {
-    MOZ_ASSERT(aFd >= 0);
-    MOZ_ASSERT(aIO);
-
-    nsAutoPtr<UnixSocketRawData> incoming(
-      new UnixSocketRawData(mMaxReadSize));
-
-    ssize_t res = incoming->Receive(aFd);
-    if (res < 0) {
-      /* an I/O error occured */
-      nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
-      NS_DispatchToMainThread(r);
-      return -1;
-    } else if (!res) {
-      /* EOF or peer shut down sending */
-      nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
-      NS_DispatchToMainThread(r);
-      return 0;
-    }
-
-#ifdef MOZ_TASK_TRACER
-    // Make unix socket creation events to be the source events of TaskTracer,
-    // and originate the rest correlation tasks from here.
-    AutoSourceEvent taskTracerEvent(SourceEventType::Unixsocket);
-#endif
-
-    nsRefPtr<nsRunnable> r =
-      new SocketIOReceiveRunnable<T>(aIO, incoming.forget());
-    NS_DispatchToMainThread(r);
-
-    return res;
-  }
-
-  template <typename T>
-  nsresult SendPendingData(int aFd, T* aIO)
-  {
-    MOZ_ASSERT(aFd >= 0);
-    MOZ_ASSERT(aIO);
-
-    while (HasPendingData()) {
-      UnixSocketIOBuffer* outgoing = mOutgoingQ.ElementAt(0);
-
-      ssize_t res = outgoing->Send(aFd);
-      if (res < 0) {
-        /* an I/O error occured */
-        nsRefPtr<nsRunnable> r = new SocketIORequestClosingRunnable<T>(aIO);
-        NS_DispatchToMainThread(r);
-        return NS_ERROR_FAILURE;
-      } else if (!res && outgoing->GetSize()) {
-        /* I/O is currently blocked; try again later */
-        return NS_OK;
-      }
-      if (!outgoing->GetSize()) {
-        mOutgoingQ.RemoveElementAt(0);
-        delete outgoing;
-      }
-    }
-
-    return NS_OK;
-  }
-
 protected:
-  SocketIOBase(size_t aMaxReadSize);
-
-private:
-  const size_t mMaxReadSize;
-
-  /**
-   * Raw data queue. Must be pushed/popped from I/O thread only.
-   */
-  nsTArray<UnixSocketIOBuffer*> mOutgoingQ;
+  SocketIOBase();
 };
 
 //
 // Socket I/O tasks
 //
 
 /* |SocketIOTask| holds a reference to a Socket I/O object. It's
  * supposed to run on the I/O thread.
@@ -629,46 +492,16 @@ protected:
   {
     MOZ_ASSERT(mIO);
   }
 
 private:
   Tio* mIO;
 };
 
-/* |SocketIOSendTask| transfers an instance of |Tdata|, such as
- * |UnixSocketRawData|, to the I/O thread and queues it up for
- * sending the contained data.
- */
-template<typename Tio, typename Tdata>
-class SocketIOSendTask final : public SocketIOTask<Tio>
-{
-public:
-  SocketIOSendTask(Tio* aIO, Tdata* aData)
-  : SocketIOTask<Tio>(aIO)
-  , mData(aData)
-  {
-    MOZ_ASSERT(aData);
-  }
-
-  void Run() override
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(!SocketIOTask<Tio>::IsCanceled());
-
-    Tio* io = SocketIOTask<Tio>::GetIO();
-    MOZ_ASSERT(!io->IsShutdownOnIOThread());
-
-    io->Send(mData);
-  }
-
-private:
-  Tdata* mData;
-};
-
 /* |SocketIOShutdownTask| signals shutdown to the Socket I/O object on
  * the I/O thread and sends it to the main thread for destruction.
  */
 template<typename Tio>
 class SocketIOShutdownTask final : public SocketIOTask<Tio>
 {
 public:
   SocketIOShutdownTask(Tio* aIO)
--- a/ipc/unixsocket/StreamSocket.cpp
+++ b/ipc/unixsocket/StreamSocket.cpp
@@ -14,38 +14,42 @@ static const size_t MAX_READ_SIZE = 1 <<
 
 namespace mozilla {
 namespace ipc {
 
 //
 // StreamSocketIO
 //
 
-class StreamSocketIO final : public UnixSocketWatcher
-                           , protected SocketIOBase
-                           , public ConnectionOrientedSocketIO
+class StreamSocketIO final
+  : public UnixSocketWatcher
+  , protected DataSocketIO
+  , public ConnectionOrientedSocketIO
 {
 public:
   class ConnectTask;
   class DelayedConnectTask;
+  class ReceiveRunnable;
 
   StreamSocketIO(MessageLoop* mIOLoop,
                  StreamSocket* aStreamSocket,
                  UnixSocketConnector* aConnector,
                  const nsACString& aAddress);
   StreamSocketIO(MessageLoop* mIOLoop, int aFd,
                  ConnectionStatus aConnectionStatus,
                  StreamSocket* aStreamSocket,
                  UnixSocketConnector* aConnector,
                  const nsACString& aAddress);
   ~StreamSocketIO();
 
-  void                GetSocketAddr(nsAString& aAddrStr) const;
-  SocketConsumerBase* GetConsumer();
-  SocketBase*         GetSocketBase();
+  void GetSocketAddr(nsAString& aAddrStr) const;
+
+  StreamSocket* GetStreamSocket();
+  DataSocket* GetDataSocket();
+  SocketBase* GetSocketBase();
 
   // StreamSocketIOBase
   //
 
   nsresult Accept(int aFd,
                   const union sockaddr_any* aAddr, socklen_t aAddrLen);
 
   // Shutdown state
@@ -80,16 +84,23 @@ public:
   void OnAccepted(int aFd, const sockaddr_any* aAddr,
                   socklen_t aAddrLen) override;
   void OnConnected() override;
   void OnError(const char* aFunction, int aErrno) override;
   void OnListening() override;
   void OnSocketCanReceiveWithoutBlocking() override;
   void OnSocketCanSendWithoutBlocking() override;
 
+  // Methods for |DataSocket|
+  //
+
+  nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer);
+  void ConsumeBuffer();
+  void DiscardBuffer();
+
 private:
   void FireSocketError();
 
   // Set up flags on file descriptor.
   static bool SetSocketFlags(int aFd);
 
   /**
    * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
@@ -122,46 +133,49 @@ private:
    * Address struct of the socket currently in use
    */
   sockaddr_any mAddr;
 
   /**
    * Task member for delayed connect task. Should only be access on main thread.
    */
   CancelableTask* mDelayedConnectTask;
+
+  /**
+   * I/O buffer for received data
+   */
+  nsAutoPtr<UnixSocketRawData> mBuffer;
 };
 
 StreamSocketIO::StreamSocketIO(MessageLoop* mIOLoop,
                                StreamSocket* aStreamSocket,
                                UnixSocketConnector* aConnector,
                                const nsACString& aAddress)
-: UnixSocketWatcher(mIOLoop)
-, SocketIOBase(MAX_READ_SIZE)
-, mStreamSocket(aStreamSocket)
-, mConnector(aConnector)
-, mShuttingDownOnIOThread(false)
-, mAddress(aAddress)
-, mDelayedConnectTask(nullptr)
+  : UnixSocketWatcher(mIOLoop)
+  , mStreamSocket(aStreamSocket)
+  , mConnector(aConnector)
+  , mShuttingDownOnIOThread(false)
+  , mAddress(aAddress)
+  , mDelayedConnectTask(nullptr)
 {
   MOZ_ASSERT(mStreamSocket);
   MOZ_ASSERT(mConnector);
 }
 
 StreamSocketIO::StreamSocketIO(MessageLoop* mIOLoop, int aFd,
                                ConnectionStatus aConnectionStatus,
                                StreamSocket* aStreamSocket,
                                UnixSocketConnector* aConnector,
                                const nsACString& aAddress)
-: UnixSocketWatcher(mIOLoop, aFd, aConnectionStatus)
-, SocketIOBase(MAX_READ_SIZE)
-, mStreamSocket(aStreamSocket)
-, mConnector(aConnector)
-, mShuttingDownOnIOThread(false)
-, mAddress(aAddress)
-, mDelayedConnectTask(nullptr)
+  : UnixSocketWatcher(mIOLoop, aFd, aConnectionStatus)
+  , mStreamSocket(aStreamSocket)
+  , mConnector(aConnector)
+  , mShuttingDownOnIOThread(false)
+  , mAddress(aAddress)
+  , mDelayedConnectTask(nullptr)
 {
   MOZ_ASSERT(mStreamSocket);
   MOZ_ASSERT(mConnector);
 }
 
 StreamSocketIO::~StreamSocketIO()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -174,26 +188,32 @@ StreamSocketIO::GetSocketAddr(nsAString&
   if (!mConnector) {
     NS_WARNING("No connector to get socket address from!");
     aAddrStr.Truncate();
     return;
   }
   mConnector->GetSocketAddr(mAddr, aAddrStr);
 }
 
-SocketConsumerBase*
-StreamSocketIO::GetConsumer()
+StreamSocket*
+StreamSocketIO::GetStreamSocket()
+{
+  return mStreamSocket.get();
+}
+
+DataSocket*
+StreamSocketIO::GetDataSocket()
 {
   return mStreamSocket.get();
 }
 
 SocketBase*
 StreamSocketIO::GetSocketBase()
 {
-  return GetConsumer();
+  return GetDataSocket();
 }
 
 bool
 StreamSocketIO::IsShutdownOnMainThread() const
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   return mStreamSocket == nullptr;
@@ -492,16 +512,78 @@ StreamSocketIO::SetSocketFlags(int aFd)
   flags |= O_NONBLOCK;
   if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags))) {
     return false;
   }
 
   return true;
 }
 
+nsresult
+StreamSocketIO::QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer)
+{
+  MOZ_ASSERT(aBuffer);
+
+  if (!mBuffer) {
+    mBuffer = new UnixSocketRawData(MAX_READ_SIZE);
+  }
+  *aBuffer = mBuffer.get();
+
+  return NS_OK;
+}
+
+/**
+ * |ReceiveRunnable| transfers data received on the I/O thread
+ * to an instance of |StreamSocket| on the main thread.
+ */
+class StreamSocketIO::ReceiveRunnable final
+  : public SocketIORunnable<StreamSocketIO>
+{
+public:
+  ReceiveRunnable(StreamSocketIO* aIO, UnixSocketBuffer* aBuffer)
+    : SocketIORunnable<StreamSocketIO>(aIO)
+    , mBuffer(aBuffer)
+  { }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    StreamSocketIO* io = SocketIORunnable<StreamSocketIO>::GetIO();
+
+    if (NS_WARN_IF(io->IsShutdownOnMainThread())) {
+      // Since we've already explicitly closed and the close
+      // happened before this, this isn't really an error.
+      return NS_OK;
+    }
+
+    StreamSocket* streamSocket = io->GetStreamSocket();
+    MOZ_ASSERT(streamSocket);
+
+    streamSocket->ReceiveSocketData(mBuffer);
+
+    return NS_OK;
+  }
+
+private:
+  nsAutoPtr<UnixSocketBuffer> mBuffer;
+};
+
+void
+StreamSocketIO::ConsumeBuffer()
+{
+  NS_DispatchToMainThread(new ReceiveRunnable(this, mBuffer.forget()));
+}
+
+void
+StreamSocketIO::DiscardBuffer()
+{
+  // Nothing to do.
+}
+
 //
 // Socket tasks
 //
 
 class StreamSocketIO::ConnectTask final
   : public SocketIOTask<StreamSocketIO>
 {
 public:
--- a/ipc/unixsocket/StreamSocket.h
+++ b/ipc/unixsocket/StreamSocket.h
@@ -2,32 +2,40 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_streamsocket_h
 #define mozilla_ipc_streamsocket_h
 
-#include "mozilla/ipc/SocketBase.h"
+#include "mozilla/ipc/DataSocket.h"
 #include "ConnectionOrientedSocket.h"
 
 namespace mozilla {
 namespace ipc {
 
 class StreamSocketIO;
 class UnixSocketConnector;
 
-class StreamSocket : public SocketConsumerBase
+class StreamSocket : public DataSocket
                    , public ConnectionOrientedSocket
 {
 public:
   StreamSocket();
 
   /**
+   * Method to be called whenever data is received. This is only called on the
+   * main thread.
+   *
+   * @param aBuffer Data received from the socket.
+   */
+  virtual void ReceiveSocketData(nsAutoPtr<UnixSocketBuffer>& aBuffer) = 0;
+
+  /**
    * Queue data to be sent to the socket on the IO thread. Can only be called on
    * originating thread.
    *
    * @param aBuffer Data to be sent to socket
    */
   void SendSocketData(UnixSocketIOBuffer* aBuffer);
 
   /**
--- a/ipc/unixsocket/moz.build
+++ b/ipc/unixsocket/moz.build
@@ -1,24 +1,26 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.ipc += [
     'ConnectionOrientedSocket.h',
+    'DataSocket.h',
     'ListenSocket.h',
     'SocketBase.h',
     'StreamSocket.h',
     'UnixSocketConnector.h'
 ]
 
 SOURCES += [
     'ConnectionOrientedSocket.cpp',
+    'DataSocket.cpp',
     'ListenSocket.cpp',
     'SocketBase.cpp',
     'StreamSocket.cpp',
     'UnixSocketConnector.cpp'
 ]
 
 FAIL_ON_WARNINGS = True
 
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -58,25 +58,44 @@ js::IsVectorObject(HandleValue v)
 {
     return CheckVectorObject(v, V::type);
 }
 
 template bool js::IsVectorObject<Int32x4>(HandleValue v);
 template bool js::IsVectorObject<Float32x4>(HandleValue v);
 template bool js::IsVectorObject<Float64x2>(HandleValue v);
 
+static inline bool
+ErrorBadArgs(JSContext* cx)
+{
+    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+    return false;
+}
+
+static inline bool
+ErrorWrongTypeArg(JSContext* cx, size_t argIndex, Handle<TypeDescr*> typeDescr)
+{
+    MOZ_ASSERT(argIndex < 10);
+    char charArgIndex[2];
+    JS_snprintf(charArgIndex, sizeof charArgIndex, "%d", argIndex);
+
+    HeapSlot& typeNameSlot = typeDescr->getReservedSlotRef(JS_DESCR_SLOT_STRING_REPR);
+    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR,
+                         JS_EncodeString(cx, typeNameSlot.toString()), charArgIndex);
+    return false;
+}
+
 template<typename V>
 bool
 js::ToSimdConstant(JSContext* cx, HandleValue v, jit::SimdConstant* out)
 {
     typedef typename V::Elem Elem;
-    if (!IsVectorObject<V>(v)) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR);
-        return false;
-    }
+    Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global()));
+    if (!IsVectorObject<V>(v))
+        return ErrorWrongTypeArg(cx, 1, typeDescr);
 
     Elem* mem = reinterpret_cast<Elem*>(v.toObject().as<TypedObject>().typedMem());
     *out = jit::SimdConstant::CreateX4(mem);
     return true;
 }
 
 template bool js::ToSimdConstant<Int32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Float32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
@@ -617,23 +636,16 @@ struct ShiftRightArithmetic {
 };
 struct ShiftRightLogical {
     static int32_t apply(int32_t v, int32_t bits) {
         return uint32_t(bits) >= 32 ? 0 : uint32_t(v) >> bits;
     }
 };
 }
 
-static inline bool
-ErrorBadArgs(JSContext* cx)
-{
-    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-    return false;
-}
-
 template<typename Out>
 static bool
 StoreResult(JSContext* cx, CallArgs& args, typename Out::Elem* result)
 {
     RootedObject obj(cx, CreateSimd<Out>(cx, result));
     if (!obj)
         return false;
     args.rval().setObject(*obj);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -653,24 +653,25 @@ NonLocalExitScope::prepareForNonLocalJum
             break;
 
           default:;
         }
 
         if (stmt->isBlockScope) {
             MOZ_ASSERT(stmt->isNestedScope);
             StaticBlockObject& blockObj = stmt->staticBlock();
-            if (!bce->emit1(JSOP_DEBUGLEAVEBLOCK))
-                return false;
-            if (!popScopeForNonLocalExit(stmt->blockScopeIndex))
-                return false;
             if (blockObj.needsClone()) {
                 if (!bce->emit1(JSOP_POPBLOCKSCOPE))
                     return false;
+            } else {
+                if (!bce->emit1(JSOP_DEBUGLEAVEBLOCK))
+                    return false;
             }
+            if (!popScopeForNonLocalExit(stmt->blockScopeIndex))
+                return false;
         }
     }
 
     FLUSH_POPS();
     return true;
 
 #undef FLUSH_POPS
 }
@@ -872,22 +873,28 @@ BytecodeEmitter::computeLocalOffset(Hand
 // JSScript::getStaticScope(jsbytecode* pc).
 //
 // Block scopes store their locals in the fixed part of a stack frame, after the
 // "fixed var" bindings.  A fixed var binding is a "var" or legacy "const"
 // binding that occurs in a function (as opposed to a script or in eval code).
 // Only functions have fixed var bindings.
 //
 // To assist the debugger, we emit a DEBUGLEAVEBLOCK opcode before leaving a
-// block scope, even if the block has no aliased locals.  This allows
-// DebugScopes to invalidate any association between a debugger scope object,
-// which can proxy access to unaliased stack locals, and the actual live frame.
-// In normal, non-debug mode, this opcode does not cause any baseline code to be
+// block scope, if the block has no aliased locals.  This allows DebugScopes
+// to invalidate any association between a debugger scope object, which can
+// proxy access to unaliased stack locals, and the actual live frame.  In
+// normal, non-debug mode, this opcode does not cause any baseline code to be
 // emitted.
 //
+// If the block has aliased locals, no DEBUGLEAVEBLOCK is emitted, and
+// POPBLOCKSCOPE itself balances the debug scope mapping. This gets around a
+// comedic situation where DEBUGLEAVEBLOCK may remove a block scope from the
+// debug scope map, but the immediate following POPBLOCKSCOPE adds it back due
+// to an onStep hook.
+//
 // Enter a nested scope with enterNestedScope.  It will emit
 // PUSHBLOCKSCOPE/ENTERWITH if needed, and arrange to record the PC bounds of
 // the scope.  Leave a nested scope with leaveNestedScope, which, for blocks,
 // will emit DEBUGLEAVEBLOCK and may emit POPBLOCKSCOPE.  (For "with" scopes it
 // emits LEAVEWITH, of course.)  Pass enterNestedScope a fresh StmtInfoBCE
 // object, and pass that same object to the corresponding leaveNestedScope.  If
 // the statement is a block scope, pass STMT_BLOCK as stmtType; otherwise for
 // with scopes pass STMT_WITH.
@@ -969,21 +976,26 @@ BytecodeEmitter::leaveNestedScope(StmtIn
     NestedScopeObject* staticScope = &blockObjBox->object->as<NestedScopeObject>();
     MOZ_ASSERT(stmt->staticScope == staticScope);
     MOZ_ASSERT(staticScope == this->staticScope);
     MOZ_ASSERT_IF(!stmt->isBlockScope, staticScope->is<StaticWithObject>());
 #endif
 
     popStatement();
 
-    if (!emit1(stmt->isBlockScope ? JSOP_DEBUGLEAVEBLOCK : JSOP_LEAVEWITH))
-        return false;
-
-    if (stmt->isBlockScope && stmt->staticScope->as<StaticBlockObject>().needsClone()) {
-        if (!emit1(JSOP_POPBLOCKSCOPE))
+    if (stmt->isBlockScope) {
+        if (stmt->staticScope->as<StaticBlockObject>().needsClone()) {
+            if (!emit1(JSOP_POPBLOCKSCOPE))
+                return false;
+        } else {
+            if (!emit1(JSOP_DEBUGLEAVEBLOCK))
+                return false;
+        }
+    } else {
+        if (!emit1(JSOP_LEAVEWITH))
             return false;
     }
 
     blockScopeList.recordEnd(blockScopeIndex, offset());
 
     return true;
 }
 
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -628,16 +628,25 @@ class FullParseHandler
             // just assert the node's op is the one used for plain assignment.
             MOZ_ASSERT(node->isOp(JSOP_NOP));
             return true;
         }
 
         return false;
     }
 
+    bool isReturnStatement(ParseNode* node) {
+        return node->isKind(PNK_RETURN);
+    }
+
+    bool isStatementPermittedAfterReturnStatement(ParseNode *node) {
+        ParseNodeKind kind = node->getKind();
+        return kind == PNK_FUNCTION || kind == PNK_VAR || kind == PNK_BREAK || kind == PNK_THROW;
+    }
+
     inline bool finishInitializerAssignment(ParseNode* pn, ParseNode* init, JSOp op);
 
     void setBeginPosition(ParseNode* pn, ParseNode* oth) {
         setBeginPosition(pn, oth->pn_pos.begin);
     }
     void setBeginPosition(ParseNode* pn, uint32_t begin) {
         pn->pn_pos.begin = begin;
         MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
@@ -654,21 +663,33 @@ class FullParseHandler
     void setPosition(ParseNode* pn, const TokenPos& pos) {
         pn->pn_pos = pos;
     }
     TokenPos getPosition(ParseNode* pn) {
         return pn->pn_pos;
     }
 
     ParseNode* newList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
+        MOZ_ASSERT(kind != PNK_VAR);
+        return new_<ListNode>(kind, op, pos());
+    }
+    ParseNode* newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
+        MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET ||
+                   kind == PNK_GLOBALCONST);
         return new_<ListNode>(kind, op, pos());
     }
 
     /* New list with one initial child node. kid must be non-null. */
     ParseNode* newList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) {
+        MOZ_ASSERT(kind != PNK_VAR);
+        return new_<ListNode>(kind, op, kid);
+    }
+    ParseNode* newDeclarationList(ParseNodeKind kind, ParseNode* kid, JSOp op = JSOP_NOP) {
+        MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET ||
+                   kind == PNK_GLOBALCONST);
         return new_<ListNode>(kind, op, kid);
     }
 
     ParseNode* newCatchList() {
         return new_<ListNode>(PNK_CATCHLIST, JSOP_NOP, pos());
     }
 
     ParseNode* newCommaExpressionList(ParseNode* kid) {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1665,17 +1665,17 @@ Parser<ParseHandler>::functionArguments(
                     return false;
 
                 Node item = handler.newBinary(PNK_ASSIGN, lhs, rhs);
                 if (!item)
                     return false;
                 if (list) {
                     handler.addList(list, item);
                 } else {
-                    list = handler.newList(PNK_VAR, item);
+                    list = handler.newDeclarationList(PNK_VAR, item);
                     if (!list)
                         return false;
                     *listp = list;
                 }
                 break;
               }
 
               case TOK_YIELD:
@@ -2815,31 +2815,54 @@ Parser<ParseHandler>::statements()
     Node pn = handler.newStatementList(pc->blockid(), pos());
     if (!pn)
         return null();
 
     Node saveBlock = pc->blockNode;
     pc->blockNode = pn;
 
     bool canHaveDirectives = pc->atBodyLevel();
+    bool afterReturn = false;
+    bool warnedAboutStatementsAfterReturn = false;
+    uint32_t statementBegin;
     for (;;) {
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
             if (tokenStream.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
         if (tt == TOK_EOF || tt == TOK_RC)
             break;
+        if (afterReturn) {
+            TokenPos pos(0, 0);
+            if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
+                return null();
+            statementBegin = pos.begin;
+        }
         Node next = statement(canHaveDirectives);
         if (!next) {
             if (tokenStream.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
+        if (!warnedAboutStatementsAfterReturn) {
+            if (afterReturn) {
+                if (!handler.isStatementPermittedAfterReturnStatement(next)) {
+                    if (!reportWithOffset(ParseWarning, false, statementBegin,
+                                          JSMSG_STMT_AFTER_RETURN))
+                    {
+                        return null();
+                    }
+                    warnedAboutStatementsAfterReturn = true;
+                }
+            } else if (handler.isReturnStatement(next)) {
+                afterReturn = true;
+            }
+        }
 
         if (canHaveDirectives) {
             if (!maybeParseDirective(pn, next, &canHaveDirectives))
                 return null();
         }
 
         handler.addStatementToList(pn, next, pc);
     }
@@ -3798,17 +3821,17 @@ Parser<ParseHandler>::variables(ParseNod
     MOZ_ASSERT_IF(psimple, *psimple);
 
     JSOp op = JSOP_NOP;
     if (kind == PNK_VAR)
         op = JSOP_DEFVAR;
     else if (kind == PNK_GLOBALCONST)
         op = JSOP_DEFCONST;
 
-    Node pn = handler.newList(kind, op);
+    Node pn = handler.newDeclarationList(kind, op);
     if (!pn)
         return null();
 
     /*
      * SpiderMonkey const is really "write once per initialization evaluation"
      * var, whereas let is block scoped. ES-Harmony wants block-scoped const so
      * this code will change soon.
      */
@@ -5163,24 +5186,47 @@ Parser<ParseHandler>::switchStatement()
         }
 
         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
 
         Node body = handler.newStatementList(pc->blockid(), pos());
         if (!body)
             return null();
 
+        bool afterReturn = false;
+        bool warnedAboutStatementsAfterReturn = false;
+        uint32_t statementBegin;
         while (true) {
             if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
             if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT)
                 break;
+            if (afterReturn) {
+                TokenPos pos(0, 0);
+                if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
+                    return null();
+                statementBegin = pos.begin;
+            }
             Node stmt = statement();
             if (!stmt)
                 return null();
+            if (!warnedAboutStatementsAfterReturn) {
+                if (afterReturn) {
+                    if (!handler.isStatementPermittedAfterReturnStatement(stmt)) {
+                        if (!reportWithOffset(ParseWarning, false, statementBegin,
+                                              JSMSG_STMT_AFTER_RETURN))
+                        {
+                            return null();
+                        }
+                        warnedAboutStatementsAfterReturn = true;
+                    }
+                } else if (handler.isReturnStatement(stmt)) {
+                    afterReturn = true;
+                }
+            }
             handler.addList(body, stmt);
         }
 
         // In ES6, lexical bindings canot be accessed until initialized. If
         // there was a 'let' declaration in the case we just parsed, remember
         // the slot starting at which new lexical bindings will be
         // assigned. Since lexical bindings from previous cases will not
         // dominate uses in the current case, any such uses will require a
@@ -5314,29 +5360,17 @@ Parser<ParseHandler>::returnStatement()
     // Parse an optional operand.
     //
     // This is ugly, but we don't want to require a semicolon.
     Node exprNode;
     TokenKind tt;
     if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
     switch (tt) {
-      case TOK_EOL: {
-        bool startsExpr;
-        if (!tokenStream.nextTokenStartsExpr(&startsExpr, TokenStream::Operand))
-            return null();
-        if (startsExpr) {
-            TokenPos pos;
-            if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
-                return null();
-            if (!reportWithOffset(ParseWarning, false, pos.begin, JSMSG_STMT_AFTER_SEMI_LESS))
-                return null();
-        }
-        // Fall through.
-      }
+      case TOK_EOL:
       case TOK_EOF:
       case TOK_SEMI:
       case TOK_RC:
         exprNode = null();
         pc->funHasReturnVoid = true;
         break;
       default: {
         exprNode = expr();
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -38,16 +38,20 @@ class SyntaxParseHandler
   public:
     enum Node {
         NodeFailure = 0,
         NodeGeneric,
         NodeName,
         NodeGetProp,
         NodeStringExprStatement,
         NodeLValue,
+        NodeReturn,
+        NodeHoistableDeclaration,
+        NodeBreak,
+        NodeThrow,
 
         // In rare cases a parenthesized |node| doesn't have the same semantics
         // as |node|.  Each such node has a special Node value, and we use a
         // different Node value to represent the parenthesized form.  See also
         // isUnparenthesized*(Node), newExprStatement(Node, uint32_t),
         // parenthesize(Node), and meaningMightChangeIfParenthesized(Node).
 
         // The directive prologue at the start of a FunctionBody or ScriptBody
@@ -209,41 +213,41 @@ class SyntaxParseHandler
     }
 
     Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
     Node newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; }
     Node newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
     Node newSwitchStatement(uint32_t begin, Node discriminant, Node caseList) { return NodeGeneric; }
     Node newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
     Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
-    Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
-    Node newReturnStatement(Node expr, Node genrval, const TokenPos& pos) { return NodeGeneric; }
+    Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
+    Node newReturnStatement(Node expr, Node genrval, const TokenPos& pos) { return NodeReturn; }
 
     Node newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
         return NodeGeneric;
     }
 
-    Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeGeneric; }
+    Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
     Node newTryStatement(uint32_t begin, Node body, Node catchList, Node finallyBlock) {
         return NodeGeneric;
     }
     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
 
     Node newPropertyAccess(Node pn, PropertyName* name, uint32_t end) {
         lastAtom = name;
         return NodeGetProp;
     }
 
     Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeLValue; }
 
     bool addCatchBlock(Node catchList, Node letBlock,
                        Node catchName, Node catchGuard, Node catchBody) { return true; }
 
     void setLastFunctionArgumentDefault(Node funcpn, Node pn) {}
-    Node newFunctionDefinition() { return NodeGeneric; }
+    Node newFunctionDefinition() { return NodeHoistableDeclaration; }
     void setFunctionBody(Node pn, Node kid) {}
     void setFunctionBox(Node pn, FunctionBox* funbox) {}
     void addFunctionArgument(Node pn, Node argpn) {}
 
     Node newForStatement(uint32_t begin, Node forHead, Node body, unsigned iflags) {
         return NodeGeneric;
     }
 
@@ -272,32 +276,45 @@ class SyntaxParseHandler
 
 
     void setPosition(Node pn, const TokenPos& pos) {}
     TokenPos getPosition(Node pn) {
         return tokenStream.currentToken().pos;
     }
 
     Node newList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
+        MOZ_ASSERT(kind != PNK_VAR);
         return NodeGeneric;
     }
+    Node newDeclarationList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
+        MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET ||
+                   kind == PNK_GLOBALCONST);
+        return kind == PNK_VAR ? NodeHoistableDeclaration : NodeGeneric;
+    }
     Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
+        MOZ_ASSERT(kind != PNK_VAR);
         return NodeGeneric;
     }
+    Node newDeclarationList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
+        MOZ_ASSERT(kind == PNK_VAR || kind == PNK_CONST || kind == PNK_LET ||
+                   kind == PNK_GLOBALCONST);
+        return kind == PNK_VAR ? NodeHoistableDeclaration : NodeGeneric;
+    }
 
     Node newCatchList() {
         return newList(PNK_CATCHLIST, JSOP_NOP);
     }
 
     Node newCommaExpressionList(Node kid) {
         return NodeUnparenthesizedCommaExpr;
     }
 
     void addList(Node list, Node kid) {
-        MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr);
+        MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr ||
+                   list == NodeHoistableDeclaration);
     }
 
     Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
                        ParseContext<SyntaxParseHandler>* pc, JSOp op)
     {
         if (kind == PNK_ASSIGN)
             return NodeUnparenthesizedAssignment;
         return newBinary(kind, lhs, rhs, op);
@@ -310,16 +327,24 @@ class SyntaxParseHandler
     bool isUnparenthesizedCommaExpression(Node node) {
         return node == NodeUnparenthesizedCommaExpr;
     }
 
     bool isUnparenthesizedAssignment(Node node) {
         return node == NodeUnparenthesizedAssignment;
     }
 
+    bool isReturnStatement(Node node) {
+        return node == NodeReturn;
+    }
+
+    bool isStatementPermittedAfterReturnStatement(Node pn) {
+        return pn == NodeHoistableDeclaration || pn == NodeBreak || pn == NodeThrow;
+    }
+
     void setOp(Node pn, JSOp op) {}
     void setBlockId(Node pn, unsigned blockid) {}
     void setFlag(Node pn, unsigned flag) {}
     void setListFlag(Node pn, unsigned flag) {}
     MOZ_WARN_UNUSED_RESULT Node parenthesize(Node node) {
         if (meaningMightChangeIfParenthesized(node))
             return NodeGeneric;
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -317,43 +317,16 @@ TokenStream::TokenStream(ExclusiveContex
     // See Parser::assignExpr() for an explanation of isExprEnding[].
     memset(isExprEnding, 0, sizeof(isExprEnding));
     isExprEnding[TOK_COMMA] = 1;
     isExprEnding[TOK_SEMI]  = 1;
     isExprEnding[TOK_COLON] = 1;
     isExprEnding[TOK_RP]    = 1;
     isExprEnding[TOK_RB]    = 1;
     isExprEnding[TOK_RC]    = 1;
-
-    memset(isExprStarting, 0, sizeof(isExprStarting));
-    isExprStarting[TOK_INC]              = 1;
-    isExprStarting[TOK_DEC]              = 1;
-    isExprStarting[TOK_LB]               = 1;
-    isExprStarting[TOK_LC]               = 1;
-    isExprStarting[TOK_LP]               = 1;
-    isExprStarting[TOK_NAME]             = 1;
-    isExprStarting[TOK_NUMBER]           = 1;
-    isExprStarting[TOK_STRING]           = 1;
-    isExprStarting[TOK_TEMPLATE_HEAD]    = 1;
-    isExprStarting[TOK_NO_SUBS_TEMPLATE] = 1;
-    isExprStarting[TOK_REGEXP]           = 1;
-    isExprStarting[TOK_TRUE]             = 1;
-    isExprStarting[TOK_FALSE]            = 1;
-    isExprStarting[TOK_NULL]             = 1;
-    isExprStarting[TOK_THIS]             = 1;
-    isExprStarting[TOK_NEW]              = 1;
-    isExprStarting[TOK_DELETE]           = 1;
-    isExprStarting[TOK_YIELD]            = 1;
-    isExprStarting[TOK_CLASS]            = 1;
-    isExprStarting[TOK_ADD]              = 1;
-    isExprStarting[TOK_SUB]              = 1;
-    isExprStarting[TOK_TYPEOF]           = 1;
-    isExprStarting[TOK_VOID]             = 1;
-    isExprStarting[TOK_NOT]              = 1;
-    isExprStarting[TOK_BITNOT]           = 1;
 }
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 bool
 TokenStream::checkOptions()
@@ -653,16 +626,17 @@ TokenStream::reportCompileErrorNumberVA(
         err.report.column = srcCoords.columnIndex(offset);
     }
 
     // If we have no location information, try to get one from the caller.
     bool callerFilename = false;
     if (offset != NoOffset && !err.report.filename && cx->isJSContext()) {
         NonBuiltinFrameIter iter(cx->asJSContext(),
                                  FrameIter::ALL_CONTEXTS, FrameIter::GO_THROUGH_SAVED,
+                                 FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK,
                                  cx->compartment()->principals());
         if (!iter.done() && iter.scriptFilename()) {
             callerFilename = true;
             err.report.filename = iter.scriptFilename();
             err.report.lineno = iter.computeLine(&err.report.column);
         }
     }
 
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -489,24 +489,16 @@ class MOZ_STACK_CLASS TokenStream
             *matchedp = true;
         } else {
             *matchedp = false;
             ungetToken();
         }
         return true;
     }
 
-    bool nextTokenStartsExpr(bool* startsExpr, Modifier modifier = None) {
-        TokenKind tt;
-        if (!peekToken(&tt, modifier))
-            return false;
-        *startsExpr = isExprStarting[tt];
-        return true;
-    }
-
     bool nextTokenEndsExpr(bool* endsExpr) {
         TokenKind tt;
         if (!peekToken(&tt))
             return false;
         *endsExpr = isExprEnding[tt];
         return true;
     }
 
@@ -831,17 +823,16 @@ class MOZ_STACK_CLASS TokenStream
     Flags               flags;              // flags -- see above
     size_t              linebase;           // start of current line
     size_t              prevLinebase;       // start of previous line;  size_t(-1) if on the first line
     TokenBuf            userbuf;            // user input buffer
     const char*         filename;          // input filename or null
     mozilla::UniquePtr<char16_t[], JS::FreePolicy> displayURL_; // the user's requested source URL or null
     mozilla::UniquePtr<char16_t[], JS::FreePolicy> sourceMapURL_; // source map's filename or null
     CharBuffer          tokenbuf;           // current token string buffer
-    uint8_t             isExprStarting[TOK_LIMIT];// which tokens can start exprs?
     uint8_t             isExprEnding[TOK_LIMIT];// which tokens definitely terminate exprs?
     ExclusiveContext*   const cx;
     bool                mutedErrors;
     StrictModeGetter*   strictModeGetter;  // used to test for strict mode
 };
 
 // Steal one JSREPORT_* bit (see jsapi.h) to tell that arguments to the error
 // message have const char16_t* type, not const char*.
--- a/js/src/jit-test/tests/asm.js/testBasic.js
+++ b/js/src/jit-test/tests/asm.js/testBasic.js
@@ -32,17 +32,17 @@ assertEq(asmLink(asmCompile(USE_ASM + 'f
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(i) { "use foopy"; "use asm"; i=i|0; } return f'))(), undefined);
 assertEq(asmLink(asmCompile(USE_ASM + '"use warm"; function f(i) { "use cold"; i=i|0 } function g() { "use hot"; return 0 } return f'))(), undefined);
 assertEq(asmLink(asmCompile(USE_ASM + '"use warm"; function f(i) { "use cold"; i=i|0 } function g() { "use asm"; return 0 } return f'))(), undefined);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(){} return f'))(), undefined);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(){;} return f'))(), undefined);
 assertAsmTypeFail(USE_ASM + 'function f(i,j){;} return f');
 assertEq(asmLink(asmCompile('"use asm";; function f(){};;; return f;;'))(), undefined);
 assertAsmTypeFail(USE_ASM + 'function f(x){} return f');
-assertAsmTypeFail(USE_ASM + 'function f(){return; return 1} return f');
+assertAsmTypeFail(USE_ASM + 'function f(){if (0) return; return 1} return f');
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(x){x=x|0} return f'))(42), undefined);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(x){x=x|0; return x|0} return f'))(42), 42);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(x){x=x|0; return x|0;;;} return f'))(42), 42);
 assertEq(asmLink(asmCompile(USE_ASM + 'function f(x,y){x=x|0;y=y|0; return (x+y)|0} return f'))(44, -2), 42);
 assertAsmTypeFail('a', USE_ASM + 'function a(){} return a');
 assertAsmTypeFail('a','b','c', USE_ASM + 'var c');
 assertAsmTypeFail('a','b','c', USE_ASM + 'var c=0');
 assertAsmTypeFail(USE_ASM + 'c=0;return {}');
--- a/js/src/jit-test/tests/asm.js/testControlFlow.js
+++ b/js/src/jit-test/tests/asm.js/testControlFlow.js
@@ -7,17 +7,17 @@ assertEq(asmLink(asmCompile(USE_ASM + "f
 assertEq(asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; if (i) return j^0; return i|0 } return f"))(1,8), 8);
 assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; if ((i|0) == 0) return 10; else if ((i|0) == 1) return 12; else if ((i|0) == 2) return 14; return 0} return f"))(2), 14);
 assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; if ((i|0) == 0) return 10; else if ((i|0) == 1) return 12; else if ((i|0) == 2) return 14; else return 16; return 0} return f"))(3), 16);
 assertEq(asmLink(asmCompile(USE_ASM + "function f(i) { i=i|0; if ((i|0) == 0) i = 10; else if ((i|0) == 1) return 12; return (i|0) } return f"))(0), 10);
 
 assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; if (i) return 0; } return f");
 assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; if (i) return 0; else return 1 } return f");
 assertAsmTypeFail(USE_ASM + "function f(i) { i=i|0; if (i) return 0; return 1.0 } return f");
-assertAsmTypeFail(USE_ASM + "function f() { return 0; 1 } return f");
+assertAsmTypeFail(USE_ASM + "function f() { if (0) return 0; 1 } return f");
 
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { while (0) {} return 0} return f"))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { for (;0;) {} return 0} return f"))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { do {} while(0); return 0} return f"))(), 0);
 
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { while (0) ; return 0} return f"))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { for (;0;) ; return 0} return f"))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { do ; while(0); return 0} return f"))(), 0);
--- a/js/src/jit-test/tests/asm.js/testFFI.js
+++ b/js/src/jit-test/tests/asm.js/testFFI.js
@@ -25,17 +25,17 @@ var imp = { inc:inc, add1:add1, add2:add
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { incc() } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { var i = 0; return (i + inc)|0 } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { inc = 0 } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return (inc() + 1)|0 } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return +((inc()|0) + 1.1) } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return +(inc() + 1.1) } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return (+inc() + 1)|0 } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { var i = 0; inc(i>>>0) } return f');
-assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return inc(); return } return f');
+assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { if (0) return inc(); return } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { inc(inc()) } return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { g(inc()) } function g() {} return f');
 assertAsmTypeFail('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { inc()|inc() } return f');
 
 assertAsmLinkFail(asmCompile('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return inc()|0 } return f'), null, {});
 assertAsmLinkFail(asmCompile('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return inc()|0 } return f'), null, {inc:0});
 assertAsmLinkFail(asmCompile('glob', 'imp', USE_ASM + 'var inc=imp.inc; function f() { return inc()|0 } return f'), null, {inc:{}});
 
--- a/js/src/jit-test/tests/asm.js/testResize.js
+++ b/js/src/jit-test/tests/asm.js/testResize.js
@@ -132,17 +132,17 @@ assertAsmTypeFail('glob', 'ffis', 'b', U
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); xyz=b2; return true } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=1; return true } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b; return true } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; 1; return true } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return 1 } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return false } function f() { return 42 } return f');
-assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true; 1 } function f() { return 42 } return f');
+assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT1 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; if (0) return true; 1 } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); b=b2; return true } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i32=new I32(b2); i8=new I8(b2); b=b2; return true } function f() { return 42 } return f');
        asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true } function f() { return 42 } return f');
 assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I32(b2); i32=new I8(b2); b=b2; return true } function f() { return 42 } return f');
        asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new II32(b2); b=b2; return true } function f() { return 42 } return f');
 
 // Tests for no calls in heap index expressions
rename from js/src/jit-test/tests/basic/semicolon-less-return.js
rename to js/src/jit-test/tests/basic/statement-after-return.js
--- a/js/src/jit-test/tests/basic/semicolon-less-return.js
+++ b/js/src/jit-test/tests/basic/statement-after-return.js
@@ -1,34 +1,33 @@
-// Warning should be shown for expression-like statement after semicolon-less
-// return (bug 1005110).
+// Warning should be shown for unreachable statement after return (bug 1151931).
 
 load(libdir + "class.js");
 
 if (options().indexOf("werror") == -1)
   options("werror");
 
 function testWarn(code, lineNumber, columnNumber) {
   var caught = false;
   try {
     eval(code);
   } catch (e) {
     caught = true;
-    assertEq(e.message, "unreachable expression after semicolon-less return statement", code);
+    assertEq(e.constructor, SyntaxError);
     assertEq(e.lineNumber, lineNumber);
     assertEq(e.columnNumber, columnNumber);
   }
   assertEq(caught, true, "warning should be caught for " + code);
 
   caught = false;
   try {
     Reflect.parse(code);
   } catch (e) {
     caught = true;
-    assertEq(e.message, "unreachable expression after semicolon-less return statement", code);
+    assertEq(e.constructor, SyntaxError);
   }
   assertEq(caught, true, "warning should be caught for " + code);
 }
 
 function testPass(code) {
   var caught = false;
   try {
     eval(code);
@@ -41,61 +40,49 @@ function testPass(code) {
   try {
     Reflect.parse(code);
   } catch (e) {
     caught = true;
   }
   assertEq(caught, false, "warning should not be caught for " + code);
 }
 
-// not EOL
-
 testPass(`
 function f() {
   return (
     1 + 2
   );
 }
 `);
-testPass(`
-function f() {
-  return;
-  1 + 2;
-}
-`);
 
-// starts expression
-
-// TOK_INC
+// unary expression
 testWarn(`
 function f() {
   var i = 0;
   return
     ++i;
 }
 `, 5, 4);
-
-// TOK_DEC
 testWarn(`
 function f() {
   var i = 0;
   return
     --i;
 }
 `, 5, 4);
 
-// TOK_LB
+// array
 testWarn(`
 function f() {
   return
     [1, 2, 3];
 }
 `, 4, 4);
 
-// TOK_LC
+// block (object)
 testWarn(`
 function f() {
   return
     {x: 10};
 }
 `, 4, 4);
 testWarn(`
 function f() {
@@ -104,47 +91,47 @@ function f() {
     method()
     {
       f();
     }
   };
 }
 `, 4, 2);
 
-// TOK_LP
+// expression in paren
 testWarn(`
 function f() {
   return
     (1 + 2);
 }
 `, 4, 4);
 
-// TOK_NAME
+// name
 testWarn(`
 function f() {
   return
     f;
 }
 `, 4, 4);
 
-// TOK_NUMBER
+// binary expression
 testWarn(`
 function f() {
   return
     1 + 2;
 }
 `, 4, 4);
 testWarn(`
 function f() {
   return
     .1 + .2;
 }
 `, 4, 4);
 
-// TOK_STRING
+// string
 testWarn(`
 function f() {
   return
     "foo";
 }
 `, 4, 4);
 testWarn(`
 function f() {
@@ -154,352 +141,371 @@ function f() {
 `, 4, 4);
 testWarn(`
 function f() {
   return
     'foo';
 }
 `, 4, 4);
 
-// TOK_TEMPLATE_HEAD
+// template string
 testWarn(`
 function f() {
   return
     \`foo\${1 + 2}\`;
 }
 `, 4, 4);
-
-// TOK_NO_SUBS_TEMPLATE
 testWarn(`
 function f() {
   return
     \`foo\`;
 }
 `, 4, 4);
 
-// TOK_REGEXP
+// RegExp
 testWarn(`
 function f() {
   return
     /foo/;
 }
 `, 4, 4);
 
-// TOK_TRUE
+// boolean
 testWarn(`
 function f() {
   return
     true;
 }
 `, 4, 4);
-
-// TOK_FALSE
 testWarn(`
 function f() {
   return
     false;
 }
 `, 4, 4);
 
-// TOK_NULL
+// null
 testWarn(`
 function f() {
   return
     null;
 }
 `, 4, 4);
 
-// TOK_THIS
+// this
 testWarn(`
 function f() {
   return
     this;
 }
 `, 4, 4);
 
-// TOK_NEW
+// new
 testWarn(`
 function f() {
   return
     new Array();
 }
 `, 4, 4);
 
-// TOK_DELETE
+// delete
 testWarn(`
 function f() {
   var a = {x: 10};
   return
     delete a.x;
 }
 `, 5, 4);
 
-// TOK_YIELD
+// yield
 testWarn(`
 function* f() {
   return
     yield 1;
 }
 `, 4, 4);
 
-// TOK_CLASS
+// class
 if (classesEnabled()) {
   testWarn(`
 function f() {
   return
     class A { constructor() {} };
 }
 `, 4, 4);
 }
 
-// TOK_ADD
+// unary expression
 testWarn(`
 function f() {
   return
     +1;
 }
 `, 4, 4);
-
-// TOK_SUB
 testWarn(`
 function f() {
   return
     -1;
 }
 `, 4, 4);
-
-// TOK_NOT
 testWarn(`
 function f() {
   return
     !1;
 }
 `, 4, 4);
-
-// TOK_BITNOT
 testWarn(`
 function f() {
   return
     ~1;
 }
 `, 4, 4);
 
-// don't start expression
-
-// TOK_EOF
+// eof
 testPass(`
 var f = new Function("return\\n");
 `);
 
-// TOK_SEMI
+// empty statement
 testPass(`
 function f() {
   return
   ;
 }
 `);
 
-// TOK_RC
+// end of block
 testPass(`
 function f() {
   {
     return
   }
 }
 `);
 
-// TOK_FUNCTION
+// function (hosted)
 testPass(`
 function f() {
   g();
   return
   function g() {}
 }
 `);
 
-// TOK_IF
-testPass(`
+// if
+testWarn(`
 function f() {
   return
   if (true)
     1 + 2;
 }
-`);
+`, 4, 2);
 
-// TOK_ELSE
+// else
 testPass(`
 function f() {
   if (true)
     return
   else
     1 + 2;
 }
 `);
 
-// TOK_SWITCH
-testPass(`
+// switch
+testWarn(`
 function f() {
   return
   switch (1) {
     case 1:
       break;
   }
 }
+`, 4, 2);
+
+// return in switch
+testWarn(`
+function f() {
+  switch (1) {
+    case 1:
+      return;
+      1 + 2;
+      break;
+  }
+}
+`, 6, 6);
+
+// break in switch
+testPass(`
+function f() {
+  switch (1) {
+    case 1:
+      return;
+      break;
+  }
+}
 `);
 
-// TOK_CASE
+// case
 testPass(`
 function f() {
   switch (1) {
     case 0:
       return
     case 1:
       break;
   }
 }
 `);
 
-// TOK_DEFAULT
+// default
 testPass(`
 function f() {
   switch (1) {
     case 0:
       return
     default:
       break;
   }
 }
 `);
 
-// TOK_WHILE
-testPass(`
+// while
+testWarn(`
 function f() {
   return
   while (false)
     1 + 2;
 }
-`);
+`, 4, 2);
 testPass(`
 function f() {
   do
     return
   while (false);
 }
 `);
 
-// TOK_DO
-testPass(`
+// do
+testWarn(`
 function f() {
   return
   do {
     1 + 2;
   } while (false);
 }
-`);
+`, 4, 2);
 
-// TOK_FOR
-testPass(`
+// for
+testWarn(`
 function f() {
   return
   for (;;) {
     break;
   }
 }
-`);
+`, 4, 2);
 
-// TOK_BREAK
+// break in for
 testPass(`
 function f() {
   for (;;) {
     return
     break;
   }
 }
-`);
+`, 5, 4);
 
-// TOK_CONTINUE
-testPass(`
+// continue
+testWarn(`
 function f() {
   for (;;) {
     return
     continue;
   }
 }
-`);
+`, 5, 4);
 
-// TOK_VAR
+// var (hosted)
 testPass(`
 function f() {
   return
   var a = 1;
 }
 `);
 
-// TOK_CONST
-testPass(`
+// const
+testWarn(`
 function f() {
   return
   const a = 1;
 }
-`);
+`, 4, 2);
 
-// TOK_WITH
-testPass(`
+// with
+testWarn(`
 function f() {
   return
   with ({}) {
     1;
   }
 }
-`);
+`, 4, 2);
 
-// TOK_RETURN
-testPass(`
+// return
+testWarn(`
 function f() {
   return
   return;
 }
-`);
+`, 4, 2);
 
-// TOK_TRY
-testPass(`
+// try
+testWarn(`
 function f() {
   return
   try {
   } catch (e) {
   }
 }
-`);
+`, 4, 2);
 
-// TOK_THROW
+// throw
 testPass(`
 function f() {
   return
   throw 1;
 }
 `);
 
-// TOK_DEBUGGER
-testPass(`
+// debugger
+testWarn(`
 function f() {
   return
   debugger;
 }
-`);
+`, 4, 2);
 
-// TOK_LET
-testPass(`
+// let
+testWarn(`
 function f() {
   return
   let a = 1;
 }
-`);
+`, 4, 2);
 
-// exceptional case
+// skip hoisted
 
-// It's not possible to distinguish between a label statement and an expression
-// starts with identifier, by checking a token next to return.
 testWarn(`
 function f() {
   return
-  a: 1;
+  var a = 0;
+  (1 + 2);
 }
-`, 4, 2);
+`, 5, 2);
+
+testWarn(`
+function f() {
+  return
+  function f() {}
+  var a = 0;
+  (1 + 2);
+}
+`, 6, 2);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Environment-gc-03.js
@@ -0,0 +1,21 @@
+// Test that block scopes cannot be resurrected by onStep.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+dbg.onDebuggerStatement = function(frame) {
+    frame.onStep = (function() {
+        frame.environment;
+    });
+};
+
+g.eval("debugger; for (let i = 0; i < 1; i++) (function(){});");
+
+// If the last freshened block scope was incorrectly resurrected by onStep
+// above, GCing will assert.
+gc();
+
+g.eval("debugger; { let i = 0; (function(){ i = 42; }); }");
+gc();
+
+g.eval("debugger; try { throw 42; } catch (e) { };");
+gc();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1148917.js
@@ -0,0 +1,14 @@
+// |jit-test| error: Error
+
+var g = newGlobal();
+g.eval('function f(a) { evaluate("f(" + " - 1);", {newContext: true}); }');
+var dbg = new Debugger(g);
+var frames = [];
+dbg.onEnterFrame = function (frame) {
+  if (frames.length == 3)
+    return;
+  frames.push(frame);
+  for (var f of frames)
+    f.eval('a').return
+};
+g.f();
--- a/js/src/jit-test/tests/debug/bug980585.js
+++ b/js/src/jit-test/tests/debug/bug980585.js
@@ -1,10 +1,10 @@
 var g = newGlobal();
 var dbg = new Debugger(g);
 
 try {
-  g.eval("function f() { var array = ['a', 'b']; [1].map(function () {}); return {array}; }");
+  g.eval("function f() { [1].map(function () {}); const x = 42; x = 43; } f();");
 } catch (e) {
   // Ignore the syntax error.
 }
 
 dbg.findScripts();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/lexical-check-5.js
@@ -0,0 +1,7 @@
+// |jit-test| error: ReferenceError
+
+{
+  for (var i = 0; i < 100; i++)
+    a += i;
+  let a = 1;
+}
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3005,32 +3005,42 @@ BaselineCompiler::emit_JSOP_PUSHBLOCKSCO
     pushArg(R0.scratchReg());
 
     return callVM(PushBlockScopeInfo);
 }
 
 typedef bool (*PopBlockScopeFn)(JSContext*, BaselineFrame*);
 static const VMFunction PopBlockScopeInfo = FunctionInfo<PopBlockScopeFn>(jit::PopBlockScope);
 
+typedef bool (*DebugLeaveThenPopBlockScopeFn)(JSContext*, BaselineFrame*, jsbytecode*);
+static const VMFunction DebugLeaveThenPopBlockScopeInfo =
+    FunctionInfo<DebugLeaveThenPopBlockScopeFn>(jit::DebugLeaveThenPopBlockScope);
+
 bool
 BaselineCompiler::emit_JSOP_POPBLOCKSCOPE()
 {
     prepareVMCall();
 
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
+
+    if (compileDebugInstrumentation_) {
+        pushArg(ImmPtr(pc));
+        pushArg(R0.scratchReg());
+        return callVM(DebugLeaveThenPopBlockScopeInfo);
+    }
+
     pushArg(R0.scratchReg());
-
     return callVM(PopBlockScopeInfo);
 }
 
 typedef bool (*FreshenBlockScopeFn)(JSContext*, BaselineFrame*);
 static const VMFunction FreshenBlockScopeInfo =
     FunctionInfo<FreshenBlockScopeFn>(jit::FreshenBlockScope);
 
-typedef bool (*DebugLeaveThenFreshenBlockScopeFn)(JSContext*, BaselineFrame*, jsbytecode* pc);
+typedef bool (*DebugLeaveThenFreshenBlockScopeFn)(JSContext*, BaselineFrame*, jsbytecode*);
 static const VMFunction DebugLeaveThenFreshenBlockScopeInfo =
     FunctionInfo<DebugLeaveThenFreshenBlockScopeFn>(jit::DebugLeaveThenFreshenBlockScope);
 
 bool
 BaselineCompiler::emit_JSOP_FRESHENBLOCKSCOPE()
 {
     prepareVMCall();
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4791,49 +4791,34 @@ CodeGenerator::visitCreateThisWithProto(
     if (callee->isConstant())
         pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
     else
         pushArg(ToRegister(callee));
 
     callVM(CreateThisWithProtoInfo, lir);
 }
 
-typedef JSObject* (*NewGCObjectFn)(JSContext* cx, gc::AllocKind allocKind,
-                                   gc::InitialHeap initialHeap, size_t ndynamic,
-                                   const js::Class* clasp);
-static const VMFunction NewGCObjectInfo =
-    FunctionInfo<NewGCObjectFn>(js::jit::NewGCObject);
-
 void
 CodeGenerator::visitCreateThisWithTemplate(LCreateThisWithTemplate* lir)
 {
     JSObject* templateObject = lir->mir()->templateObject();
-    gc::AllocKind allocKind = templateObject->asTenured().getAllocKind();
-    gc::InitialHeap initialHeap = lir->mir()->initialHeap();
-    const js::Class* clasp = templateObject->getClass();
-    size_t ndynamic = 0;
-    if (templateObject->isNative())
-        ndynamic = templateObject->as<NativeObject>().numDynamicSlots();
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
 
-    OutOfLineCode* ool = oolCallVM(NewGCObjectInfo, lir,
-                                   (ArgList(), Imm32(int32_t(allocKind)), Imm32(initialHeap),
-                                    Imm32(ndynamic), ImmPtr(clasp)),
+    OutOfLineCode* ool = oolCallVM(NewInitObjectWithTemplateInfo, lir,
+                                   (ArgList(), ImmGCPtr(templateObject)),
                                    StoreRegisterTo(objReg));
 
     // Allocate. If the FreeList is empty, call to VM, which may GC.
-    masm.newGCThing(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry());
-
-    // Initialize based on the templateObject.
+    bool initContents = !templateObject->is<PlainObject>() ||
+                        ShouldInitFixedSlots(lir, &templateObject->as<PlainObject>());
+    masm.createGCObject(objReg, tempReg, templateObject, lir->mir()->initialHeap(), ool->entry(),
+                        initContents);
+
     masm.bind(ool->rejoin());
-
-    bool initContents = !templateObject->is<PlainObject>() ||
-                          ShouldInitFixedSlots(lir, &templateObject->as<PlainObject>());
-    masm.initGCThing(objReg, tempReg, templateObject, initContents);
 }
 
 typedef JSObject* (*NewIonArgumentsObjectFn)(JSContext* cx, JitFrameLayout* frame, HandleObject);
 static const VMFunction NewIonArgumentsObjectInfo =
     FunctionInfo<NewIonArgumentsObjectFn>((NewIonArgumentsObjectFn) ArgumentsObject::createForIon);
 
 void
 CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12903,16 +12903,19 @@ MDefinition*
 IonBuilder::addLexicalCheck(MDefinition* input)
 {
     MOZ_ASSERT(JSOp(*pc) == JSOP_CHECKLEXICAL || JSOp(*pc) == JSOP_CHECKALIASEDLEXICAL);
 
     MInstruction* lexicalCheck;
 
     // If we're guaranteed to not be JS_UNINITIALIZED_LEXICAL, no need to check.
     if (input->type() == MIRType_MagicUninitializedLexical) {
+        // Mark the input as implicitly used so the JS_UNINITIALIZED_LEXICAL
+        // magic value will be preserved on bailout.
+        input->setImplicitlyUsedUnchecked();
         lexicalCheck = MThrowUninitializedLexical::New(alloc());
         current->add(lexicalCheck);
         if (!resumeAfter(lexicalCheck))
             return nullptr;
         return constant(UndefinedValue());
     }
 
     if (input->type() == MIRType_Value) {
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1179,29 +1179,16 @@ MacroAssembler::allocateObject(Register 
     pop(temp);
     callFreeStub(temp);
     jump(fail);
 
     bind(&success);
 }
 
 void
-MacroAssembler::newGCThing(Register result, Register temp, JSObject* templateObj,
-                           gc::InitialHeap initialHeap, Label* fail)
-{
-    gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
-    MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
-
-    size_t ndynamic = 0;
-    if (templateObj->isNative())
-        ndynamic = templateObj->as<NativeObject>().numDynamicSlots();
-    allocateObject(result, temp, allocKind, ndynamic, initialHeap, fail);
-}
-
-void
 MacroAssembler::createGCObject(Register obj, Register temp, JSObject* templateObj,
                                gc::InitialHeap initialHeap, Label* fail, bool initContents,
                                bool convertDoubleElements)
 {
     gc::AllocKind allocKind = templateObj->asTenured().getAllocKind();
     MOZ_ASSERT(gc::IsObjectAllocKind(allocKind));
 
     uint32_t nDynamicSlots = 0;
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -797,18 +797,16 @@ class MacroAssembler : public MacroAssem
 
   public:
     void callMallocStub(size_t nbytes, Register result, Label* fail);
     void callFreeStub(Register slots);
     void createGCObject(Register result, Register temp, JSObject* templateObj,
                         gc::InitialHeap initialHeap, Label* fail, bool initContents = true,
                         bool convertDoubleElements = false);
 
-    void newGCThing(Register result, Register temp, JSObject* templateObj,
-                     gc::InitialHeap initialHeap, Label* fail);
     void initGCThing(Register obj, Register temp, JSObject* templateObj,
                      bool initContents = true, bool convertDoubleElements = false);
 
     void initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject);
 
     void newGCString(Register result, Register temp, Label* fail);
     void newGCFatInlineString(Register result, Register temp, Label* fail);
 
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -81,28 +81,16 @@ InvokeFunction(JSContext* cx, HandleObje
         RootedScript script(cx, cx->currentScript(&pc));
         TypeScript::Monitor(cx, script, pc, rv.get());
     }
 
     *rval = rv;
     return true;
 }
 
-JSObject*
-NewGCObject(JSContext* cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap,
-            size_t ndynamic, const js::Class* clasp)
-{
-    JSObject* obj = js::Allocate<JSObject>(cx, allocKind, ndynamic, initialHeap, clasp);
-    if (!obj)
-        return nullptr;
-
-    SetNewObjectMetadata(cx, obj);
-    return obj;
-}
-
 bool
 CheckOverRecursed(JSContext* cx)
 {
     // We just failed the jitStackLimit check. There are two possible reasons:
     //  - jitStackLimit was the real stack limit and we're over-recursed
     //  - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt
     //    and we need to call JSRuntime::handleInterrupt.
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
@@ -992,25 +980,33 @@ PushBlockScope(JSContext* cx, BaselineFr
 bool
 PopBlockScope(JSContext* cx, BaselineFrame* frame)
 {
     frame->popBlock(cx);
     return true;
 }
 
 bool
+DebugLeaveThenPopBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
+{
+    MOZ_ALWAYS_TRUE(DebugLeaveBlock(cx, frame, pc));
+    frame->popBlock(cx);
+    return true;
+}
+
+bool
 FreshenBlockScope(JSContext* cx, BaselineFrame* frame)
 {
     return frame->freshenBlock(cx);
 }
 
 bool
 DebugLeaveThenFreshenBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
 {
-    DebugLeaveBlock(cx, frame, pc);
+    MOZ_ALWAYS_TRUE(DebugLeaveBlock(cx, frame, pc));
     return frame->freshenBlock(cx);
 }
 
 bool
 DebugLeaveBlock(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
 {
     MOZ_ASSERT(frame->script()->baselineScript()->hasDebugInstrumentation());
     if (cx->compartment()->isDebuggee())
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -627,18 +627,16 @@ class AutoDetectInvalidation
 
     ~AutoDetectInvalidation() {
         if (!disabled_ && ionScript_->invalidated())
             setReturnOverride();
     }
 };
 
 bool InvokeFunction(JSContext* cx, HandleObject obj0, uint32_t argc, Value* argv, Value* rval);
-JSObject* NewGCObject(JSContext* cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap,
-                      size_t ndynamic, const js::Class* clasp);
 
 bool CheckOverRecursed(JSContext* cx);
 bool CheckOverRecursedWithExtra(JSContext* cx, BaselineFrame* frame,
                                 uint32_t extra, uint32_t earlyCheck);
 
 bool DefVarOrConst(JSContext* cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
 bool SetConst(JSContext* cx, HandlePropertyName name, HandleObject scopeChain, HandleValue rval);
 bool MutatePrototype(JSContext* cx, HandlePlainObject obj, HandleValue value);
@@ -723,16 +721,17 @@ bool OnDebuggerStatement(JSContext* cx, 
 bool GlobalHasLiveOnDebuggerStatement(JSContext* cx);
 
 bool EnterWith(JSContext* cx, BaselineFrame* frame, HandleValue val,
                Handle<StaticWithObject*> templ);
 bool LeaveWith(JSContext* cx, BaselineFrame* frame);
 
 bool PushBlockScope(JSContext* cx, BaselineFrame* frame, Handle<StaticBlockObject*> block);
 bool PopBlockScope(JSContext* cx, BaselineFrame* frame);
+bool DebugLeaveThenPopBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc);
 bool FreshenBlockScope(JSContext* cx, BaselineFrame* frame);
 bool DebugLeaveThenFreshenBlockScope(JSContext* cx, BaselineFrame* frame, jsbytecode* pc);
 bool DebugLeaveBlock(JSContext* cx, BaselineFrame* frame, jsbytecode* pc);
 
 bool InitBaselineFrameForOsr(BaselineFrame* frame, InterpreterFrame* interpFrame,
                              uint32_t numStackValues);
 
 JSObject* CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -299,22 +299,22 @@ MSG_DEF(JSMSG_REDECLARED_PARAM,        1
 MSG_DEF(JSMSG_RESERVED_ID,             1, JSEXN_SYNTAXERR, "{0} is a reserved identifier")
 MSG_DEF(JSMSG_REST_WITH_DEFAULT,       0, JSEXN_SYNTAXERR, "rest parameter may not have a default")
 MSG_DEF(JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL, 1, JSEXN_SYNTAXERR, "self-hosted code cannot contain top-level {0} declarations")
 MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
 MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND,     0, JSEXN_SYNTAXERR, "missing ; after for-loop condition")
 MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT,     0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")
 MSG_DEF(JSMSG_SEMI_BEFORE_STMNT,       0, JSEXN_SYNTAXERR, "missing ; before statement")
 MSG_DEF(JSMSG_SOURCE_TOO_LONG,         0, JSEXN_RANGEERR, "source is too long")
-MSG_DEF(JSMSG_STMT_AFTER_SEMI_LESS,    0, JSEXN_SYNTAXERR, "unreachable expression after semicolon-less return statement")
+MSG_DEF(JSMSG_STMT_AFTER_RETURN,       0, JSEXN_SYNTAXERR, "unreachable code after return statement")
 MSG_DEF(JSMSG_STRICT_CODE_LET_EXPR_STMT, 0, JSEXN_ERR, "strict mode code may not contain unparenthesized let expression statements")
 MSG_DEF(JSMSG_STRICT_CODE_WITH,        0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements")
 MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
 MSG_DEF(JSMSG_TEMPLSTR_UNTERM_EXPR,    0, JSEXN_SYNTAXERR, "missing } in template string")
-MSG_DEF(JSMSG_SIMD_NOT_A_VECTOR,       0, JSEXN_TYPEERR, "value isn't a SIMD value object")
+MSG_DEF(JSMSG_SIMD_NOT_A_VECTOR,       2, JSEXN_TYPEERR, "expecting a SIMD {0} object as argument {1}")
 MSG_DEF(JSMSG_TOO_MANY_CASES,          0, JSEXN_INTERNALERR, "too many switch cases")
 MSG_DEF(JSMSG_TOO_MANY_CATCH_VARS,     0, JSEXN_SYNTAXERR, "too many catch variables")
 MSG_DEF(JSMSG_TOO_MANY_CON_ARGS,       0, JSEXN_SYNTAXERR, "too many constructor arguments")
 MSG_DEF(JSMSG_TOO_MANY_DEFAULTS,       0, JSEXN_SYNTAXERR, "more than one switch default")
 MSG_DEF(JSMSG_TOO_MANY_FUN_ARGS,       0, JSEXN_SYNTAXERR, "too many function arguments")
 MSG_DEF(JSMSG_TOO_MANY_LOCALS,         0, JSEXN_SYNTAXERR, "too many local variables")
 MSG_DEF(JSMSG_TOO_MANY_YIELDS,         0, JSEXN_SYNTAXERR, "too many yield expressions")
 MSG_DEF(JSMSG_TOUGH_BREAK,             0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch")
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -753,26 +753,35 @@ CreateLazyScriptsForCompartment(JSContex
     // script did not escape the script.
     //
     // Note that while we ideally iterate over LazyScripts, LazyScripts do not
     // currently stand in 1-1 relation with JSScripts; JSFunctions with the
     // same LazyScript may create different JSScripts due to relazification of
     // clones. See bug 1105306.
     for (gc::ZoneCellIter i(cx->zone(), JSFunction::FinalizeKind); !i.done(); i.next()) {
         JSObject* obj = i.get<JSObject>();
-        if (obj->compartment() == cx->compartment() && obj->is<JSFunction>()) {
-            JSFunction* fun = &obj->as<JSFunction>();
-            if (fun->isInterpretedLazy()) {
-                LazyScript* lazy = fun->lazyScriptOrNull();
-                if (lazy && lazy->sourceObject() && !lazy->maybeScript() &&
-                    !lazy->hasUncompiledEnclosingScript())
-                {
-                    if (!lazyFunctions.append(fun))
-                        return false;
-                }
+
+        // Sweeping is incremental; take care to not delazify functions that
+        // are about to be finalized. GC things referenced by objects that are
+        // about to be finalized (e.g., in slots) may already be freed.
+        if (gc::IsAboutToBeFinalizedUnbarriered(&obj) ||
+            obj->compartment() != cx->compartment() ||
+            !obj->is<JSFunction>())
+        {
+            continue;
+        }
+
+        JSFunction* fun = &obj->as<JSFunction>();
+        if (fun->isInterpretedLazy()) {
+            LazyScript* lazy = fun->lazyScriptOrNull();
+            if (lazy && lazy->sourceObject() && !lazy->maybeScript() &&
+                !lazy->hasUncompiledEnclosingScript())
+            {
+                if (!lazyFunctions.append(fun))
+                    return false;
             }
         }
     }
 
     // Create scripts for each lazy function, updating the list of functions to
     // process with any newly exposed inner functions in created scripts.
     // A function cannot be delazified until its outer script exists.
     for (size_t i = 0; i < lazyFunctions.length(); i++) {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -337,19 +337,26 @@ class JSFunction : public js::NativeObje
 
             flags_ &= ~INTERPRETED_LAZY;
             flags_ |= INTERPRETED;
             initScript(script);
         }
         return nonLazyScript();
     }
 
-    JSScript* nonLazyScript() const {
+    // The state of a JSFunction whose script errored out during bytecode
+    // compilation. Such JSFunctions are only reachable via GC iteration and
+    // not from script.
+    bool hasUncompiledScript() const {
         MOZ_ASSERT(hasScript());
-        MOZ_ASSERT(u.i.s.script_);
+        return !u.i.s.script_;
+    }
+
+    JSScript* nonLazyScript() const {
+        MOZ_ASSERT(!hasUncompiledScript());
         return u.i.s.script_;
     }
 
     bool getLength(JSContext* cx, uint16_t* length) {
         JS::RootedFunction self(cx, this);
         if (self->isInterpretedLazy() && !self->getOrCreateScript(cx))
             return false;
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3906,17 +3906,17 @@ LazyScript::hasUncompiledEnclosingScript
     //
     // If the enclosing scope is a function with a null script or has a script
     // without code, it was not successfully compiled.
 
     if (!enclosingScope() || !enclosingScope()->is<JSFunction>())
         return false;
 
     JSFunction& fun = enclosingScope()->as<JSFunction>();
-    return fun.isInterpreted() && (!fun.hasScript() || !fun.nonLazyScript()->code());
+    return !fun.hasScript() || fun.hasUncompiledScript() || !fun.nonLazyScript()->code();
 }
 
 uint32_t
 LazyScript::staticLevel(JSContext* cx) const
 {
     for (StaticScopeIter<NoGC> ssi(enclosingScope()); !ssi.done(); ssi++) {
         if (ssi.type() == StaticScopeIter<NoGC>::Function)
             return ssi.funScript()->staticLevel() + 1;
--- a/js/src/tests/js1_8_5/extensions/recursion.js
+++ b/js/src/tests/js1_8_5/extensions/recursion.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(xulRuntime.OS=="Darwin"&&isDebugBuild) -- this takes too long to over-recurse.
+// |reftest| skip-if((xulRuntime.OS=="Darwin"||Android)&&isDebugBuild) -- this takes too long to over-recurse.
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  * Contributor:
  *   Christian Holler <decoder@own-hero.net>
  */
 
 //-----------------------------------------------------------------------------
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -5667,30 +5667,31 @@ CheckThisFrame(JSContext* cx, const Call
     RootedNativeObject thisobj(cx, CheckThisFrame(cx, args, fnname, true));    \
     if (!thisobj)                                                              \
         return false
 
 #define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, frame)                 \
     THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj);                   \
     AbstractFramePtr frame = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \
     if (frame.isScriptFrameIterData()) {                                       \
-        ScriptFrameIter iter(*(ScriptFrameIter::Data*)(frame.raw()));         \
+        ScriptFrameIter iter(*(ScriptFrameIter::Data*)(frame.raw()));          \
         frame = iter.abstractFramePtr();                                       \
     }
 
 #define THIS_FRAME_ITER(cx, argc, vp, fnname, args, thisobj, maybeIter, iter)  \
     THIS_FRAME_THISOBJ(cx, argc, vp, fnname, args, thisobj);                   \
     Maybe<ScriptFrameIter> maybeIter;                                          \
     {                                                                          \
         AbstractFramePtr f = AbstractFramePtr::FromRaw(thisobj->getPrivate()); \
         if (f.isScriptFrameIterData()) {                                       \
-            maybeIter.emplace(*(ScriptFrameIter::Data*)(f.raw()));            \
+            maybeIter.emplace(*(ScriptFrameIter::Data*)(f.raw()));             \
         } else {                                                               \
             maybeIter.emplace(cx, ScriptFrameIter::ALL_CONTEXTS,               \
-                              ScriptFrameIter::GO_THROUGH_SAVED);              \
+                              ScriptFrameIter::GO_THROUGH_SAVED,               \
+                              ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK); \
             ScriptFrameIter& iter = *maybeIter;                                \
             while (!iter.hasUsableAbstractFramePtr() || iter.abstractFramePtr() != f) \
                 ++iter;                                                        \
             AbstractFramePtr data = iter.copyDataAsAbstractFramePtr();         \
             if (!data)                                                         \
                 return false;                                                  \
             thisobj->setPrivate(data.raw());                                   \
         }                                                                      \
@@ -6402,26 +6403,26 @@ DebuggerObject_checkThis(JSContext* cx, 
     return nthisobj;
 }
 
 #define THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, fnname, args, obj)            \
     CallArgs args = CallArgsFromVp(argc, vp);                                 \
     RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname));         \
     if (!obj)                                                                 \
         return false;                                                         \
-    obj = (JSObject*) obj->as<NativeObject>().getPrivate();                  \
+    obj = (JSObject*) obj->as<NativeObject>().getPrivate();                   \
     MOZ_ASSERT(obj)
 
 #define THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, fnname, args, dbg, obj) \
    CallArgs args = CallArgsFromVp(argc, vp);                                  \
    RootedObject obj(cx, DebuggerObject_checkThis(cx, args, fnname));          \
    if (!obj)                                                                  \
        return false;                                                          \
    Debugger* dbg = Debugger::fromChildJSObject(obj);                          \
-   obj = (JSObject*) obj->as<NativeObject>().getPrivate();                   \
+   obj = (JSObject*) obj->as<NativeObject>().getPrivate();                    \
    MOZ_ASSERT(obj)
 
 static bool
 DebuggerObject_construct(JSContext* cx, unsigned argc, Value* vp)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                          "Debugger.Object");
     return false;
@@ -7327,20 +7328,20 @@ DebuggerEnv_checkThis(JSContext* cx, con
     return nthisobj;
 }
 
 #define THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env)                \
     CallArgs args = CallArgsFromVp(argc, vp);                                 \
     NativeObject* envobj = DebuggerEnv_checkThis(cx, args, fnname);           \
     if (!envobj)                                                              \
         return false;                                                         \
-    Rooted<Env*> env(cx, static_cast<Env*>(envobj->getPrivate()));           \
+    Rooted<Env*> env(cx, static_cast<Env*>(envobj->getPrivate()));            \
     MOZ_ASSERT(env);                                                          \
     MOZ_ASSERT(!IsSyntacticScope(env));
- 
+
  #define THIS_DEBUGENV_OWNER(cx, argc, vp, fnname, args, envobj, env, dbg)    \
      THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env);                  \
     Debugger* dbg = Debugger::fromChildJSObject(envobj)
 
 static bool
 DebuggerEnv_construct(JSContext* cx, unsigned argc, Value* vp)
 {
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -3775,32 +3775,35 @@ CASE(JSOP_PUSHBLOCKSCOPE)
         goto error;
 }
 END_CASE(JSOP_PUSHBLOCKSCOPE)
 
 CASE(JSOP_POPBLOCKSCOPE)
 {
 #ifdef DEBUG
     // Pop block from scope chain.
-    MOZ_ASSERT(*(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH) == JSOP_DEBUGLEAVEBLOCK);
-    NestedScopeObject* scope = script->getStaticBlockScope(REGS.pc - JSOP_DEBUGLEAVEBLOCK_LENGTH);
+    NestedScopeObject* scope = script->getStaticBlockScope(REGS.pc);
     MOZ_ASSERT(scope && scope->is<StaticBlockObject>());
     StaticBlockObject& blockObj = scope->as<StaticBlockObject>();
     MOZ_ASSERT(blockObj.needsClone());
 #endif
 
+    if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
+        DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc);
+
     // Pop block from scope chain.
     REGS.fp()->popBlock(cx);
 }
 END_CASE(JSOP_POPBLOCKSCOPE)
 
 CASE(JSOP_DEBUGLEAVEBLOCK)
 {
     MOZ_ASSERT(script->getStaticBlockScope(REGS.pc));
     MOZ_ASSERT(script->getStaticBlockScope(REGS.pc)->is<StaticBlockObject>());
+    MOZ_ASSERT(!script->getStaticBlockScope(REGS.pc)->as<StaticBlockObject>().needsClone());
 
     // FIXME: This opcode should not be necessary.  The debugger shouldn't need
     // help from bytecode to do its job.  See bug 927782.
 
     if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
         DebugScopes::onPopBlock(cx, REGS.fp(), REGS.pc);
 }
 END_CASE(JSOP_DEBUGLEAVEBLOCK)
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -585,67 +585,71 @@ FrameIter::settleOnActivation()
         MOZ_ASSERT(!data_.interpFrames_.frame()->runningInJit());
         data_.pc_ = data_.interpFrames_.pc();
         data_.state_ = INTERP;
         return;
     }
 }
 
 FrameIter::Data::Data(JSContext* cx, SavedOption savedOption,
-                      ContextOption contextOption, JSPrincipals* principals)
+                      ContextOption contextOption, DebuggerEvalOption debuggerEvalOption,
+                      JSPrincipals* principals)
   : cx_(cx),
     savedOption_(savedOption),
     contextOption_(contextOption),
+    debuggerEvalOption_(debuggerEvalOption),
     principals_(principals),
     pc_(nullptr),
     interpFrames_(nullptr),
     activations_(cx->runtime()),
     jitFrames_(),
     ionInlineFrameNo_(0),
     asmJSFrames_()
 {
 }
 
 FrameIter::Data::Data(const FrameIter::Data& other)
   : cx_(other.cx_),
     savedOption_(other.savedOption_),
     contextOption_(other.contextOption_),
+    debuggerEvalOption_(other.debuggerEvalOption_),
     principals_(other.principals_),
     state_(other.state_),
     pc_(other.pc_),
     interpFrames_(other.interpFrames_),
     activations_(other.activations_),
     jitFrames_(other.jitFrames_),
     ionInlineFrameNo_(other.ionInlineFrameNo_),
     asmJSFrames_(other.asmJSFrames_)
 {
 }
 
 FrameIter::FrameIter(JSContext* cx, SavedOption savedOption)
-  : data_(cx, savedOption, CURRENT_CONTEXT, nullptr),
+  : data_(cx, savedOption, CURRENT_CONTEXT, FOLLOW_DEBUGGER_EVAL_PREV_LINK, nullptr),
     ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 {
     // settleOnActivation can only GC if principals are given.
     JS::AutoSuppressGCAnalysis nogc;
     settleOnActivation();
 }
 
 FrameIter::FrameIter(JSContext* cx, ContextOption contextOption,
-                     SavedOption savedOption)
-  : data_(cx, savedOption, contextOption, nullptr),
+                     SavedOption savedOption, DebuggerEvalOption debuggerEvalOption)
+  : data_(cx, savedOption, contextOption, debuggerEvalOption, nullptr),
     ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 {
     // settleOnActivation can only GC if principals are given.
     JS::AutoSuppressGCAnalysis nogc;
     settleOnActivation();
 }
 
 FrameIter::FrameIter(JSContext* cx, ContextOption contextOption,
-                     SavedOption savedOption, JSPrincipals* principals)
-  : data_(cx, savedOption, contextOption, principals),
+                     SavedOption savedOption, DebuggerEvalOption debuggerEvalOption,
+                     JSPrincipals* principals)
+  : data_(cx, savedOption, contextOption, debuggerEvalOption, principals),
     ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 {
     settleOnActivation();
 }
 
 FrameIter::FrameIter(const FrameIter& other)
   : data_(other.data_),
     ionInlineFrames_(other.data_.cx_,
@@ -712,17 +716,20 @@ FrameIter::popAsmJSFrame()
 
 FrameIter&
 FrameIter::operator++()
 {
     switch (data_.state_) {
       case DONE:
         MOZ_CRASH("Unexpected state");
       case INTERP:
-        if (interpFrame()->isDebuggerEvalFrame() && interpFrame()->evalInFramePrev()) {
+        if (interpFrame()->isDebuggerEvalFrame() &&
+            interpFrame()->evalInFramePrev() &&
+            data_.debuggerEvalOption_ == FOLLOW_DEBUGGER_EVAL_PREV_LINK)
+        {
             AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
 
             // Eval-in-frame can cross contexts and works across saved frame
             // chains.
             ContextOption prevContextOption = data_.contextOption_;
             SavedOption prevSavedOption = data_.savedOption_;
             data_.contextOption_ = ALL_CONTEXTS;
             data_.savedOption_ = GO_THROUGH_SAVED;
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1581,46 +1581,50 @@ class AsmJSActivation : public Activatio
 //    interface marked below.
 //  - NonBuiltinScriptFrameIter additionally filters out builtin (self-hosted)
 //    scripts.
 class FrameIter
 {
   public:
     enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
     enum ContextOption { CURRENT_CONTEXT, ALL_CONTEXTS };
+    enum DebuggerEvalOption { FOLLOW_DEBUGGER_EVAL_PREV_LINK,
+                              IGNORE_DEBUGGER_EVAL_PREV_LINK };
     enum State { DONE, INTERP, JIT, ASMJS };
 
     // Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on
     // the heap, so this structure should not contain any GC things.
     struct Data
     {
         JSContext * cx_;
         SavedOption         savedOption_;
         ContextOption       contextOption_;
+        DebuggerEvalOption  debuggerEvalOption_;
         JSPrincipals *      principals_;
 
         State               state_;
 
         jsbytecode *        pc_;
 
         InterpreterFrameIterator interpFrames_;
         ActivationIterator activations_;
 
         jit::JitFrameIterator jitFrames_;
         unsigned ionInlineFrameNo_;
         AsmJSFrameIterator asmJSFrames_;
 
         Data(JSContext* cx, SavedOption savedOption, ContextOption contextOption,
-             JSPrincipals* principals);
+             DebuggerEvalOption debuggerEvalOption, JSPrincipals* principals);
         Data(const Data& other);
     };
 
     MOZ_IMPLICIT FrameIter(JSContext* cx, SavedOption = STOP_AT_SAVED);
-    FrameIter(JSContext* cx, ContextOption, SavedOption);
-    FrameIter(JSContext* cx, ContextOption, SavedOption, JSPrincipals*);
+    FrameIter(JSContext* cx, ContextOption, SavedOption,
+              DebuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK);
+    FrameIter(JSContext* cx, ContextOption, SavedOption, DebuggerEvalOption, JSPrincipals*);
     FrameIter(const FrameIter& iter);
     MOZ_IMPLICIT FrameIter(const Data& data);
     MOZ_IMPLICIT FrameIter(AbstractFramePtr frame);
 
     bool done() const { return data_.state_ == DONE; }
 
     // -------------------------------------------------------
     // The following functions can only be called when !done()
@@ -1751,27 +1755,29 @@ class ScriptFrameIter : public FrameIter
     explicit ScriptFrameIter(JSContext* cx, SavedOption savedOption = STOP_AT_SAVED)
       : FrameIter(cx, savedOption)
     {
         settle();
     }
 
     ScriptFrameIter(JSContext* cx,
                     ContextOption cxOption,
-                    SavedOption savedOption)
-      : FrameIter(cx, cxOption, savedOption)
+                    SavedOption savedOption,
+                    DebuggerEvalOption debuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK)
+      : FrameIter(cx, cxOption, savedOption, debuggerEvalOption)
     {
         settle();
     }
 
     ScriptFrameIter(JSContext* cx,
                     ContextOption cxOption,
                     SavedOption savedOption,
+                    DebuggerEvalOption debuggerEvalOption,
                     JSPrincipals* prin)
-      : FrameIter(cx, cxOption, savedOption, prin)
+      : FrameIter(cx, cxOption, savedOption, debuggerEvalOption, prin)
     {
         settle();
     }
 
     ScriptFrameIter(const ScriptFrameIter& iter) : FrameIter(iter) { settle(); }
     explicit ScriptFrameIter(const FrameIter::Data& data) : FrameIter(data) { settle(); }
     explicit ScriptFrameIter(AbstractFramePtr frame) : FrameIter(frame) { settle(); }
 
@@ -1802,27 +1808,30 @@ class NonBuiltinFrameIter : public Frame
                                  FrameIter::SavedOption opt = FrameIter::STOP_AT_SAVED)
       : FrameIter(cx, opt)
     {
         settle();
     }
 
     NonBuiltinFrameIter(JSContext* cx,
                         FrameIter::ContextOption contextOption,
-                        FrameIter::SavedOption savedOption)
-      : FrameIter(cx, contextOption, savedOption)
+                        FrameIter::SavedOption savedOption,
+                        FrameIter::DebuggerEvalOption debuggerEvalOption =
+                        FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK)
+      : FrameIter(cx, contextOption, savedOption, debuggerEvalOption)
     {
         settle();
     }
 
     NonBuiltinFrameIter(JSContext* cx,
                         FrameIter::ContextOption contextOption,
                         FrameIter::SavedOption savedOption,
+                        FrameIter::DebuggerEvalOption debuggerEvalOption,
                         JSPrincipals* principals)
-      : FrameIter(cx, contextOption, savedOption, principals)
+      : FrameIter(cx, contextOption, savedOption, debuggerEvalOption, principals)
     {
         settle();
     }
 
     explicit NonBuiltinFrameIter(const FrameIter::Data& data)
       : FrameIter(data)
     {}
 
@@ -1844,27 +1853,30 @@ class NonBuiltinScriptFrameIter : public
                                        ScriptFrameIter::STOP_AT_SAVED)
       : ScriptFrameIter(cx, opt)
     {
         settle();
     }
 
     NonBuiltinScriptFrameIter(JSContext* cx,
                               ScriptFrameIter::ContextOption contextOption,
-                              ScriptFrameIter::SavedOption savedOption)
-      : ScriptFrameIter(cx, contextOption, savedOption)
+                              ScriptFrameIter::SavedOption savedOption,
+                              ScriptFrameIter::DebuggerEvalOption debuggerEvalOption =
+                              ScriptFrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK)
+      : ScriptFrameIter(cx, contextOption, savedOption, debuggerEvalOption)
     {
         settle();
     }
 
     NonBuiltinScriptFrameIter(JSContext* cx,
                               ScriptFrameIter::ContextOption contextOption,
                               ScriptFrameIter::SavedOption savedOption,
+                              ScriptFrameIter::DebuggerEvalOption debuggerEvalOption,
                               JSPrincipals* principals)
-      : ScriptFrameIter(cx, contextOption, savedOption, principals)
+      : ScriptFrameIter(cx, contextOption, savedOption, debuggerEvalOption, principals)
     {
         settle();
     }
 
     explicit NonBuiltinScriptFrameIter(const ScriptFrameIter::Data& data)
       : ScriptFrameIter(data)
     {}
 
@@ -1878,17 +1890,18 @@ class NonBuiltinScriptFrameIter : public
 /*
  * Blindly iterate over all frames in the current thread's stack. These frames
  * can be from different contexts and compartments, so beware.
  */
 class AllFramesIter : public ScriptFrameIter
 {
   public:
     explicit AllFramesIter(JSContext* cx)
-      : ScriptFrameIter(cx, ScriptFrameIter::ALL_CONTEXTS, ScriptFrameIter::GO_THROUGH_SAVED)
+      : ScriptFrameIter(cx, ScriptFrameIter::ALL_CONTEXTS, ScriptFrameIter::GO_THROUGH_SAVED,
+                        ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK)
     {}
 };
 
 /* Popular inline definitions. */
 
 inline JSScript*
 FrameIter::script() const
 {
--- a/js/xpconnect/idl/nsIScriptError.idl
+++ b/js/xpconnect/idl/nsIScriptError.idl
@@ -6,32 +6,35 @@
 /*
  * nsIConsoleMessage subclass for representing JavaScript errors and warnings.
  */
 
 
 #include "nsISupports.idl"
 #include "nsIConsoleMessage.idl"
 
-[scriptable, uuid(d6a8dae2-367f-4939-a843-11e0c48e240c)]
+[scriptable, uuid(248b2c94-2736-4d29-bfdf-bc64a2e60d35)]
 interface nsIScriptError : nsIConsoleMessage
 {
     /** pseudo-flag for default case */
     const unsigned long errorFlag = 0x0;
 
     /** message is warning */
     const unsigned long warningFlag = 0x1;
 
     /** exception was thrown for this case - exception-aware hosts can ignore */
     const unsigned long exceptionFlag = 0x2;
 
     // XXX check how strict is implemented these days.
     /** error or warning is due to strict option */
     const unsigned long strictFlag = 0x4;
 
+    /** just a log message */
+    const unsigned long infoFlag = 0x8;
+
     /**
      * The error message without any context/line number information.
      *
      * @note nsIConsoleMessage.message will return the error formatted
      *       with file/line information.
      */
     readonly attribute AString errorMessage;
 
--- a/js/xpconnect/src/nsScriptError.cpp
+++ b/js/xpconnect/src/nsScriptError.cpp
@@ -84,18 +84,23 @@ nsScriptError::GetMessageMoz(char16_t** 
 
     return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsScriptError::GetLogLevel(uint32_t* aLogLevel)
 {
-  *aLogLevel = mFlags & (uint32_t)nsIScriptError::errorFlag ?
-               nsIConsoleMessage::error : nsIConsoleMessage::warn;
+  if (mFlags & (uint32_t)nsIScriptError::infoFlag) {
+    *aLogLevel = nsIConsoleMessage::info;
+  } else if (mFlags & (uint32_t)nsIScriptError::warningFlag) {
+    *aLogLevel = nsIConsoleMessage::warn;
+  } else {
+    *aLogLevel = nsIConsoleMessage::error;
+  }
   return NS_OK;
 }
 
 // nsIScriptError methods
 NS_IMETHODIMP
 nsScriptError::GetErrorMessage(nsAString& aResult) {
     aResult.Assign(mMessage);
     return NS_OK;
--- a/layout/xul/test/browser_bug685470.js
+++ b/layout/xul/test/browser_bug685470.js
@@ -1,33 +1,19 @@
 add_task(function* () {
   const html = "<p id=\"p1\" title=\"tooltip is here\">This paragraph has a tooltip.</p>";
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, "data:text/html," + html);
 
   yield new Promise(resolve => {
     SpecialPowers.pushPrefEnv({"set": [["ui.tooltipDelay", 0]]}, resolve);
   });
 
-  var popup = false;
-  var doc;
-  var win;
-  var p1;
-
-  let onPopupShown = function(aEvent) {
-    popup = true;
-  }
-  document.addEventListener("popupshown", onPopupShown, true);
+  yield BrowserTestUtils.synthesizeMouseAtCenter("#p1", { type: "mousemove" },
+                                                 gBrowser.selectedBrowser);
+  yield BrowserTestUtils.synthesizeMouseAtCenter("#p1", { }, gBrowser.selectedBrowser);
 
-  // Send a mousemove at a known position to start the test.
-  BrowserTestUtils.synthesizeMouseAtCenter("#p1", { type: "mousemove" },
-                                           gBrowser.selectedBrowser);
-  BrowserTestUtils.synthesizeMouseAtCenter("#p1", { }, gBrowser.selectedBrowser);
+  // Wait until the tooltip timeout triggers that would normally have opened the popup.
+  yield new Promise(resolve => setTimeout(resolve, 0));
+  is(document.getElementById("aHTMLTooltip").state, "closed", "local tooltip is closed");
+  is(document.getElementById("remoteBrowserTooltip").state, "closed", "remote tooltip is closed");
 
-  yield new Promise(resolve => {
-    setTimeout(function() {
-      is(popup, false, "shouldn't get tooltip after click");
-      resolve();
-    }, 200);
-  });
-
-  document.removeEventListener("popupshown", onPopupShown, true);
   gBrowser.removeCurrentTab();
 });
--- a/python/mozbuild/mozbuild/test/test_testing.py
+++ b/python/mozbuild/mozbuild/test/test_testing.py
@@ -122,17 +122,45 @@ ALL_TESTS_JSON = b'''
             "flavor": "instrumentation",
             "here": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/browser/junit3",
             "manifest": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/browser/junit3/instrumentation.ini",
             "name": "src/TestDistribution.java",
             "path": "/Users/nalexander/Mozilla/gecko-dev/mobile/android/tests/browser/junit3/src/TestDistribution.java",
             "relpath": "src/TestDistribution.java",
             "subsuite": "browser"
         }
-    ]
+    ],
+    "image/test/browser/browser_bug666317.js": [
+        {
+            "dir_relpath": "image/test/browser",
+            "file_relpath": "image/test/browser/browser_bug666317.js",
+            "flavor": "browser-chrome",
+            "here": "/home/chris/m-c/obj-dbg/_tests/testing/mochitest/browser/image/test/browser",
+            "manifest": "/home/chris/m-c/image/test/browser/browser.ini",
+            "name": "browser_bug666317.js",
+            "path": "/home/chris/m-c/obj-dbg/_tests/testing/mochitest/browser/image/test/browser/browser_bug666317.js",
+            "relpath": "image/test/browser/browser_bug666317.js",
+            "skip-if": "e10s # Bug 948194 - Decoded Images seem to not be discarded on memory-pressure notification with e10s enabled",
+            "subsuite": ""
+        }
+   ],
+   "browser/devtools/markupview/test/browser_markupview_copy_image_data.js": [
+        {
+            "dir_relpath": "browser/devtools/markupview/test",
+            "file_relpath": "browser/devtools/markupview/test/browser_markupview_copy_image_data.js",
+            "flavor": "browser-chrome",
+            "here": "/home/chris/m-c/obj-dbg/_tests/testing/mochitest/browser/browser/devtools/markupview/test",
+            "manifest": "/home/chris/m-c/browser/devtools/markupview/test/browser.ini",
+            "name": "browser_markupview_copy_image_data.js",
+            "path": "/home/chris/m-c/obj-dbg/_tests/testing/mochitest/browser/browser/devtools/markupview/test/browser_markupview_copy_image_data.js",
+            "relpath": "browser/devtools/markupview/test/browser_markupview_copy_image_data.js",
+            "subsuite": "devtools",
+            "tags": "devtools"
+        }
+   ]
 }'''.strip()
 
 
 class Base(unittest.TestCase):
     def setUp(self):
         self._temp_files = []
 
     def tearDown(self):
@@ -148,24 +176,24 @@ class Base(unittest.TestCase):
         self._temp_files.append(f)
 
         return TestMetadata(filename=f.name)
 
 
 class TestTestMetadata(Base):
     def test_load(self):
         t = self._get_test_metadata()
-        self.assertEqual(len(t._tests_by_path), 6)
+        self.assertEqual(len(t._tests_by_path), 8)
 
         self.assertEqual(len(list(t.tests_with_flavor('xpcshell'))), 3)
         self.assertEqual(len(list(t.tests_with_flavor('mochitest-plain'))), 0)
 
     def test_resolve_all(self):
         t = self._get_test_metadata()
-        self.assertEqual(len(list(t.resolve_tests())), 7)
+        self.assertEqual(len(list(t.resolve_tests())), 9)
 
     def test_resolve_filter_flavor(self):
         t = self._get_test_metadata()
         self.assertEqual(len(list(t.resolve_tests(flavor='xpcshell'))), 4)
 
     def test_resolve_by_dir(self):
         t = self._get_test_metadata()
         self.assertEqual(len(list(t.resolve_tests(paths=['services/common']))), 2)
@@ -177,16 +205,21 @@ class TestTestMetadata(Base):
         self.assertEqual(len(list(t.resolve_tests(flavor='xpcshell',
             under_path='services'))), 2)
 
     def test_resolve_multiple_paths(self):
         t = self._get_test_metadata()
         result = list(t.resolve_tests(paths=['services', 'toolkit']))
         self.assertEqual(len(result), 4)
 
+    def test_resolve_path_prefix(self):
+        t = self._get_test_metadata()
+        result = list(t.resolve_tests(paths=['image']))
+        self.assertEqual(len(result), 1)
+
 
 class TestTestResolver(Base):
     FAKE_TOPSRCDIR = '/Users/gps/src/firefox'
 
     def setUp(self):
         Base.setUp(self)
 
         self._temp_dirs = []
--- a/python/mozbuild/mozbuild/testing.py
+++ b/python/mozbuild/mozbuild/testing.py
@@ -75,18 +75,19 @@ class TestMetadata(object):
     def resolve_tests(self, paths=None, flavor=None, subsuite=None, under_path=None):
         """Resolve tests from an identifier.
 
         This is a generator of dicts describing each test.
 
         ``paths`` can be an iterable of values to use to identify tests to run.
         If an entry is a known test file, tests associated with that file are
         returned (there may be multiple configurations for a single file). If
-        an entry is a directory, all tests in that directory are returned. If
-        the string appears in a known test file, that test file is considered.
+        an entry is a directory, or a prefix of a directory containing tests,
+        all tests in that directory are returned. If the string appears in a
+        known test file, that test file is considered.
 
         If ``under_path`` is a string, it will be used to filter out tests that
         aren't in the specified path prefix relative to topsrcdir or the
         test's installed dir.
 
         If ``flavor`` is a string, it will be used to filter returned tests
         to only be the flavor specified. A flavor is something like
         ``xpcshell``.
@@ -118,18 +119,20 @@ class TestMetadata(object):
 
         candidate_paths = set()
 
         for path in sorted(paths):
             if path is None:
                 candidate_paths |= set(self._tests_by_path.keys())
                 continue
 
-            # If the path is a directory, pull in all tests in that directory.
-            if path in self._test_dirs:
+            # If the path is a directory, or the path is a prefix of a directory
+            # containing tests, pull in all tests in that directory.
+            if (path in self._test_dirs or
+                any(p.startswith(path) for p in self._tests_by_path)):
                 candidate_paths |= {p for p in self._tests_by_path
                                     if p.startswith(path)}
                 continue
 
             # If it's a test file, add just that file.
             candidate_paths |= {p for p in self._tests_by_path if path in p}
 
         for p in sorted(candidate_paths):
--- a/services/mobileid/MobileIdentityManager.jsm
+++ b/services/mobileid/MobileIdentityManager.jsm
@@ -166,30 +166,44 @@ this.MobileIdentityManager = {
 
     // _iccInfo is a local cache containing the information about the SIM cards
     // that is interesting for the Mobile ID flow.
     // The index of this array does not necesarily need to match the real
     // identifier of the SIM card ("clientId" or "serviceId" in RIL language).
     this._iccInfo = [];
 
     for (let i = 0; i < this.ril.numRadioInterfaces; i++) {
-      let rilContext = this.ril.getRadioInterface(i).rilContext;
-      if (!rilContext) {
-        log.warn("Tried to get the RIL context for an invalid service ID " + i);
+      let icc = this.iccService.getIccByServiceId(i);
+      if (!icc) {
+        log.warn("Tried to get the Icc instance for an invalid service ID " + i);
         continue;
       }
 
-      let info = rilContext.iccInfo;
+      let info = icc.iccInfo;
       if (!info || !info.iccid ||
           !info.mcc || !info.mcc.length ||
           !info.mnc || !info.mnc.length) {
         log.warn("Absent or invalid ICC info");
         continue;
       }
 
+      // GSM SIMs may have MSISDN while CDMA SIMs may have MDN
+      let phoneNumber = null;
+      try {
+        if (info.iccType === "sim" || info.iccType === "usim") {
+          let gsmInfo = info.QueryInterface(Ci.nsIGsmIccInfo);
+          phoneNumber = gsmInfo.msisdn;
+        } else if (info.iccType === "ruim" || info.iccType === "csim") {
+          let cdmaInfo = info.QueryInterface(Ci.nsICdmaIccInfo);
+          phoneNumber = cdmaInfo.mdn;
+        }
+      } catch (e) {
+        log.error("Failed to retrieve phoneNumber: " + e);
+      }
+
       let connection = this.mobileConnectionService.getItemByServiceId(i);
       let voice = connection && connection.voice;
       let data = connection && connection.data;
       let operator = null;
       if (voice &&
           voice.network &&
           voice.network.shortName &&
           voice.network.shortName.length) {
@@ -203,28 +217,24 @@ this.MobileIdentityManager = {
 
       this._iccInfo.push({
         // Because it is possible that the _iccInfo array index doesn't match
         // the real client ID, we need to store this value for later usage.
         clientId: i,
         iccId: info.iccid,
         mcc: info.mcc,
         mnc: info.mnc,
-        // GSM SIMs may have MSISDN while CDMA SIMs may have MDN
-        msisdn: info.msisdn || info.mdn || null,
+        msisdn: phoneNumber,
         operator: operator,
         roaming: voice && voice.roaming
       });
 
       // We need to subscribe to ICC change notifications so we can refresh
       // the cache if any change is observed.
-      let icc = this.iccService.getIccByServiceId(i);
-      if (icc) {
-        icc.registerListener(iccListener);
-      }
+      icc.registerListener(iccListener);
     }
 
     return this._iccInfo;
 #endif
     return null;
   },
 
   get iccIds() {
--- a/services/mobileid/tests/xpcshell/head.js
+++ b/services/mobileid/tests/xpcshell/head.js
@@ -41,71 +41,74 @@ const SESSION_TOKEN = "aSessionToken";
 const ICC_ID = "aIccId";
 const ANOTHER_ICC_ID = "anotherIccId";
 const MNC = "aMnc";
 const ANOTHER_MNC = "anotherMnc";
 const MCC = "aMcc";
 const ANOTHER_MCC = "anotherMcc";
 const OPERATOR = "aOperator";
 const ANOTHER_OPERATOR = "anotherOperator";
+const ICC_INFO = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
+                                         Ci.nsIIccInfo]),
+  iccType: "usim",
+  iccid: ICC_ID,
+  mcc: MCC,
+  mnc: MNC,
+  msisdn: PHONE_NUMBER,
+  operator: OPERATOR
+};
+const ANOTHER_ICC_INFO = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
+                                         Ci.nsIIccInfo]),
+  iccType: "usim",
+  iccid: ANOTHER_ICC_ID,
+  mcc: ANOTHER_MCC,
+  mnc: ANOTHER_MNC,
+  msisdn: ANOTHER_PHONE_NUMBER,
+  operator: ANOTHER_OPERATOR
+};
+const INVALID_ICC_INFO = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
+                                         Ci.nsIIccInfo]),
+  iccType: "usim",
+  iccid: null,
+  mcc: "",
+  mnc: "",
+  msisdn: "",
+  operator: ""
+};
 const RADIO_INTERFACE = {
-  rilContext: {
-    iccInfo: {
-      iccid: ICC_ID,
-      mcc: MCC,
-      mnc: MNC,
-      msisdn: PHONE_NUMBER,
-      operator: OPERATOR
-    }
-  },
   voice: {
     network: {
       shortName: OPERATOR
     },
     roaming: false
   },
   data: {
     network: {
       shortName: OPERATOR
     }
   }
 };
 const ANOTHER_RADIO_INTERFACE = {
-  rilContext: {
-    iccInfo: {
-      iccid: ANOTHER_ICC_ID,
-      mcc: ANOTHER_MCC,
-      mnc: ANOTHER_MNC,
-      msisdn: ANOTHER_PHONE_NUMBER,
-      operator: ANOTHER_OPERATOR
-    }
-  },
   voice: {
     network: {
       shortName: ANOTHER_OPERATOR
     },
     roaming: false
   },
   data: {
     network: {
       shortName: ANOTHER_OPERATOR
     }
   }
 };
 
 const INVALID_RADIO_INTERFACE = {
-  rilContext: {
-    iccInfo: {
-      iccid: null,
-      mcc: "",
-      mnc: "",
-      msisdn: "",
-      operator: ""
-    }
-  },
   voice: {
     network: {
       shortName: ""
     },
     roaming: undefined
   },
   data: {
     network: {
--- a/services/mobileid/tests/xpcshell/test_mobileid_manager.js
+++ b/services/mobileid/tests/xpcshell/test_mobileid_manager.js
@@ -998,43 +998,45 @@ add_test(function() {
   MobileIdentityManager._mobileConnectionService = {
     _interfaces: [RADIO_INTERFACE, ANOTHER_RADIO_INTERFACE],
     getItemByServiceId: function(aIndex) {
       return this._interfaces[aIndex];
     }
   };
 
   MobileIdentityManager._iccService = {
-    _iccs: [],
     _listeners: [],
+    _iccInfos: [ICC_INFO, ANOTHER_ICC_INFO],
     getIccByServiceId: function(aClientId) {
       let self = this;
-      this_iccs.push({
+      return {
+        get iccInfo() {
+          return self._iccInfos[aClientId];
+        },
         registerListener: function(aIccListener) {
           self._listeners.push(aIccListener);
         },
         unregisterListener: function() {
           self._listeners.pop();
         }
-      });
+      };
     }
   };
 
   let ui = new MockUi();
   ui.startFlow = function() {
     // At this point we've already built the ICC cache.
-    let interfaces = MobileIdentityManager._ril._interfaces;
-    for (let i = 0; i < interfaces.length; i++) {
-      let interfaceIccInfo = interfaces[i].rilContext.iccInfo;
+    let mockIccInfo = [ICC_INFO, ANOTHER_ICC_INFO];
+    for (let i = 0; i < mockIccInfo.length; i++) {
       let mIdIccInfo = MobileIdentityManager._iccInfo[i];
-      do_check_eq(interfaceIccInfo.iccid, mIdIccInfo.iccId);
-      do_check_eq(interfaceIccInfo.mcc, mIdIccInfo.mcc);
-      do_check_eq(interfaceIccInfo.mnc, mIdIccInfo.mnc);
-      do_check_eq(interfaceIccInfo.msisdn, mIdIccInfo.msisdn);
-      do_check_eq(interfaceIccInfo.operator, mIdIccInfo.operator);
+      do_check_eq(mockIccInfo[i].iccid, mIdIccInfo.iccId);
+      do_check_eq(mockIccInfo[i].mcc, mIdIccInfo.mcc);
+      do_check_eq(mockIccInfo[i].mnc, mIdIccInfo.mnc);
+      do_check_eq(mockIccInfo[i].msisdn, mIdIccInfo.msisdn);
+      do_check_eq(mockIccInfo[i].operator, mIdIccInfo.operator);
     }
 
     // We should have listeners for each valid icc.
     do_check_eq(MobileIdentityManager._iccService._listeners.length, 2);
 
     // We can mock an ICC change event at this point.
     MobileIdentityManager._iccService._listeners[0].notifyIccInfoChanged();
 
@@ -1106,28 +1108,31 @@ add_test(function() {
   MobileIdentityManager._mobileConnectionService = {
     _interfaces: [INVALID_RADIO_INTERFACE],
     getItemByServiceId: function(aIndex) {
       return this._interfaces[aIndex];
     }
   };
 
   MobileIdentityManager._iccService = {
-    _iccs: [],
     _listeners: [],
+    _iccInfos: [INVALID_ICC_INFO],
     getIccByServiceId: function(aClientId) {
       let self = this;
-      this_iccs.push({
+      return {
+        get iccInfo() {
+          return self._iccInfos[aClientId];
+        },
         registerListener: function(aIccListener) {
           self._listeners.push(aIccListener);
         },
         unregisterListener: function() {
           self._listeners.pop();
         }
-      });
+      };
     }
   };
 
   let ui = new MockUi();
   ui.startFlow = function() {
     // At this point we've already built the ICC cache.
     do_check_eq(MobileIdentityManager._iccInfo.length, 0);
     do_check_eq(MobileIdentityManager._iccIds.length, 0);
--- a/testing/taskcluster/tasks/builds/b2g_emulator_ics_debug.yml
+++ b/testing/taskcluster/tasks/builds/b2g_emulator_ics_debug.yml
@@ -1,15 +1,16 @@
 $inherits:
   from: 'tasks/builds/b2g_emulator_base.yml'
   variables:
     build_name: 'emulator-ics'
     build_type: 'debug'
 task:
   workerType: emulator-ics-debug
+  provisionerId: aws-provisioner-v1
   scopes:
     - 'docker-worker:cache:workspace-emulator-ics-debug'
   metadata:
     name: '[TC] B2G Emulator ICS Debug'
   extra:
     treeherder:
       collection:
         debug: true
--- a/testing/taskcluster/tasks/builds/b2g_emulator_ics_opt.yml
+++ b/testing/taskcluster/tasks/builds/b2g_emulator_ics_opt.yml
@@ -1,15 +1,16 @@
 $inherits:
   from: 'tasks/builds/b2g_emulator_base.yml'
   variables:
     build_type: 'opt'
     build_name: 'emulator-ics'
 task:
   workerType: emulator-ics
+  provisionerId: aws-provisioner-v1
   routes:
     - 'index.buildbot.branches.{{project}}.emulator-ics'
     - 'index.buildbot.revisions.{{head_rev}}.{{project}}.emulator-ics'
   scopes:
     - 'docker-worker:cache:workspace-emulator-ics-opt'
   metadata:
     name: '[TC] B2G Emulator ICS Opt'
 
--- a/testing/taskcluster/tasks/builds/b2g_emulator_x86_kk_opt.yml
+++ b/testing/taskcluster/tasks/builds/b2g_emulator_x86_kk_opt.yml
@@ -1,15 +1,16 @@
 $inherits:
   from: 'tasks/builds/b2g_emulator_x86_base.yml'
   variables:
     build_name: 'emulator-x86-kk'
     build_type: 'opt'
 task:
   workerType: emualtor-x86-kk
+  provisionerId: aws-provisioner-v1
   scopes:
     - 'docker-worker:cache:workspace-emulator-kk-x86-opt'
   metadata:
     name: '[TC] B2G KK X86 Emulator (Opt)'
 
   extra:
     treeherderEnv:
       - staging
--- a/testing/taskcluster/tasks/tests/b2g_emulator_cpp_unit.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_cpp_unit.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] CPP Unit Tests'
     description: CPP Unit Tests test run
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
         --config-file ./mozharness_configs/remove_executables.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_crashtest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_crashtest.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] Crashtest'
     description: Crashtest test run {{chunk}}
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     maxRunTime: 3600
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_js_reftest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_js_reftest.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] JSReftest'
     description: JSReftest test run {{chunk}}
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
         --config-file ./mozharness_configs/remove_executables.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_marionette.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_marionette.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] Marionette Framework Unit Tests'
     description: Marionette Framework Unit Tests test run
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/marionette.py
         --no-read-buildbot-config
         --config-file ./mozharness/configs/marionette/automation_emulator_config.py
         --config-file ./mozharness_configs/remove_executables.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_marionette_webapi.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_marionette_webapi.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] Marionette WebAPI Tests'
     description: Marionette WebAPI test run
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/marionette.py
         --no-read-buildbot-config
         --config-file ./mozharness/configs/marionette/automation_emulator_config.py
         --config-file ./mozharness_configs/remove_executables.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_mochitest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_mochitest.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] Mochitest'
     description: Mochitest test run {{chunk}}
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     maxRunTime: 7200
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_mochitest_media.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_mochitest_media.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] Mochitest'
     description: Mochitest Media test run
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
         --config-file ./mozharness_configs/emulator_override.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_reftest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_reftest.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] Reftest'
     description: Reftest test run {{chunk}}
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
         --config-file ./mozharness_configs/remove_executables.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_xpcshell.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_xpcshell.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] XPCShell'
     description: XPCShell test run
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     maxRunTime: 6000
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
--- a/testing/taskcluster/tasks/tests/b2g_emulator_xpcshell_chunked.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_xpcshell_chunked.yml
@@ -2,16 +2,17 @@
 $inherits:
   from: 'tasks/test.yml'
 task:
   metadata:
     name: '[TC] XPCShell'
     description: XPCShell test run {{chunk}}
 
   workerType: b2gtest-emulator
+  provisionerId: aws-provisioner-v1
   payload:
     maxRunTime: 6000
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -1297,67 +1297,107 @@ function do_send_remote_message(name) {
     sender = 'sendAsyncMessage';
   }
   mm[sender](name);
 }
 
 /**
  * Add a test function to the list of tests that are to be run asynchronously.
  *
+ * @param funcOrProperties
+ *        A function to be run or an object represents test properties.
+ *        Supported properties:
+ *          skip_if : An arrow function which has an expression to be
+ *                    evaluated whether the test is skipped or not.
+ * @param func
+ *        A function to be run only if the funcOrProperies is not a function.
+ *
  * Each test function must call run_next_test() when it's done. Test files
  * should call run_next_test() in their run_test function to execute all
  * async tests.
  *
  * @return the test function that was passed in.
  */
 let _gTests = [];
-function add_test(func) {
-  _gTests.push([false, func]);
+function add_test(funcOrProperties, func) {
+  if (typeof funcOrProperties == "function") {
+    _gTests.push([{ _isTask: false }, funcOrProperties]);
+  } else if (typeof funcOrProperties == "object") {
+    funcOrProperties._isTask = false;
+    _gTests.push([funcOrProperties, func]);
+  } else {
+    do_throw("add_test() should take a function or an object and a function");
+  }
   return func;
 }
 
 /**
  * Add a test function which is a Task function.
  *
+ * @param funcOrProperties
+ *        A generator function to be run or an object represents test
+ *        properties.
+ *        Supported properties:
+ *          skip_if : An arrow function which has an expression to be
+ *                    evaluated whether the test is skipped or not.
+ * @param func
+ *        A generator function to be run only if the funcOrProperies is not a
+ *        function.
+ *
  * Task functions are functions fed into Task.jsm's Task.spawn(). They are
  * generators that emit promises.
  *
  * If an exception is thrown, a do_check_* comparison fails, or if a rejected
  * promise is yielded, the test function aborts immediately and the test is
  * reported as a failure.
  *
  * Unlike add_test(), there is no need to call run_next_test(). The next test
  * will run automatically as soon the task function is exhausted. To trigger
  * premature (but successful) termination of the function, simply return or
  * throw a Task.Result instance.
  *
  * Example usage:
  *
- * add_task(function test() {
+ * add_task(function* test() {
  *   let result = yield Promise.resolve(true);
  *
  *   do_check_true(result);
  *
  *   let secondary = yield someFunctionThatReturnsAPromise(result);
  *   do_check_eq(secondary, "expected value");
  * });
  *
- * add_task(function test_early_return() {
+ * add_task(function* test_early_return() {
  *   let result = yield somethingThatReturnsAPromise();
  *
  *   if (!result) {
  *     // Test is ended immediately, with success.
  *     return;
  *   }
  *
  *   do_check_eq(result, "foo");
  * });
+ *
+ * add_task({
+ *   skip_if: () => !("@mozilla.org/telephony/volume-service;1" in Components.classes),
+ * }, function* test_volume_service() {
+ *   let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
+ *     .getService(Ci.nsIVolumeService);
+ *   ...
+ * });
  */
-function add_task(func) {
-  _gTests.push([true, func]);
+function add_task(funcOrProperties, func) {
+  if (typeof funcOrProperties == "function") {
+    _gTests.push([{ _isTask: true }, funcOrProperties]);
+  } else if (typeof funcOrProperties == "object") {
+    funcOrProperties._isTask = true;
+    _gTests.push([funcOrProperties, func]);
+  } else {
+    do_throw("add_task() should take a function or an object and a function");
+  }
 }
 let _Task = Components.utils.import("resource://gre/modules/Task.jsm", {}).Task;
 _Task.Debugging.maintainStack = true;
 
 
 /**
  * Runs the next test function from the list of async tests.
  */
@@ -1372,22 +1412,35 @@ function run_next_test()
                     "under any circumstances!");
   }
  
   function _run_next_test()
   {
     if (_gTestIndex < _gTests.length) {
       // Flush uncaught errors as early and often as possible.
       _Promise.Debugging.flushUncaughtErrors();
-      let _isTask;
-      [_isTask, _gRunningTest] = _gTests[_gTestIndex++];
+      let _properties;
+      [_properties, _gRunningTest,] = _gTests[_gTestIndex++];
+      if (typeof(_properties.skip_if) == "function" && _properties.skip_if()) {
+        let _condition = _properties.skip_if.toSource().replace(/\(\)\s*=>\s*/, "");
+        let _message = _gRunningTest.name
+          + " skipped because the following conditions were"
+          + " met: (" + _condition + ")";
+        _testLogger.testStatus(_TEST_NAME,
+                               _gRunningTest.name,
+                               "SKIP",
+                               "SKIP",
+                               _message);
+        do_execute_soon(run_next_test);
+        return;
+      }
       _testLogger.info(_TEST_NAME + " | Starting " + _gRunningTest.name);
       do_test_pending(_gRunningTest.name);
 
-      if (_isTask) {
+      if (_properties._isTask) {
         _gTaskRunning = true;
         _Task.spawn(_gRunningTest).then(
           () => { _gTaskRunning = false; run_next_test(); },
           (ex) => { _gTaskRunning = false; do_report_unexpected_exception(ex); }
         );
       } else {
         // Exceptions do not kill asynchronous tests, so they'll time out.
         try {
--- a/testing/xpcshell/selftest.py
+++ b/testing/xpcshell/selftest.py
@@ -542,16 +542,102 @@ tail =
         self.assertTestResult(True)
         self.assertEquals(1, self.x.testCount)
         self.assertEquals(1, self.x.passCount)
         self.assertEquals(0, self.x.failCount)
         self.assertEquals(0, self.x.todoCount)
         self.assertInLog(TEST_PASS_STRING)
         self.assertNotInLog(TEST_FAIL_STRING)
 
+    def testSkipForAddTest(self):
+        """
+        Check that add_test is skipped if |skip_if| condition is true
+        """
+        self.writeFile("test_skip.js", """
+add_test({
+  skip_if: () => true,
+}, function test_should_be_skipped() {
+  do_check_true(false);
+  run_next_test();
+});
+""")
+        self.writeManifest(["test_skip.js"])
+        self.assertTestResult(True, verbose=True)
+        self.assertEquals(1, self.x.testCount)
+        self.assertEquals(1, self.x.passCount)
+        self.assertEquals(0, self.x.failCount)
+        self.assertEquals(0, self.x.todoCount)
+        self.assertInLog(TEST_PASS_STRING)
+        self.assertInLog("TEST-SKIP")
+        self.assertNotInLog(TEST_FAIL_STRING)
+
+    def testNotSkipForAddTask(self):
+        """
+        Check that add_task is not skipped if |skip_if| condition is false
+        """
+        self.writeFile("test_not_skip.js", """
+add_task({
+  skip_if: () => false,
+}, function test_should_not_be_skipped() {
+  do_check_true(true);
+});
+""")
+        self.writeManifest(["test_not_skip.js"])
+        self.assertTestResult(True, verbose=True)
+        self.assertEquals(1, self.x.testCount)
+        self.assertEquals(1, self.x.passCount)
+        self.assertEquals(0, self.x.failCount)
+        self.assertEquals(0, self.x.todoCount)
+        self.assertInLog(TEST_PASS_STRING)
+        self.assertNotInLog("TEST-SKIP")
+        self.assertNotInLog(TEST_FAIL_STRING)
+
+    def testSkipForAddTask(self):
+        """
+        Check that add_task is skipped if |skip_if| condition is true
+        """
+        self.writeFile("test_skip.js", """
+add_task({
+  skip_if: () => true,
+}, function test_should_be_skipped() {
+  do_check_true(false);
+});
+""")
+        self.writeManifest(["test_skip.js"])
+        self.assertTestResult(True, verbose=True)
+        self.assertEquals(1, self.x.testCount)
+        self.assertEquals(1, self.x.passCount)
+        self.assertEquals(0, self.x.failCount)
+        self.assertEquals(0, self.x.todoCount)
+        self.assertInLog(TEST_PASS_STRING)
+        self.assertInLog("TEST-SKIP")
+        self.assertNotInLog(TEST_FAIL_STRING)
+
+    def testNotSkipForAddTest(self):
+        """
+        Check that add_test is not skipped if |skip_if| condition is false
+        """
+        self.writeFile("test_not_skip.js", """
+add_test({
+  skip_if: () => false,
+}, function test_should_not_be_skipped() {
+  do_check_true(true);
+  run_next_test();
+});
+""")
+        self.writeManifest(["test_not_skip.js"])
+        self.assertTestResult(True, verbose=True)
+        self.assertEquals(1, self.x.testCount)
+        self.assertEquals(1, self.x.passCount)
+        self.assertEquals(0, self.x.failCount)
+        self.assertEquals(0, self.x.todoCount)
+        self.assertInLog(TEST_PASS_STRING)
+        self.assertNotInLog("TEST-SKIP")
+        self.assertNotInLog(TEST_FAIL_STRING)
+
     def testSyntaxError(self):
         """
         Check that running a test file containing a syntax error produces
         a test failure and expected output.
         """
         self.writeFile("test_syntax_error.js", '"')
         self.writeManifest(["test_syntax_error.js"])
 
--- a/toolkit/components/console/content/console.css
+++ b/toolkit/components/console/content/console.css
@@ -8,17 +8,18 @@
   overflow: auto;
 }
 
 .console-rows {
   -moz-user-focus: normal;
 }
 
 .console-row[type="error"],
-.console-row[type="warning"] {
+.console-row[type="warning"],
+.console-row[type="message"][typetext] {
   -moz-binding: url("chrome://global/content/consoleBindings.xml#error");
 }
 
 .console-row[type="message"] {
   -moz-binding: url("chrome://global/content/consoleBindings.xml#message");
 }
 
 .console-msg-text,
--- a/toolkit/components/console/content/consoleBindings.xml
+++ b/toolkit/components/console/content/consoleBindings.xml
@@ -216,22 +216,22 @@
       </method>
 
       <method name="appendError">
         <parameter name="aObject"/>
         <body><![CDATA[
           var row = this.createConsoleRow();
           var nsIScriptError = Components.interfaces.nsIScriptError;
 
-          // Is this error actually just a non-fatal warning?
-          var warning = aObject.flags & nsIScriptError.warningFlag != 0;
+          // nsIConsoleMessage constants: debug, info, warn, error
+          var typetext = ["typeMessage", "typeMessage", "typeWarning", "typeError"][aObject.logLevel];
+          var type = ["message", "message", "warning", "error"][aObject.logLevel];
 
-          var typetext = warning ? "typeWarning" : "typeError";
           row.setAttribute("typetext", this.mStrBundle.getString(typetext));
-          row.setAttribute("type", warning ? "warning" : "error");
+          row.setAttribute("type", type);
           row.setAttribute("msg", aObject.errorMessage);
           row.setAttribute("category", aObject.category);
           row.setAttribute("time", this.properFormatTime(aObject.timeStamp));
           if (aObject.lineNumber || aObject.sourceName) {
             row.setAttribute("href", this._truncateIfNecessary(aObject.sourceName).string);
             row.mSourceName = aObject.sourceName;
             row.setAttribute("line", aObject.lineNumber);
           } else {
--- a/toolkit/devtools/server/actors/webconsole.js
+++ b/toolkit/devtools/server/actors/webconsole.js
@@ -1267,16 +1267,17 @@ WebConsoleActor.prototype =
       lineNumber: aPageError.lineNumber,
       columnNumber: aPageError.columnNumber,
       category: aPageError.category,
       timeStamp: aPageError.timeStamp,
       warning: !!(aPageError.flags & aPageError.warningFlag),
       error: !!(aPageError.flags & aPageError.errorFlag),
       exception: !!(aPageError.flags & aPageError.exceptionFlag),
       strict: !!(aPageError.flags & aPageError.strictFlag),
+      info: !!(aPageError.flags & aPageError.infoFlag),
       private: aPageError.isFromPrivateWindow,
     };
   },
 
   /**
    * Handler for window.console API calls received from the ConsoleAPIListener.
    * This method sends the object to the remote Web Console client.
    *
--- a/toolkit/locales/en-US/chrome/global/console.properties
+++ b/toolkit/locales/en-US/chrome/global/console.properties
@@ -1,14 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 typeError=Error:
 typeWarning=Warning:
+typeMessage=Message:
 errFile=Source File: %S
 errLine=Line: %S
 errLineCol=Line: %S, Column: %S
 errCode=Source Code:
 errTime=Timestamp: %S
 
 # LOCALIZATION NOTE (evaluationContextChanged): The message displayed when the
 # browser console's evaluation context (window against which input is evaluated)
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -1335,92 +1335,99 @@ nsMemoryReporterManager::GetReportsExten
     MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
                          generation, mGetReportsState->mGeneration);
     return NS_OK;
   }
 
   MEMORY_REPORTING_LOG("GetReports (gen=%u, %d child(ren) present)\n",
                        generation, mNumChildProcesses);
 
-  if (mNumChildProcesses > 0) {
-    // Request memory reports from child processes.  We do this *before*
-    // collecting reports for this process so each process can collect
-    // reports in parallel.
-    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-    NS_ENSURE_STATE(obs);
-
-    nsPrintfCString genStr("generation=%x anonymize=%d minimize=%d DMDident=",
-                           generation, aAnonymize ? 1 : 0, aMinimize ? 1 : 0);
-    nsAutoString msg = NS_ConvertUTF8toUTF16(genStr);
-    msg += aDMDDumpIdent;
-
-    obs->NotifyObservers(nullptr, "child-memory-reporter-request",
-                         msg.get());
-
-    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
-    NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
-    rv = timer->InitWithFuncCallback(TimeoutCallback,
-                                     this, kTimeoutLengthMS,
-                                     nsITimer::TYPE_ONE_SHOT);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mGetReportsState = new GetReportsState(generation,
-                                           aAnonymize,
-                                           timer,
-                                           mNumChildProcesses,
-                                           aHandleReport,
-                                           aHandleReportData,
-                                           aFinishReporting,
-                                           aFinishReportingData,
-                                           aDMDDumpIdent);
-  } else {
-    mGetReportsState = new GetReportsState(generation,
-                                           aAnonymize,
-                                           nullptr,
-                                           /* mNumChildProcesses = */ 0,
-                                           aHandleReport,
-                                           aHandleReportData,
-                                           aFinishReporting,
-                                           aFinishReportingData,
-                                           aDMDDumpIdent);
-  }
+  mGetReportsState = new GetReportsState(generation,
+                                         aAnonymize,
+                                         aMinimize,
+                                         mNumChildProcesses,
+                                         aHandleReport,
+                                         aHandleReportData,
+                                         aFinishReporting,
+                                         aFinishReportingData,
+                                         aDMDDumpIdent);
 
   if (aMinimize) {
     rv = MinimizeMemoryUsage(NS_NewRunnableMethod(
       this, &nsMemoryReporterManager::StartGettingReports));
   } else {
     rv = StartGettingReports();
   }
   return rv;
 }
 
 nsresult
 nsMemoryReporterManager::StartGettingReports()
 {
   GetReportsState* s = mGetReportsState;
+  nsresult rv;
 
   // Get reports for this process.
   FILE* parentDMDFile = nullptr;
 #ifdef MOZ_DMD
   if (!s->mDMDDumpIdent.IsEmpty()) {
-    nsresult rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(),
+    rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(),
                                                   &parentDMDFile);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       // Proceed with the memory report as if DMD were disabled.
       parentDMDFile = nullptr;
     }
   }
 #endif
   GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
                                    s->mAnonymize, parentDMDFile);
-  s->mParentDone = true;
+
+  MOZ_ASSERT(s->mNumChildProcessesCompleted == 0);
+  if (s->mNumChildProcesses > 0) {
+    // Request memory reports from child processes.  This happens
+    // after the parent report so that the parent's main thread will
+    // be free to process the child reports, instead of causing them
+    // to be buffered and consume (possibly scarce) memory.
+    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    // Don't use NS_ENSURE_* here; can't return until the report is finished.
+    if (NS_WARN_IF(!timer)) {
+      FinishReporting();
+      return NS_ERROR_FAILURE;
+    }
+    rv = timer->InitWithFuncCallback(TimeoutCallback,
+                                     this, kTimeoutLengthMS,
+                                     nsITimer::TYPE_ONE_SHOT);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      FinishReporting();
+      return rv;
+    }
 
-  // If there are no remaining child processes, we can finish up immediately.
-  return (s->mNumChildProcessesCompleted >= s->mNumChildProcesses) ?
-    FinishReporting() : NS_OK;
+    MOZ_ASSERT(!s->mTimer);
+    s->mTimer.swap(timer);
+
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    if (NS_WARN_IF(!obs)) {
+      FinishReporting();
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    nsPrintfCString genStr("generation=%x anonymize=%d minimize=%d DMDident=",
+                           s->mGeneration, s->mAnonymize ? 1 : 0,
+                           s->mMinimize ? 1 : 0);
+    nsAutoString msg = NS_ConvertUTF8toUTF16(genStr);
+    msg += s->mDMDDumpIdent;
+
+    obs->NotifyObservers(nullptr, "child-memory-reporter-request",
+                         msg.get());
+
+    return NS_OK;
+  } else {
+    // If there are no child processes, we can finish up immediately.
+    return FinishReporting();
+  }
 }
 
 typedef nsCOMArray<nsIMemoryReporter> MemoryReporterArray;
 
 static PLDHashOperator
 StrongEnumerator(nsRefPtrHashKey<nsIMemoryReporter>* aElem, void* aData)
 {
   MemoryReporterArray* allReporters = static_cast<MemoryReporterArray*>(aData);
@@ -1481,28 +1488,21 @@ nsMemoryReporterManager::GetReportsForTh
   if (aDMDFile) {
     return nsMemoryInfoDumper::DumpDMDToFile(aDMDFile);
   }
 #endif
 
   return NS_OK;
 }
 
-// This function has no return value.  If something goes wrong, there's no
-// clear place to report the problem to, but that's ok -- we will end up
-// hitting the timeout and executing TimeoutCallback().
-void
-nsMemoryReporterManager::HandleChildReports(
-  const uint32_t& aGeneration,
-  const InfallibleTArray<dom::MemoryReport>& aChildReports)
+nsMemoryReporterManager::GetReportsState*
+nsMemoryReporterManager::GetStateForGeneration(uint32_t aGeneration)
 {
   // Memory reporting only happens on the main thread.
-  if (!NS_IsMainThread()) {
-    MOZ_CRASH();
-  }
+  MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   GetReportsState* s = mGetReportsState;
 
   if (!s) {
     // If we reach here, either:
     //
     // - A child process reported back too late, and no subsequent request
     //   is in flight.
@@ -1511,79 +1511,104 @@ nsMemoryReporterManager::HandleChildRepo
     //   triggered from somewhere other than GetReports(), causing child
     //   processes to report back when the nsMemoryReporterManager wasn't
     //   expecting it.
     //
     // Either way, there's nothing to be done.  Just ignore it.
     MEMORY_REPORTING_LOG(
       "HandleChildReports: no request in flight (aGen=%u)\n",
       aGeneration);
-    return;
+    return nullptr;
   }
 
   if (aGeneration != s->mGeneration) {
     // If we reach here, a child process must have reported back, too late,
     // while a subsequent (higher-numbered) request is in flight.  Again,
     // ignore it.
     MOZ_ASSERT(aGeneration < s->mGeneration);
     MEMORY_REPORTING_LOG(
       "HandleChildReports: gen mismatch (aGen=%u, s->gen=%u)\n",
       aGeneration, s->mGeneration);
+    return nullptr;
+  }
+
+  return s;
+}
+
+// This function has no return value.  If something goes wrong, there's no
+// clear place to report the problem to, but that's ok -- we will end up
+// hitting the timeout and executing TimeoutCallback().
+void
+nsMemoryReporterManager::HandleChildReport(
+  uint32_t aGeneration,
+  const dom::MemoryReport& aChildReport)
+{
+  GetReportsState* s = GetStateForGeneration(aGeneration);
+  if (!s) {
     return;
   }
 
-  // Process the reports from the child process.
-  for (uint32_t i = 0; i < aChildReports.Length(); i++) {
-    const dom::MemoryReport& r = aChildReports[i];
+  // Child reports should have a non-empty process.
+  MOZ_ASSERT(!aChildReport.process().IsEmpty());
+
+  // If the call fails, ignore and continue.
+  s->mHandleReport->Callback(aChildReport.process(),
+                             aChildReport.path(),
+                             aChildReport.kind(),
+                             aChildReport.units(),
+                             aChildReport.amount(),
+                             aChildReport.desc(),
+                             s->mHandleReportData);
+}
 
-    // Child reports should have a non-empty process.
-    MOZ_ASSERT(!r.process().IsEmpty());
+void
+nsMemoryReporterManager::EndChildReport(uint32_t aGeneration, bool aSuccess)
+{
+  GetReportsState* s = GetStateForGeneration(aGeneration);
+  if (!s) {
+    return;
+  }
+
+  s->mNumChildProcessesCompleted++;
 
-    // If the call fails, ignore and continue.
-    s->mHandleReport->Callback(r.process(), r.path(), r.kind(),
-                               r.units(), r.amount(), r.desc(),
-                               s->mHandleReportData);
+  if (aSuccess) {
+    MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): completed child %d\n",
+                         aGeneration, s->mNumChildProcessesCompleted);
+  } else {
+    // Unfortunately, there's no way to indicate this in the report yet.
+    // (Also, we don't have the child's identifier at this point.)
+    MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): child %d exited"
+                         " during report\n",
+                         aGeneration, s->mNumChildProcessesCompleted);
   }
 
   // If all the child processes have reported, we can cancel the timer and
   // finish up.  Otherwise, just return.
-
-  s->mNumChildProcessesCompleted++;
-  MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): completed child %d\n",
-                       aGeneration, s->mNumChildProcessesCompleted);
-
-  if (s->mNumChildProcessesCompleted >= s->mNumChildProcesses &&
-      s->mParentDone) {
+  if (s->mNumChildProcessesCompleted >= s->mNumChildProcesses) {
     s->mTimer->Cancel();
     FinishReporting();
   }
 }
 
 /* static */ void
 nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
 {
   nsMemoryReporterManager* mgr = static_cast<nsMemoryReporterManager*>(aData);
   GetReportsState* s = mgr->mGetReportsState;
 
-  MOZ_ASSERT(mgr->mGetReportsState);
+  // Release assert because: if the pointer is null we're about to
+  // crash regardless of DEBUG, and this way the compiler doesn't
+  // complain about unused variables.
+  MOZ_RELEASE_ASSERT(s, "mgr->mGetReportsState");
   MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u)\n",
                        s->mGeneration);
 
   // We don't bother sending any kind of cancellation message to the child
   // processes that haven't reported back.
-
-  if (s->mParentDone) {
-    mgr->FinishReporting();
-  } else {
-    // This is unlikely -- the timeout expired during MinimizeMemoryUsage.
-    MEMORY_REPORTING_LOG("Timeout expired before parent report started!");
-    // Let the parent continue with its report, but ensure that
-    // StartGettingReports gives up immediately after that.
-    s->mNumChildProcesses = s->mNumChildProcessesCompleted;
-  }
+  mgr->FinishReporting();
 }
 
 nsresult
 nsMemoryReporterManager::FinishReporting()
 {
   // Memory reporting only happens on the main thread.
   if (!NS_IsMainThread()) {
     MOZ_CRASH();
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -49,26 +49,29 @@ public:
   // Inter-process memory reporting proceeds as follows.
   //
   // - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
   //   synchronously gets memory reports for the current process, tells all
   //   child processes to get memory reports, and sets up some state
   //   (mGetReportsState) for when the child processes report back, including a
   //   timer.  Control then returns to the main event loop.
   //
-  // - HandleChildReports() is called (asynchronously) once per child process
-  //   that reports back.  If all child processes report back before time-out,
+  // - HandleChildReport() is called (asynchronously) once per child process
+  //   reporter callback.
+  //
+  // - EndChildReport() is called (asynchronously) once per child process that
+  //   finishes reporting back.  If all child processes do so before time-out,
   //   the timer is cancelled.  (The number of child processes is part of the
   //   saved request state.)
   //
   // - TimeoutCallback() is called (asynchronously) if all the child processes
   //   don't respond within the time threshold.
   //
   // - FinishReporting() finishes things off.  It is *always* called -- either
-  //   from HandleChildReports() (if all child processes have reported back) or
+  //   from EndChildReport() (if all child processes have reported back) or
   //   from TimeoutCallback() (if time-out occurs).
   //
   // All operations occur on the main thread.
   //
   // The above sequence of steps is a "request".  A partially-completed request
   // is described as "in flight".
   //
   // Each request has a "generation", a unique number that identifies it.  This
@@ -90,42 +93,46 @@ public:
   //   because partial information is better than nothing.
   //
   // - If a child process reports after the time-out occurs, it is ignored.
   //   (Generation checking will ensure it is ignored even if a subsequent
   //   request is in flight;  this is the main use of generations.)  No error
   //   is reported, because there's nothing sensible to be done about it at
   //   this late stage.
   //
+  // - If the time-out occurs after a child process has sent some reports but
+  //   before it has signaled completion (see bug 1151597), then what it
+  //   successfully sent will be included, with no explicit indication that it
+  //   is incomplete.
+  //
   // Now, what what happens if a child process is created/destroyed in the
   // middle of a request?  Well, GetReportsState contains a copy of
   // mNumChildProcesses which it uses to determine finished-ness.  So...
   //
   // - If a process is created, it won't have received the request for reports,
   //   and the GetReportsState's mNumChildProcesses won't account for it.  So
   //   the reported data will reflect how things were when the request began.
   //
-  // - If a process is destroyed before reporting back, we'll just hit the
-  //   time-out, because we'll have received reports (barring other errors)
-  //   from N-1 child process.  So the reported data will reflect how things
-  //   are when the request ends.
+  // - If a process is destroyed before it starts reporting back, the reported
+  //   data will reflect how things are when the request ends.
+  //
+  // - If a process is destroyed after it starts reporting back but before it
+  //   finishes, the reported data will contain a partial report for it.
   //
   // - If a process is destroyed after reporting back, but before all other
   //   child processes have reported back, it will be included in the reported
   //   data.  So the reported data will reflect how things were when the
   //   request began.
   //
-  // The inconsistencies between these three cases are unfortunate but
-  // difficult to avoid.  It's enough of an edge case to not be worth doing
-  // more.
+  // The inconsistencies between these cases are unfortunate but difficult to
+  // avoid.  It's enough of an edge case to not be worth doing more.
   //
-  void HandleChildReports(
-    const uint32_t& aGeneration,
-    const InfallibleTArray<mozilla::dom::MemoryReport>& aChildReports);
-  nsresult FinishReporting();
+  void HandleChildReport(uint32_t aGeneration,
+                         const mozilla::dom::MemoryReport& aChildReport);
+  void EndChildReport(uint32_t aGeneration, bool aSuccess);
 
   // Functions that (a) implement distinguished amounts, and (b) are outside of
   // this module.
   struct AmountFns
   {
     mozilla::InfallibleAmountFn mJSMainRuntimeGCHeap;
     mozilla::InfallibleAmountFn mJSMainRuntimeTemporaryPeak;
     mozilla::InfallibleAmountFn mJSMainRuntimeCompartmentsSystem;
@@ -170,16 +177,17 @@ public:
     }
   };
   SizeOfTabFns mSizeOfTabFns;
 
 private:
   nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter,
                                   bool aForce, bool aStrongRef);
   nsresult StartGettingReports();
+  nsresult FinishReporting();
 
   static void TimeoutCallback(nsITimer* aTimer, void* aData);
   // Note: this timeout needs to be long enough to allow for the
   // possibility of DMD reports and/or running on a low-end phone.
   static const uint32_t kTimeoutLengthMS = 50000;
 
   mozilla::Mutex mMutex;
   bool mIsRegistrationBlocked;
@@ -193,51 +201,52 @@ private:
 
   uint32_t mNumChildProcesses;
   uint32_t mNextGeneration;
 
   struct GetReportsState
   {
     uint32_t                             mGeneration;
     bool                                 mAnonymize;
+    bool                                 mMinimize;
     nsCOMPtr<nsITimer>                   mTimer;
     uint32_t                             mNumChildProcesses;
     uint32_t                             mNumChildProcessesCompleted;
-    bool                                 mParentDone;
     nsCOMPtr<nsIHandleReportCallback>    mHandleReport;
     nsCOMPtr<nsISupports>                mHandleReportData;
     nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
     nsCOMPtr<nsISupports>                mFinishReportingData;
     nsString                             mDMDDumpIdent;
 
-    GetReportsState(uint32_t aGeneration, bool aAnonymize, nsITimer* aTimer,
+    GetReportsState(uint32_t aGeneration, bool aAnonymize, bool aMinimize,
                     uint32_t aNumChildProcesses,
                     nsIHandleReportCallback* aHandleReport,
                     nsISupports* aHandleReportData,
                     nsIFinishReportingCallback* aFinishReporting,
                     nsISupports* aFinishReportingData,
                     const nsAString& aDMDDumpIdent)
       : mGeneration(aGeneration)
       , mAnonymize(aAnonymize)
-      , mTimer(aTimer)
+      , mMinimize(aMinimize)
       , mNumChildProcesses(aNumChildProcesses)
       , mNumChildProcessesCompleted(0)
-      , mParentDone(false)
       , mHandleReport(aHandleReport)
       , mHandleReportData(aHandleReportData)
       , mFinishReporting(aFinishReporting)
       , mFinishReportingData(aFinishReportingData)
       , mDMDDumpIdent(aDMDDumpIdent)
     {
     }
   };
 
   // When this is non-null, a request is in flight.  Note: We use manual
   // new/delete for this because its lifetime doesn't match block scope or
   // anything like that.
   GetReportsState* mGetReportsState;
+
+  GetReportsState* GetStateForGeneration(uint32_t aGeneration);
 };
 
 #define NS_MEMORY_REPORTER_MANAGER_CID \
 { 0xfb97e4f5, 0x32dd, 0x497a, \
 { 0xba, 0xa2, 0x7d, 0x1e, 0x55, 0x7, 0x99, 0x10 } }
 
 #endif // nsMemoryReporterManager_h__
--- a/xpcom/glue/nsTArray-inl.h
+++ b/xpcom/glue/nsTArray-inl.h
@@ -407,18 +407,25 @@ nsTArray_base<Alloc, Copy>::SwapArrayEle
   Copy::CopyElements(smallerElements, largerElements, largerLength, aElemSize);
   Copy::CopyElements(largerElements, temp.Elements(), smallerLength, aElemSize);
 
   // Swap the arrays' lengths.
   MOZ_ASSERT((aOther.Length() == 0 || mHdr != EmptyHdr()) &&
              (Length() == 0 || aOther.mHdr != EmptyHdr()),
              "Don't set sEmptyHdr's length.");
   size_type tempLength = Length();
-  mHdr->mLength = aOther.Length();
-  aOther.mHdr->mLength = tempLength;
+
+  // Avoid writing to EmptyHdr, since it can trigger false
+  // positives with TSan.
+  if (mHdr != EmptyHdr()) {
+    mHdr->mLength = aOther.Length();
+  }
+  if (aOther.mHdr != EmptyHdr()) {
+    aOther.mHdr->mLength = tempLength;
+  }
 
   return Alloc::SuccessResult();
 }
 
 template<class Alloc, class Copy>
 bool
 nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type aElemSize)
 {