Merge fx-team to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 29 Jul 2015 13:55:46 -0400
changeset 286881 7910e4e1015a8843580a63bfed4097945c2df2d1
parent 286880 8a62423b8343b409b5c37e80a7f284e42f1c4c77 (current diff)
parent 286875 62cd40885e9362dc726f681675b64aa9577982aa (diff)
child 286882 14ae070d4a419ea30357a588985694b35f63ae2b
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c. a=merge
security/manager/ssl/tests/mochitest/bugs/mochitest.ini
security/manager/ssl/tests/mochitest/bugs/test_bug480509.html
testing/web-platform/meta/IndexedDB/idbcursor_delete_index5.htm.ini
testing/web-platform/meta/IndexedDB/idbcursor_delete_objectstore5.htm.ini
testing/web-platform/meta/workers/Worker_ErrorEvent_bubbles_cancelable.htm.ini
toolkit/components/url-classifier/PrivateBrowsingTrackingProtectionWhitelist.js
toolkit/components/url-classifier/nsIPrivateBrowsingTrackingProtectionWhitelist.idl
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -14,16 +14,18 @@ namespace mozilla {
 namespace a11y {
 
 bool
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
 {
   if (mShutdown)
     return true;
 
+  CheckDocTree();
+
   if (aData.NewTree().IsEmpty()) {
     NS_ERROR("no children being added");
     return false;
   }
 
   ProxyAccessible* parent = GetAccessible(aData.ID());
 
   // XXX This should really never happen, but sometimes we fail to fire the
@@ -43,16 +45,18 @@ DocAccessibleParent::RecvShowEvent(const
   MOZ_ASSERT(consumed == aData.NewTree().Length());
 #ifdef DEBUG
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
 #endif
 
+  CheckDocTree();
+
   return true;
 }
 
 uint32_t
 DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
                                 const nsTArray<a11y::AccessibleData>& aNewTree,
                                 uint32_t aIdx, uint32_t aIdxInParent)
 {
@@ -113,16 +117,18 @@ DocAccessibleParent::RecvHideEvent(const
     NS_ERROR("invalid root being removed!");
     return true;
   }
 
   ProxyAccessible* parent = root->Parent();
   parent->RemoveChild(root);
   root->Shutdown();
 
+  CheckDocTree();
+
   return true;
 }
 
 bool
 DocAccessibleParent::RecvEvent(const uint64_t& aID, const uint32_t& aEventType)
 {
   ProxyAccessible* proxy = GetAccessible(aID);
   if (!proxy) {
@@ -185,19 +191,22 @@ bool
 DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
 {
   // One document should never directly be the child of another.
   // We should always have at least an outer doc accessible in between.
   MOZ_ASSERT(aID);
   if (!aID)
     return false;
 
+  CheckDocTree();
+
   auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
   bool result = AddChildDoc(childDoc, aID, false);
   MOZ_ASSERT(result);
+  CheckDocTree();
   return result;
 }
 
 bool
 DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
                                  uint64_t aParentID, bool aCreating)
 {
   // We do not use GetAccessible here because we want to be sure to not get the
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -622,19 +622,17 @@ pref("app.update.socket.retryTimeout", 3
 // Note: Offline errors will always retry when the network comes online.
 pref("app.update.socket.maxErrors", 20);
 
 // Enable update logging for now, to diagnose growing pains in the
 // field.
 pref("app.update.log", true);
 
 // SystemUpdate API
-#ifdef MOZ_WIDGET_GONK
 pref("dom.system_update.active", "@mozilla.org/updates/update-prompt;1");
-#endif
 #else
 // Explicitly disable the shutdown watchdog.  It's enabled by default.
 // When the updater is disabled, we want to know about shutdown hangs.
 pref("shutdown.watchdog.timeoutSecs", -1);
 #endif
 
 // Allow webapps update checking
 pref("webapps.update.enabled", true);
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- 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="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "21256d7665f972255d198f8af81a8df4bd0e0fc4", 
+        "git_revision": "088f350b39baf8f86c7c1161fd4be178ce822b7b", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "5ec7add1a5ed54e7c57e9fd24704624a9e84139a", 
+    "revision": "97666dae0fe5da2a0da4f57f41fcb12e9c2fe709", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
   <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="07c383a786f188904311a37f6062c2cb84c9b61d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="21256d7665f972255d198f8af81a8df4bd0e0fc4"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -704,18 +704,22 @@
 ; Modules
 @RESPATH@/modules/*
 
 ; Safe Browsing
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
+@RESPATH@/components/url-classifier.xpt
+
+; Private Browsing
+@RESPATH@/components/privatebrowsing.xpt
+@RESPATH@/components/PrivateBrowsing.manifest
 @RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
-@RESPATH@/components/url-classifier.xpt
 
 ; GNOME hooks
 #ifdef MOZ_ENABLE_GNOME_COMPONENT
 @RESPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@
 #endif
 
 ; ANGLE on Win32
 #ifdef XP_WIN32
--- a/browser/config/mozconfigs/linux64/opt-tsan
+++ b/browser/config/mozconfigs/linux64/opt-tsan
@@ -1,8 +1,11 @@
 ac_add_options --with-google-oauth-api-keyfile=/builds/google-oauth-api.key
 
 . $topsrcdir/build/unix/mozconfig.tsan
 
+export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
+. $topsrcdir/build/unix/mozconfig.gtk
+
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=tsan
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/tooltool-manifests/linux64/tsan.manifest
+++ b/browser/config/tooltool-manifests/linux64/tsan.manifest
@@ -3,10 +3,17 @@
 "clang_version": "r241773"
 }, 
 {
 "size": 89690541, 
 "digest": "470d258d9785a120fcba65eee90daa632a42affa0f97f57d70fc8285bd76bcc27d4d0d70b6c37577ab271a04c843b6269425391a8d6df1967718dba26dd3a73d", 
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true
+},
+{
+"size": 4431740,
+"digest": "68fc56b0fb0cdba629b95683d6649ff76b00dccf97af90960c3d7716f6108b2162ffd5ffcd5c3a60a21b28674df688fe4dabc67345e2da35ec5abeae3d48c8e3",
+"algorithm": "sha512",
+"filename": "gtk3.tar.xz",
+"unpack": true
 }
 ]
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -0,0 +1,34 @@
+[
+{
+"clang_version": "r183744"
+}, 
+{
+"size": 70350828, 
+"digest": "6cd04e8ec44c6fef159349c22bd0476891e4a2d46479f9586283eaf3305e42f79c720d40dfec0e78d8899c1651189b12e285de60862ffd0612b0dac7a0c336c6", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "clang.tar.bz2"
+},
+{
+"size": 2581027, 
+"digest": "9b59abef2bd4ae3a5b792de96e1336d879c1c5b6b07382797ae1bcc299e74ddf805bc95b7bc813cf3b4586db5eb4d0f41d09b2f85f0629cf27e57a4de851129c", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "cctools.tar.gz"
+},
+{
+"size": 35215976, 
+"visibility": "internal", 
+"digest": "8be736545ddab25ebded188458ce974d5c9a7e29f3c50d2ebfbcb878f6aff853dd2ff5a3528bdefc64396a10101a1b50fd2fe52000140df33643cebe1ea759da", 
+"algorithm": "sha512", 
+"unpack": true,
+"filename": "MacOSX10.7.sdk.tar.bz2"
+},
+{
+"size": 167175,
+"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+"algorithm": "sha512",
+"unpack": true,
+"filename": "sccache.tar.bz2"
+}
+]
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_595934_message_categories.js
@@ -37,17 +37,16 @@ const TESTS = [
       form.submit();
     },
   },
   {
     // #3
     file: "test-bug-595934-workers.html",
     category: "Web Worker",
     matchString: "fooBarWorker",
-    expectError: true,
   },
   {
     // #4
     file: "test-bug-595934-malformedxml.xhtml",
     category: "malformed-xml",
     matchString: "no element found",
   },
   {
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -633,20 +633,24 @@
 @RESPATH@/modules/*
 
 ; Safe Browsing
 #ifdef MOZ_URL_CLASSIFIER
 @RESPATH@/components/nsURLClassifier.manifest
 @RESPATH@/components/nsUrlClassifierHashCompleter.js
 @RESPATH@/components/nsUrlClassifierListManager.js
 @RESPATH@/components/nsUrlClassifierLib.js
-@RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
 @RESPATH@/components/url-classifier.xpt
 #endif
 
+; Private Browsing
+@RESPATH@/components/privatebrowsing.xpt
+@RESPATH@/components/PrivateBrowsing.manifest
+@RESPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
+
 ; ANGLE GLES-on-D3D rendering library
 #ifdef MOZ_ANGLE_RENDERER
 @BINPATH@/libEGL.dll
 @BINPATH@/libGLESv2.dll
 
 #ifdef MOZ_D3DCOMPILER_VISTA_DLL
 @BINPATH@/@MOZ_D3DCOMPILER_VISTA_DLL@
 #endif
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -168,134 +168,134 @@ if test -z "$BUILDING_JS" -o -n "$JS_STA
         # we don't need to pass data to and from legacy char* APIs
         ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_CHARSET_IS_UTF8"
         # make sure to not accidentally pick up system-icu headers
         ICU_CPPFLAGS="$ICU_CPPFLAGS -I$icudir/common -I$icudir/i18n"
 
         ICU_CROSS_BUILD_OPT=""
 
         if test "$CROSS_COMPILE"; then
-    	# Remove _DEPEND_CFLAGS from HOST_FLAGS to avoid configure error
-    	HOST_ICU_CFLAGS="$HOST_CFLAGS"
-    	HOST_ICU_CXXFLAGS="$HOST_CXXFLAGS"
+            # Remove _DEPEND_CFLAGS from HOST_FLAGS to avoid configure error
+            HOST_ICU_CFLAGS="$HOST_CFLAGS"
+            HOST_ICU_CXXFLAGS="$HOST_CXXFLAGS"
 
-    	HOST_ICU_CFLAGS=`echo $HOST_ICU_CFLAGS | sed "s|$_DEPEND_CFLAGS||g"`
-    	HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CFXXLAGS | sed "s|$_DEPEND_CFLAGS||g"`
+            HOST_ICU_CFLAGS=`echo $HOST_ICU_CFLAGS | sed "s|$_DEPEND_CFLAGS||g"`
+            HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CFXXLAGS | sed "s|$_DEPEND_CFLAGS||g"`
 
-    	# ICU requires RTTI
-    	if test "$GNU_CC"; then
-    	    HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CXXFLAGS | sed 's|-fno-rtti|-frtti|g'`
-    	elif test "$_MSC_VER"; then
-    	    HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CXXFLAGS | sed 's|-GR-|-GR|g'`
-    	fi
+            # ICU requires RTTI
+            if test "$GNU_CC"; then
+                HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CXXFLAGS | sed 's|-fno-rtti|-frtti|g'`
+            elif test "$_MSC_VER"; then
+                HOST_ICU_CXXFLAGS=`echo $HOST_ICU_CXXFLAGS | sed 's|-GR-|-GR|g'`
+            fi
 
-    	HOST_ICU_BUILD_OPTS=""
-    	if test -n "$MOZ_DEBUG"; then
-    	    HOST_ICU_BUILD_OPTS="$HOST_ICU_BUILD_OPTS --enable-debug"
-    	fi
+            HOST_ICU_BUILD_OPTS=""
+            if test -n "$MOZ_DEBUG"; then
+                HOST_ICU_BUILD_OPTS="$HOST_ICU_BUILD_OPTS --enable-debug"
+            fi
 
-    	abs_srcdir=`(cd $srcdir; pwd)`
-    	mkdir -p $_objdir/intl/icu/host
-        (export AR="$HOST_AR"
-         export RANLIB="$HOST_RANLIB"
-         export CC="$HOST_CC"
-         export CXX="$HOST_CXX"
-         export CPP="$HOST_CPP"
-         export LD="$HOST_LD"
-         export CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS"
-         export CPPFLAGS="$ICU_CPPFLAGS"
-         export CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS"
-         export LDFLAGS="$HOST_LDFLAGS"
-         ac_configure_args="$HOST_ICU_BUILD_OPTS"
-         ac_configure_args="$ac_configure_args --enable-static --disable-shared --enable-extras=no --enable-icuio=no --enable-layout=no --enable-tests=no --enable-samples=no"
-         AC_OUTPUT_SUBDIRS_NOW(intl/icu/source:intl/icu/host)
-        ) || exit 1
-    	# generate config/icucross.mk
-    	$GMAKE -C $_objdir/intl/icu/host/ config/icucross.mk
+            abs_srcdir=`(cd $srcdir; pwd)`
+            mkdir -p $_objdir/intl/icu/host
+            (export AR="$HOST_AR"
+             export RANLIB="$HOST_RANLIB"
+             export CC="$HOST_CC"
+             export CXX="$HOST_CXX"
+             export CPP="$HOST_CPP"
+             export LD="$HOST_LD"
+             export CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS"
+             export CPPFLAGS="$ICU_CPPFLAGS"
+             export CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS"
+             export LDFLAGS="$HOST_LDFLAGS"
+             ac_configure_args="$HOST_ICU_BUILD_OPTS"
+             ac_configure_args="$ac_configure_args --enable-static --disable-shared --enable-extras=no --enable-icuio=no --enable-layout=no --enable-tests=no --enable-samples=no"
+             AC_OUTPUT_SUBDIRS_NOW(intl/icu/source:intl/icu/host)
+            ) || exit 1
+            # generate config/icucross.mk
+            $GMAKE -C $_objdir/intl/icu/host/ config/icucross.mk
 
-    	# --with-cross-build requires absolute path
-    	ICU_HOST_PATH=`cd $_objdir/intl/icu/host && pwd`
-    	ICU_CROSS_BUILD_OPT="--with-cross-build=$ICU_HOST_PATH --disable-tools"
-    	ICU_TARGET_OPT="--build=$build --host=$target"
+            # --with-cross-build requires absolute path
+            ICU_HOST_PATH=`cd $_objdir/intl/icu/host && pwd`
+            ICU_CROSS_BUILD_OPT="--with-cross-build=$ICU_HOST_PATH --disable-tools"
+            ICU_TARGET_OPT="--build=$build --host=$target"
         else
-    	# CROSS_COMPILE isn't set build and target are i386 and x86-64.
-    	# So we must set target for --build and --host.
-    	ICU_TARGET_OPT="--build=$target --host=$target"
+            # CROSS_COMPILE isn't set build and target are i386 and x86-64.
+            # So we must set target for --build and --host.
+            ICU_TARGET_OPT="--build=$target --host=$target"
         fi
 
         if test -z "$MOZ_SHARED_ICU"; then
-    	# To reduce library size, use static linking
-    	ICU_LINK_OPTS="--enable-static --disable-shared"
+            # To reduce library size, use static linking
+            ICU_LINK_OPTS="--enable-static --disable-shared"
         else
-    	ICU_LINK_OPTS="--disable-static --enable-shared"
+            ICU_LINK_OPTS="--disable-static --enable-shared"
         fi
         # Force the ICU static libraries to be position independent code
         ICU_CFLAGS="$DSO_PIC_CFLAGS $CFLAGS"
         ICU_CXXFLAGS="$DSO_PIC_CFLAGS $CXXFLAGS"
 
         ICU_BUILD_OPTS=""
         if test -n "$MOZ_DEBUG" -o "MOZ_DEBUG_SYMBOLS"; then
-    	ICU_CFLAGS="$ICU_CFLAGS $MOZ_DEBUG_FLAGS"
-    	ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_DEBUG_FLAGS"
-    	if test -n "$CROSS_COMPILE" -a "$OS_TARGET" = "Darwin" \
-    		-a "$HOST_OS_ARCH" != "Darwin"
-    	then
-    	    # Bug 951758: Cross-OSX builds with non-Darwin hosts have issues
-    	    # with -g and friends (like -gdwarf and -gfull) because they try
-    	    # to run dsymutil
-    	    changequote(,)
-    	    ICU_CFLAGS=`echo $ICU_CFLAGS | sed 's|-g[^ \t]*||g'`
-    	    ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-g[^ \t]*||g'`
-    	    changequote([,])
-    	fi
+            ICU_CFLAGS="$ICU_CFLAGS $MOZ_DEBUG_FLAGS"
+            ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_DEBUG_FLAGS"
+            if test -n "$CROSS_COMPILE" -a "$OS_TARGET" = "Darwin" \
+                    -a "$HOST_OS_ARCH" != "Darwin"
+            then
+                # Bug 951758: Cross-OSX builds with non-Darwin hosts have issues
+                # with -g and friends (like -gdwarf and -gfull) because they try
+                # to run dsymutil
+                changequote(,)
+                ICU_CFLAGS=`echo $ICU_CFLAGS | sed 's|-g[^ \t]*||g'`
+                ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-g[^ \t]*||g'`
+                changequote([,])
+            fi
 
-    	ICU_LDFLAGS="$MOZ_DEBUG_LDFLAGS"
-    	if test -z "$MOZ_DEBUG"; then
-    	    # To generate debug symbols, it requires MOZ_DEBUG_FLAGS.
-    	    # But, not debug build.
-    	    ICU_CFLAGS="$ICU_CFLAGS -UDEBUG -DNDEBUG"
-    	    ICU_CXXFLAGS="$ICU_CXXFLAGS -UDEBUG -DNDEBUG"
-    	elif test -z "$MOZ_NO_DEBUG_RTL"; then
-    	    ICU_BUILD_OPTS="$ICU_BUILD_OPTS --enable-debug"
-    	fi
+            ICU_LDFLAGS="$MOZ_DEBUG_LDFLAGS"
+            if test -z "$MOZ_DEBUG"; then
+                # To generate debug symbols, it requires MOZ_DEBUG_FLAGS.
+                # But, not debug build.
+                ICU_CFLAGS="$ICU_CFLAGS -UDEBUG -DNDEBUG"
+                ICU_CXXFLAGS="$ICU_CXXFLAGS -UDEBUG -DNDEBUG"
+            elif test -z "$MOZ_NO_DEBUG_RTL"; then
+                ICU_BUILD_OPTS="$ICU_BUILD_OPTS --enable-debug"
+            fi
         fi
         if test -z "$MOZ_OPTIMIZE"; then
-    	ICU_BUILD_OPTS="$ICU_BUILD_OPTS --disable-release"
+            ICU_BUILD_OPTS="$ICU_BUILD_OPTS --disable-release"
         else
-    	ICU_CFLAGS="$ICU_CFLAGS $MOZ_OPTIMIZE_FLAGS"
-    	ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_OPTIMIZE_FLAGS"
+            ICU_CFLAGS="$ICU_CFLAGS $MOZ_OPTIMIZE_FLAGS"
+            ICU_CXXFLAGS="$ICU_CXXFLAGS $MOZ_OPTIMIZE_FLAGS"
         fi
 
         if test "$am_cv_langinfo_codeset" = "no"; then
-    	# ex. Android
-    	ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_HAVE_NL_LANGINFO_CODESET=0"
+            # ex. Android
+            ICU_CPPFLAGS="$ICU_CPPFLAGS -DU_HAVE_NL_LANGINFO_CODESET=0"
         fi
 
         # ICU requires RTTI
         if test "$GNU_CC"; then
-    	ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-fno-rtti|-frtti|g'`
+            ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-fno-rtti|-frtti|g'`
         else
-    	if test "$_MSC_VER"; then
-    	    ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-GR-|-GR|g'`
-    	fi
+            if test "$_MSC_VER"; then
+                ICU_CXXFLAGS=`echo $ICU_CXXFLAGS | sed 's|-GR-|-GR|g'`
+            fi
 
-    	# Add RTL flags for MSVCRT.DLL
-    	if test -n "$MOZ_DEBUG" -a -z "$MOZ_NO_DEBUG_RTL"; then
-    	    ICU_CFLAGS="$ICU_CFLAGS -MDd"
-    	    ICU_CXXFLAGS="$ICU_CXXFLAGS -MDd"
-    	else
-    	    ICU_CFLAGS="$ICU_CFLAGS -MD"
-    	    ICU_CXXFLAGS="$ICU_CXXFLAGS -MD"
-    	fi
+            # Add RTL flags for MSVCRT.DLL
+            if test -n "$MOZ_DEBUG" -a -z "$MOZ_NO_DEBUG_RTL"; then
+                ICU_CFLAGS="$ICU_CFLAGS -MDd"
+                ICU_CXXFLAGS="$ICU_CXXFLAGS -MDd"
+            else
+                ICU_CFLAGS="$ICU_CFLAGS -MD"
+                ICU_CXXFLAGS="$ICU_CXXFLAGS -MD"
+            fi
 
-    	# add disable optimize flag for workaround for bug 899948
-    	if test -z "$MOZ_OPTIMIZE"; then
-    	    ICU_CFLAGS="$ICU_CFLAGS -Od"
-    	    ICU_CXXFLAGS="$ICU_CXXFLAGS -Od"
-    	fi
+            # add disable optimize flag for workaround for bug 899948
+            if test -z "$MOZ_OPTIMIZE"; then
+                ICU_CFLAGS="$ICU_CFLAGS -Od"
+                ICU_CXXFLAGS="$ICU_CXXFLAGS -Od"
+            fi
         fi
 
         if test -n "$gonkdir"; then
             ICU_CXXFLAGS="-I$gonkdir/abi/cpp/include $ICU_CXXFLAGS"
         fi
 
         if test -z "$MOZ_SHARED_ICU"; then
           ICU_CXXFLAGS="$ICU_CXXFLAGS -DU_STATIC_IMPLEMENTATION"
new file mode 100644
--- /dev/null
+++ b/build/macosx/cross-mozconfig.common
@@ -0,0 +1,50 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOZ_AUTOMATION_L10N_CHECK=0
+#TODO: bug 935237 - fix packaging
+MOZ_AUTOMATION_PACKAGE=0
+#TODO: bug 543111 - fix Breakpad
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_UPLOAD_SYMBOLS=0
+
+if [ "x$IS_NIGHTLY" = "xyes" ]; then
+  # Some nightlies (eg: Mulet) don't want these set.
+  MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
+  MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
+fi
+. "$topsrcdir/build/mozconfig.common"
+#TODO: bug 543111 - fix Breakpad
+ac_add_options --disable-crashreporter
+
+# ld needs libLTO.so from llvm
+mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/clang/lib"
+
+CROSS_CCTOOLS_PATH=$topsrcdir/cctools
+CROSS_SYSROOT=$topsrcdir/MacOSX10.7.sdk
+CROSS_PRIVATE_FRAMEWORKS=$CROSS_SYSROOT/System/Library/PrivateFrameworks
+FLAGS="-target x86_64-apple-darwin10 -mlinker-version=136 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT"
+
+export CC="$topsrcdir/clang/bin/clang $FLAGS"
+export CXX="$topsrcdir/clang/bin/clang++ $FLAGS"
+export CPP="$topsrcdir/clang/bin/clang $FLAGS -E"
+export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
+export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip"
+export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10-
+#TODO: bug 1184202 - would be nice if these could be detected with TOOLCHAIN_PREFIX automatically
+export AR=${TOOLCHAIN_PREFIX}ar
+export RANLIB=${TOOLCHAIN_PREFIX}ranlib
+export STRIP=${TOOLCHAIN_PREFIX}strip
+export OTOOL=${TOOLCHAIN_PREFIX}otool
+
+export HOST_CC=gcc
+export HOST_CXX=g++
+export HOST_LDFLAGS="-g"
+
+ac_add_options --target=x86_64-apple-darwin
+ac_add_options --with-macos-private-frameworks=$CROSS_PRIVATE_FRAMEWORKS
+
+. "$topsrcdir/build/mozconfig.cache"
+
+export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
copy from build/macosx/mozconfig.common
copy to build/macosx/local-mozconfig.common
--- a/build/macosx/mozconfig.common
+++ b/build/macosx/mozconfig.common
@@ -1,37 +1,5 @@
-# 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/.
-
-MOZ_AUTOMATION_L10N_CHECK=0
-
-if [ "x$IS_NIGHTLY" = "xyes" ]; then
-  # Some nightlies (eg: Mulet) don't want these set.
-  MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
-  MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
-  MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
+if test `uname -s` = Linux; then
+  . $topsrcdir/build/macosx/cross-mozconfig.common
+else
+  . $topsrcdir/build/macosx/local-mozconfig.common
 fi
-. "$topsrcdir/build/mozconfig.common"
-
-if [ -d "$topsrcdir/clang" ]; then
-    # mozilla-central based build
-    export CC=$topsrcdir/clang/bin/clang
-    export CXX=$topsrcdir/clang/bin/clang++
-    export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
-elif [ -d "$topsrcdir/../clang" ]; then
-    # comm-central based build
-    export CC=$topsrcdir/../clang/bin/clang
-    export CXX=$topsrcdir/../clang/bin/clang++
-    export LLVMCONFIG=$topsrcdir/../clang/bin/llvm-config
-fi
-
-# If not set use the system default clang
-if [ -z "$CC" ]; then
-    export CC=clang
-fi
-
-# If not set use the system default clang++
-if [ -z "$CXX" ]; then
-    export CXX=clang++
-fi
-
-export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -756,26 +756,16 @@ nsScriptSecurityManager::CheckLoadURIWit
 
     // If the schemes don't match, the policy is specified by the protocol
     // flags on the target URI.  Note that the order of policy checks here is
     // very important!  We start from most restrictive and work our way down.
     // Note that since we're working with the innermost URI, we can just use
     // the methods that work on chains of nested URIs and they will only look
     // at the flags for our one URI.
 
-    // Special case: moz-extension has a whitelist of URIs that are loadable by
-    // anyone.
-    if (targetScheme.EqualsLiteral("moz-extension") && GetAddonPolicyService()) {
-      bool loadable = false;
-      rv = GetAddonPolicyService()->ExtensionURILoadableByAnyone(targetBaseURI, &loadable);
-      if (NS_SUCCEEDED(rv) && loadable) {
-        return NS_OK;
-      }
-    }
-
     // Check for system target URI
     rv = DenyAccessIfURIHasFlags(targetBaseURI,
                                  nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
     if (NS_FAILED(rv)) {
         // Deny access, since the origin principal is not system
         if (reportErrors) {
             ReportError(nullptr, errorTag, sourceURI, aTargetURI);
         }
--- a/caps/tests/mochitest/test_extensionURL.html
+++ b/caps/tests/mochitest/test_extensionURL.html
@@ -84,32 +84,40 @@ https://bugzilla.mozilla.org/show_bug.cg
     aps.setExtensionURIToAddonIdCallback(SpecialPowers.wrapCallback(function (uri) { return 'imaginaryaddon-' + uri.host[0]; }));
 
     function testLoad(url, navigate, shouldThrow) {
       var ifr = document.createElement('iframe');
       var p = new Promise(function(resolve, reject) {
         ifr.onload = function() {
           ok(true, 'Loaded ' + url);
           var prin = SpecialPowers.wrap(ifr.contentWindow).document.nodePrincipal;
-          is(prin.originNoSuffix, url, 'Principal origin is correct: ' + url);
+          function stripTrailingSlash(s) { return s.replace(/\/$/, ''); };
+          is(stripTrailingSlash(prin.URI.spec), url, 'Principal uri is correct: ' + url);
+          function stripPath(s) { return s.replace(/(.*\/\/.+)\/.*/, '$1'); };
+          is(prin.originNoSuffix, stripPath(url), 'Principal origin is correct: ' + prin.originNoSuffix);
           is(prin.originAttributes.addonId, 'imaginaryaddon-' + url[url.indexOf('/') + 2], 'addonId is correct');
-          is(SpecialPowers.wrap(ifr.contentWindow).document.title, 'resource test file',
-             'document looks right');
+          if (/_blank/.test(url)) {
+            is(SpecialPowers.wrap(ifr.contentWindow).document.documentElement.innerHTML,
+               '<head></head><body></body>', 'blank document looks right');
+          } else {
+            is(SpecialPowers.wrap(ifr.contentWindow).document.title, 'resource test file',
+               'document looks right');
+          }
           ifr.remove();
           resolve();
         };
         document.body.appendChild(ifr);
 
         var threw = false;
         try {
           navigate(ifr, url);
         } catch (e) {
           ifr.remove();
           threw = true;
-          ok(/denied|insecure/.test(e), "exceiton correct: " + e);
+          ok(/denied|insecure/.test(e), "exception correct: " + e);
         }
         is(threw, !!shouldThrow, "Correct throwing behavior for: " + url);
         !threw || resolve();
       });
 
       return p;
     }
 
@@ -121,16 +129,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation, /* shouldThrow = */ true))
     .then(setWhitelistCallback.bind(null, /cherise/))
     .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithLocation))
     .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation, /* shouldThrow = */ true))
     .then(setWhitelistCallback.bind(null, /cherise|liebchen/))
     .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithLocation))
     .then(testLoad.bind(null, 'moz-extension://liebchen', navigateWithSrc))
     .then(testLoad.bind(null, 'moz-extension://cherise', navigateWithSrc))
+    .then(testLoad.bind(null, 'moz-extension://cherise/_blank.html', navigateWithSrc))
     .then(SimpleTest.finish.bind(SimpleTest),
           function(e) { ok(false, "rejected promise: " + e); SimpleTest.finish() }
     );
   });
 
   </script>
 </head>
 <body>
--- a/configure.in
+++ b/configure.in
@@ -4340,20 +4340,17 @@ cairo-cocoa)
     # Use -Wl as a trick to avoid -framework and framework names from
     # being separated by AC_SUBST_LIST.
     TK_LIBS='-Wl,-framework,CoreLocation -Wl,-framework,QuartzCore -Wl,-framework,Carbon -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,AddressBook -Wl,-framework,OpenGL'
     TK_CFLAGS="-DNO_X11"
     CFLAGS="$CFLAGS $TK_CFLAGS"
     CXXFLAGS="$CXXFLAGS $TK_CFLAGS"
     MOZ_USER_DIR="Mozilla"
     MOZ_FS_LAYOUT=bundle
-    # skip event loop instrumentation on UIKit for now
-    if test "$MOZ_WIDGET_TOOLKIT" == "cocoa"; then
-      MOZ_INSTRUMENT_EVENT_LOOP=1
-    fi
+    MOZ_INSTRUMENT_EVENT_LOOP=1
     ;;
 
 cairo-uikit)
     MOZ_WIDGET_TOOLKIT=uikit
     AC_DEFINE(MOZ_WIDGET_UIKIT)
     LDFLAGS="$LDFLAGS -framework UIKit -lobjc"
     TK_CFLAGS="-DNO_X11"
     TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreGraphics -Wl,-framework,CoreText -Wl,-framework,AVFoundation -Wl,-framework,AudioToolbox -Wl,-framework,CoreMedia -Wl,-framework,CoreVideo -Wl,-framework,OpenGLES -Wl,-framework,QuartzCore'
@@ -9211,32 +9208,16 @@ AC_SUBST(JS_SHARED_LIBRARY)
 MOZ_CREATE_CONFIG_STATUS()
 
 # No need to run subconfigures when building with LIBXUL_SDK_DIR
 if test "$COMPILE_ENVIRONMENT" -a -z "$LIBXUL_SDK_DIR"; then
   MOZ_SUBCONFIGURE_ICU()
   MOZ_SUBCONFIGURE_FFI()
 fi
 
-# Hack around an Apple bug that affects the egrep that comes with OS X 10.7.
-# "env ARCHPREFERENCE=i386,x86_64 arch egrep" first tries to use the 32-bit
-# Intel part of the egrep fat binary, even on 64-bit systems, and falls back on
-# the 64-bit part if it's not a fat binary, as can happen with MacPorts. We
-# (apparently) only need this hack when egrep's "pattern" is particularly long
-# (as in the following code) and the first egrep on our $PATH is Apple's.  See
-# bug 655339.
-case "$host" in
-*-apple-darwin11*)
-    FIXED_EGREP="env ARCHPREFERENCE=i386,x86_64 arch egrep"
-    ;;
-*)
-    FIXED_EGREP="egrep"
-    ;;
-esac
-
 # Run jemalloc configure script
 
 if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -o -n "$MOZ_REPLACE_MALLOC"; then
   ac_configure_args="--build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_ --disable-valgrind"
   # We're using memalign for _aligned_malloc in memory/build/mozmemory_wrap.c
   # on Windows, so just export memalign on all platforms.
   ac_configure_args="$ac_configure_args ac_cv_func_memalign=yes"
   if test -n "$MOZ_REPLACE_MALLOC"; then
--- a/dom/alarm/AlarmService.jsm
+++ b/dom/alarm/AlarmService.jsm
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /* static functions */
-const DEBUG = false;
+const DEBUG = true;
 
 function debug(aStr) {
   DEBUG && dump("AlarmService: " + aStr + "\n");
 }
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
--- a/dom/base/MultipartBlobImpl.h
+++ b/dom/base/MultipartBlobImpl.h
@@ -93,17 +93,17 @@ public:
     return mLength;
   }
 
   virtual void GetInternalStream(nsIInputStream** aInputStream,
                                  ErrorResult& aRv) override;
 
   virtual const nsTArray<nsRefPtr<BlobImpl>>* GetSubBlobImpls() const override
   {
-    return &mBlobImpls;
+    return mBlobImpls.Length() ? &mBlobImpls : nullptr;
   }
 
   virtual void GetMozFullPathInternal(nsAString& aFullPath,
                                       ErrorResult& aRv) const override;
 
   virtual nsresult
   SetMutable(bool aMutable) override;
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3470,27 +3470,26 @@ nsPIDOMWindow::SetFrameElementInternal(E
     NS_ERROR("frameElement set on inner window with no outer!");
 
     return;
   }
 
   mOuterWindow->SetFrameElementInternal(aFrameElement);
 }
 
-void
+bool
 nsPIDOMWindow::AddAudioContext(AudioContext* aAudioContext)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   mAudioContexts.AppendElement(aAudioContext);
 
+  // Return true if the context should be muted and false if not.
   nsIDocShell* docShell = GetDocShell();
-  if (docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline()) {
-    aAudioContext->Mute();
-  }
+  return docShell && !docShell->GetAllowMedia() && !aAudioContext->IsOffline();
 }
 
 void
 nsPIDOMWindow::RemoveAudioContext(AudioContext* aAudioContext)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   mAudioContexts.RemoveElement(aAudioContext);
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -713,17 +713,17 @@ public:
    */
   virtual void
   FirePopupBlockedEvent(nsIDocument* aDoc,
                         nsIURI* aPopupURI,
                         const nsAString& aPopupWindowName,
                         const nsAString& aPopupWindowFeatures) = 0;
 
   // Inner windows only.
-  void AddAudioContext(mozilla::dom::AudioContext* aAudioContext);
+  bool AddAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void RemoveAudioContext(mozilla::dom::AudioContext* aAudioContext);
   void MuteAudioContexts();
   void UnmuteAudioContexts();
 
   // Given an inner window, return its outer if the inner is the current inner.
   // Otherwise (argument null or not an inner or not current) return null.
   static nsPIDOMWindow* GetOuterFromCurrentInner(nsPIDOMWindow* aInner)
   {
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -740,23 +740,17 @@ nsPerformance::InsertUserEntry(Performan
 
   if (nsContentUtils::IsUserTimingLoggingEnabled()) {
     nsAutoCString uri;
     nsresult rv = GetOwner()->GetDocumentURI()->GetHost(uri);
     if(NS_FAILED(rv)) {
       // If we have no URI, just put in "none".
       uri.AssignLiteral("none");
     }
-    PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n",
-            uri.get(),
-            NS_ConvertUTF16toUTF8(aEntry->GetEntryType()).get(),
-            NS_ConvertUTF16toUTF8(aEntry->GetName()).get(),
-            aEntry->StartTime(),
-            aEntry->Duration(),
-            static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC));
+    PerformanceBase::LogEntry(aEntry, uri);
   }
 
   PerformanceBase::InsertUserEntry(aEntry);
 }
 
 DOMHighResTimeStamp
 nsPerformance::DeltaFromNavigationStart(DOMHighResTimeStamp aTime)
 {
@@ -976,16 +970,28 @@ PerformanceBase::Measure(const nsAString
 
 void
 PerformanceBase::ClearMeasures(const Optional<nsAString>& aName)
 {
   ClearUserEntries(aName, NS_LITERAL_STRING("measure"));
 }
 
 void
+PerformanceBase::LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const
+{
+  PERFLOG("Performance Entry: %s|%s|%s|%f|%f|%" PRIu64 "\n",
+          aOwner.BeginReading(),
+          NS_ConvertUTF16toUTF8(aEntry->GetEntryType()).get(),
+          NS_ConvertUTF16toUTF8(aEntry->GetName()).get(),
+          aEntry->StartTime(),
+          aEntry->Duration(),
+          static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC));
+}
+
+void
 PerformanceBase::InsertUserEntry(PerformanceEntry* aEntry)
 {
   mUserEntries.InsertElementSorted(aEntry,
                                    PerformanceEntryComparator());
 }
 
 void
 PerformanceBase::SetResourceTimingBufferSize(uint64_t aMaxSize)
--- a/dom/base/nsPerformance.h
+++ b/dom/base/nsPerformance.h
@@ -345,16 +345,18 @@ protected:
   virtual DOMHighResTimeStamp
   GetPerformanceTimingFromString(const nsAString& aTimingName) = 0;
 
   bool IsResourceEntryLimitReached() const
   {
     return mResourceEntries.Length() >= mResourceTimingBufferSize;
   }
 
+  void LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const;
+
 private:
   nsTArray<nsRefPtr<PerformanceEntry>> mUserEntries;
   nsTArray<nsRefPtr<PerformanceEntry>> mResourceEntries;
 
   uint64_t mResourceTimingBufferSize;
   static const uint64_t kDefaultResourceTimingBufferSize = 150;
 };
 
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -96,18 +96,18 @@ private:
   JSObject* mWrapper;
 };
 
 static void
 DebugWrapperTraceCallback(JS::GCCellPtr aPtr, const char* aName, void* aClosure)
 {
   DebugWrapperTraversalCallback* callback =
     static_cast<DebugWrapperTraversalCallback*>(aClosure);
-  if (aPtr.isObject()) {
-    callback->NoteJSObject(aPtr.toObject());
+  if (aPtr.is<JSObject>()) {
+    callback->NoteJSObject(&aPtr.as<JSObject>());
   }
 }
 
 void
 nsWrapperCache::CheckCCWrapperTraversal(void* aScriptObjectHolder,
                                         nsScriptObjectTracer* aTracer)
 {
   JSObject* wrapper = GetWrapper();
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -317,18 +317,20 @@ private:
     MOZ_ASSERT((aFlagsToUnset & ~kWrapperFlagsMask) == 0, "Bad wrapper flag bits");
     mFlags &= ~aFlagsToUnset;
   }
 
   static void HoldJSObjects(void* aScriptObjectHolder,
                             nsScriptObjectTracer* aTracer);
 
 #ifdef DEBUG
+public:
   void CheckCCWrapperTraversal(void* aScriptObjectHolder,
                                nsScriptObjectTracer* aTracer);
+private:
 #endif // DEBUG
 
   /**
    * If this bit is set then we're preserving the wrapper, which in effect ties
    * the lifetime of the JS object stored in the cache to the lifetime of the
    * native object. We rely on the cycle collector to break the cycle that this
    * causes between the native object and the JS object, so it is important that
    * any native object that supports preserving of its wrapper
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_pluginAudio.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<embed type="application/x-test" width="200" height="200"></embed>
+<script>
+var plugin = document.querySelector("embed");
+onload = function() {
+  plugin.startAudioPlayback();
+};
+function stopAudio() {
+  plugin.stopAudioPlayback();
+}
+function pluginMuted() {
+  return plugin.audioMuted();
+}
+function toggleMuteState(muted) {
+  var Ci = SpecialPowers.Ci;
+  var utils = SpecialPowers.wrap(window).top
+                           .QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
+  utils.audioMuted = muted;
+}
+</script>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -235,16 +235,17 @@ support-files =
   wholeTexty-helper.xml
   file_nonascii_blob_url.html
   referrerHelper.js
   test_performance_user_timing.js
   img_referrer_testserver.sjs
   file_audioLoop.html
   file_webaudioLoop.html
   file_webaudioLoop2.html
+  file_pluginAudio.html
   referrer_helper.js
   referrer_testserver.sjs
   script_postmessages_fileList.js
   iframe_postMessages.html
 
 [test_anonymousContent_api.html]
 [test_anonymousContent_append_after_reflow.html]
 [test_anonymousContent_insert.html]
@@ -296,16 +297,18 @@ skip-if = buildapp == 'mulet' || buildap
 [test_messagemanager_targetchain.html]
 [test_named_frames.html]
 [test_navigator_resolve_identity.html]
 [test_navigator_language.html]
 [test_openDialogChromeOnly.html]
 [test_open_null_features.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Fails on b2g-desktop, tracked in bug 1011874
 [test_postMessage_solidus.html]
+[test_pluginAudioNotification.html]
+skip-if = (buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android') # Plugins don't work on Android and/or B2G/Mulet
 [test_screen_orientation.html]
 [test_settimeout_extra_arguments.html]
 [test_settimeout_inner.html]
 [test_setting_opener.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_simplecontentpolicy.html]
 skip-if = e10s || buildapp == 'b2g'
 [test_url.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_pluginAudioNotification.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for audio controller in windows</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+</pre>
+<iframe></iframe>
+
+<script type="application/javascript">
+
+// Copied from /dom/plugins/test/mochitest/utils.js
+function getTestPlugin(pluginName) {
+  var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
+                                 .getService(SpecialPowers.Ci.nsIPluginHost);
+  var tags = ph.getPluginTags();
+  var name = pluginName || "Test Plug-in";
+  for (var tag of tags) {
+    if (tag.name == name) {
+      return tag;
+    }
+  }
+
+  ok(false, "Could not find plugin tag with plugin name '" + name + "'");
+  return null;
+}
+// Copied from /dom/plugins/test/mochitest/utils.js
+function setTestPluginEnabledState(newEnabledState, pluginName) {
+  var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
+  if (!oldEnabledState) {
+    return;
+  }
+  var plugin = getTestPlugin(pluginName);
+  while (plugin.enabledState != newEnabledState) {
+    // Run a nested event loop to wait for the preference change to
+    // propagate to the child. Yuck!
+    SpecialPowers.Services.tm.currentThread.processNextEvent(true);
+  }
+  SimpleTest.registerCleanupFunction(function() {
+    SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
+  });
+}
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+SimpleTest.waitForExplicitFinish();
+
+var expectedNotification = null;
+var iframe = null;
+
+var observer = {
+  observe: function(subject, topic, data) {
+    is(topic, "media-playback", "media-playback received");
+    is(data, expectedNotification, "This is the right notification");
+    SimpleTest.executeSoon(runTest);
+  }
+};
+
+var observerService = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+                                   .getService(SpecialPowers.Ci.nsIObserverService);
+
+var tests = [
+  function() {
+    iframe = document.querySelector("iframe");
+    SpecialPowers.pushPrefEnv({"set": [["media.useAudioChannelService", true]]}, runTest);
+  },
+
+  function() {
+    observerService.addObserver(observer, "media-playback", false);
+    ok(true, "Observer set");
+    runTest();
+  },
+
+  function() {
+    expectedNotification = 'active';
+    iframe.src = "file_pluginAudio.html";
+  },
+
+  function() {
+    ok(!iframe.contentWindow.pluginMuted(), "Plugin should not be muted");
+    iframe.contentWindow.toggleMuteState(true);
+    ok(iframe.contentWindow.pluginMuted(), "Plugin should be muted");
+    iframe.contentWindow.toggleMuteState(false);
+    ok(!iframe.contentWindow.pluginMuted(), "Plugin should not be muted");
+    runTest();
+  },
+
+  function() {
+    expectedNotification = 'inactive';
+    iframe.contentWindow.stopAudio();
+  },
+
+  function() {
+    observerService.removeObserver(observer, "media-playback");
+    ok(true, "Observer removed");
+    runTest();
+  }
+];
+
+function runTest() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+onload = runTest;
+
+</script>
+</body>
+</html>
+
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -874,16 +874,66 @@ template <class T>
 struct TypeNeedsOuterization
 {
   // We only need to outerize Window objects, so anything inheriting from
   // nsGlobalWindow (which inherits from EventTarget itself).
   static const bool value =
     IsBaseOf<nsGlobalWindow, T>::value || IsSame<EventTarget, T>::value;
 };
 
+#ifdef DEBUG
+template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
+struct CheckWrapperCacheTracing
+{
+  static inline void Check(T* aObject)
+  {
+  }
+};
+
+template<typename T>
+struct CheckWrapperCacheTracing<T, true>
+{
+  static void Check(T* aObject)
+  {
+    // Rooting analysis thinks QueryInterface may GC, but we're dealing with
+    // a subset of QueryInterface, C++ only types here.
+    JS::AutoSuppressGCAnalysis nogc;
+
+    nsWrapperCache* wrapperCacheFromQI = nullptr;
+    aObject->QueryInterface(NS_GET_IID(nsWrapperCache),
+                            reinterpret_cast<void**>(&wrapperCacheFromQI));
+
+    MOZ_ASSERT(wrapperCacheFromQI,
+               "Missing nsWrapperCache from QueryInterface implementation?");
+
+    if (!wrapperCacheFromQI->GetWrapperPreserveColor()) {
+      // Can't assert that we trace the wrapper, since we don't have any
+      // wrapper to trace.
+      return;
+    }
+
+    nsISupports* ccISupports = nullptr;
+    aObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
+                            reinterpret_cast<void**>(&ccISupports));
+    MOZ_ASSERT(ccISupports,
+               "nsWrapperCache object which isn't cycle collectable?");
+
+    nsXPCOMCycleCollectionParticipant* participant = nullptr;
+    CallQueryInterface(ccISupports, &participant);
+    MOZ_ASSERT(participant, "Can't QI to CycleCollectionParticipant?");
+
+    bool wasPreservingWrapper = wrapperCacheFromQI->PreservingWrapper();
+    wrapperCacheFromQI->SetPreservingWrapper(true);
+    wrapperCacheFromQI->CheckCCWrapperTraversal(ccISupports, participant);
+    wrapperCacheFromQI->SetPreservingWrapper(wasPreservingWrapper);
+  }
+};
+
+#endif
+
 template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
 MOZ_ALWAYS_INLINE bool
 DoGetOrCreateDOMReflector(JSContext* cx, T* value,
                           JS::MutableHandle<JS::Value> rval)
 {
   MOZ_ASSERT(value);
   JSObject* obj = value->GetWrapperPreserveColor();
   // We can get rid of this when we remove support for hasXPConnectImpls.
@@ -898,16 +948,22 @@ DoGetOrCreateDOMReflector(JSContext* cx,
 
     obj = value->WrapObject(cx, nullptr);
     if (!obj) {
       // At this point, obj is null, so just return false.
       // Callers seem to be testing JS_IsExceptionPending(cx) to
       // figure out whether WrapObject() threw.
       return false;
     }
+
+#ifdef DEBUG
+    if (IsBaseOf<nsWrapperCache, T>::value) {
+      CheckWrapperCacheTracing<T>::Check(value);
+    }
+#endif
   }
 
 #ifdef DEBUG
   const DOMJSClass* clasp = GetDOMClass(obj);
   // clasp can be null if the cache contained a non-DOM object.
   if (clasp) {
     // Some sanity asserts about our object.  Specifically:
     // 1)  If our class claims we're nsISupports, we better be nsISupports
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -695,25 +695,29 @@ BluetoothHfpManager::HandleVoiceConnecti
     // Notify BluetoothRilListener of service change
     mListener->ServiceChanged(aClientId, service);
   }
   mService = service;
 
   // Signal
   JS::Rooted<JS::Value> value(nsContentUtils::RootingCxForThread());
   voiceInfo->GetRelSignalStrength(&value);
-  NS_ENSURE_TRUE_VOID(value.isNumber());
-  mSignal = (int)ceil(value.toNumber() / 20.0);
+  if (value.isNumber()) {
+    mSignal = (int)ceil(value.toNumber() / 20.0);
+  }
 
   UpdateDeviceCIND();
 
   // Operator name
   nsCOMPtr<nsIMobileNetworkInfo> network;
   voiceInfo->GetNetwork(getter_AddRefs(network));
-  NS_ENSURE_TRUE_VOID(network);
+  if (!network) {
+    BT_LOGD("Unable to get network information");
+    return;
+  }
   network->GetLongName(mOperatorName);
 
   // According to GSM 07.07, "<format> indicates if the format is alphanumeric
   // or numeric; long alphanumeric format can be upto 16 characters long and
   // short format up to 8 characters (refer GSM MoU SE.13 [9])..."
   // However, we found that the operator name may sometimes be longer than 16
   // characters. After discussion, we decided to fix this here but not in RIL
   // or modem.
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
@@ -1003,16 +1003,21 @@ BluetoothAdapter::HandlePropertyChanged(
 
     // BluetoothAdapterAttribute properties
     if (IsAdapterAttributeChanged(type, arr[i].value())) {
       SetPropertyByValue(arr[i]);
       BT_APPEND_ENUM_STRING_FALLIBLE(types, BluetoothAdapterAttribute, type);
     }
   }
 
+  if (types.IsEmpty()) {
+    // No adapter attribute changed
+    return;
+  }
+
   DispatchAttributeEvent(types);
 }
 
 void
 BluetoothAdapter::HandleDeviceFound(const BluetoothValue& aValue)
 {
   MOZ_ASSERT(mDiscoveryHandleInUse);
   MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
@@ -1129,17 +1134,17 @@ BluetoothAdapter::HandleDeviceUnpaired(c
   BluetoothDeviceEventInit init;
   init.mAddress = deviceAddress;
   DispatchDeviceEvent(NS_LITERAL_STRING(DEVICE_UNPAIRED_ID), init);
 }
 
 void
 BluetoothAdapter::DispatchAttributeEvent(const Sequence<nsString>& aTypes)
 {
-  NS_ENSURE_TRUE_VOID(aTypes.Length());
+  MOZ_ASSERT(!aTypes.IsEmpty());
 
   BluetoothAttributeEventInit init;
   init.mAttrs = aTypes;
 
   nsRefPtr<BluetoothAttributeEvent> event =
     BluetoothAttributeEvent::Constructor(
       this, NS_LITERAL_STRING(ATTRIBUTE_CHANGED_ID), init);
 
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.h
@@ -280,16 +280,18 @@ private:
    * Handle "LeDeviceFound" bluetooth signal.
    *
    * @param aValue [in] Properties array of the scanned device.
    */
   void HandleLeDeviceFound(const BluetoothValue& aValue);
 
   /**
    * Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
+   *
+   * @param aTypes [in] Array of changed attributes. Must be non-empty.
    */
   void DispatchAttributeEvent(const Sequence<nsString>& aTypes);
 
   /**
    * Fire BluetoothDeviceEvent to trigger
    * ondeviceparied/ondeviceunpaired event handler.
    *
    * @param aType [in] Event type to fire
--- a/dom/bluetooth/bluetooth2/BluetoothDevice.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothDevice.cpp
@@ -288,23 +288,28 @@ BluetoothDevice::HandlePropertyChanged(c
 
     // BluetoothDeviceAttribute properties
     if (IsDeviceAttributeChanged(type, arr[i].value())) {
       SetPropertyByValue(arr[i]);
       BT_APPEND_ENUM_STRING_FALLIBLE(types, BluetoothDeviceAttribute, type);
     }
   }
 
+  if (types.IsEmpty()) {
+    // No device attribute changed
+    return;
+  }
+
   DispatchAttributeEvent(types);
 }
 
 void
 BluetoothDevice::DispatchAttributeEvent(const Sequence<nsString>& aTypes)
 {
-  NS_ENSURE_TRUE_VOID(aTypes.Length());
+  MOZ_ASSERT(!aTypes.IsEmpty());
 
   BluetoothAttributeEventInit init;
   init.mAttrs = aTypes;
   nsRefPtr<BluetoothAttributeEvent> event =
     BluetoothAttributeEvent::Constructor(
       this, NS_LITERAL_STRING(ATTRIBUTE_CHANGED_ID), init);
 
   DispatchTrustedEvent(event);
--- a/dom/bluetooth/bluetooth2/BluetoothDevice.h
+++ b/dom/bluetooth/bluetooth2/BluetoothDevice.h
@@ -112,16 +112,18 @@ private:
    * Handle "PropertyChanged" bluetooth signal.
    *
    * @param aValue [in] Array of changed properties
    */
   void HandlePropertyChanged(const BluetoothValue& aValue);
 
   /**
    * Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
+   *
+   * @param aTypes [in] Array of changed attributes. Must be non-empty.
    */
   void DispatchAttributeEvent(const Sequence<nsString>& aTypes);
 
   /**
    * Convert uint32_t to BluetoothDeviceType.
    *
    * @param aValue [in] uint32_t to convert
    */
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -646,33 +646,37 @@ BluetoothHfpManager::HandleVoiceConnecti
   if (service != sCINDItems[CINDType::SERVICE].value) {
     // Notify BluetoothRilListener of service change
     mListener->ServiceChanged(aClientId, service);
   }
   UpdateCIND(CINDType::SERVICE, service);
 
   JS::Rooted<JS::Value> value(nsContentUtils::RootingCxForThread());
   voiceInfo->GetRelSignalStrength(&value);
-  NS_ENSURE_TRUE_VOID(value.isNumber());
-  uint8_t signal = ceil(value.toNumber() / 20.0);
-  UpdateCIND(CINDType::SIGNAL, signal);
+  if (value.isNumber()) {
+    uint8_t signal = ceil(value.toNumber() / 20.0);
+    UpdateCIND(CINDType::SIGNAL, signal);
+  }
 
   /**
    * Possible return values for mode are:
    * - -1 (unknown): set mNetworkSelectionMode to 0 (auto)
    * - 0 (automatic): set mNetworkSelectionMode to 0 (auto)
    * - 1 (manual): set mNetworkSelectionMode to 1 (manual)
    */
   int32_t mode;
   connection->GetNetworkSelectionMode(&mode);
   mNetworkSelectionMode = (mode == 1) ? 1 : 0;
 
   nsCOMPtr<nsIMobileNetworkInfo> network;
   voiceInfo->GetNetwork(getter_AddRefs(network));
-  NS_ENSURE_TRUE_VOID(network);
+  if (!network) {
+    BT_LOGD("Unable to get network information");
+    return;
+  }
   network->GetLongName(mOperatorName);
 
   // According to GSM 07.07, "<format> indicates if the format is alphanumeric
   // or numeric; long alphanumeric format can be upto 16 characters long and
   // short format up to 8 characters (refer GSM MoU SE.13 [9])..."
   // However, we found that the operator name may sometimes be longer than 16
   // characters. After discussion, we decided to fix this here but not in RIL
   // or modem.
--- a/dom/cache/PrincipalVerifier.cpp
+++ b/dom/cache/PrincipalVerifier.cpp
@@ -126,29 +126,36 @@ PrincipalVerifier::VerifyOnMainThread()
   // We disallow null principal and unknown app IDs on the client side, but
   // double-check here.
   if (NS_WARN_IF(principal->GetIsNullPrincipal() ||
                  principal->GetUnknownAppId())) {
     DispatchToInitiatingThread(NS_ERROR_FAILURE);
     return;
   }
 
+  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
+  if (NS_WARN_IF(!ssm)) {
+    DispatchToInitiatingThread(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
+    return;
+  }
+
+  // Verify if a child process uses system principal, which is not allowed
+  // to prevent system principal is spoofed.
+  if (NS_WARN_IF(actor && ssm->IsSystemPrincipal(principal))) {
+    DispatchToInitiatingThread(NS_ERROR_FAILURE);
+    return;
+  }
+
   // Verify that a child process claims to own the app for this principal
   if (NS_WARN_IF(actor && !AssertAppPrincipal(actor, principal))) {
     DispatchToInitiatingThread(NS_ERROR_FAILURE);
     return;
   }
   actor = nullptr;
 
-  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
-  if (NS_WARN_IF(!ssm)) {
-    DispatchToInitiatingThread(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
-    return;
-  }
-
 #ifdef DEBUG
   // Sanity check principal origin by using it to construct a URI and security
   // checking it.  Don't do this for the system principal, though, as its origin
   // is a synthetic [System Principal] string.
   if (!ssm->IsSystemPrincipal(principal)) {
     nsAutoCString origin;
     rv = principal->GetOriginNoSuffix(origin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -1820,16 +1820,22 @@ WebGLContext::DidRefresh()
 }
 
 bool
 WebGLContext::TexImageFromVideoElement(const TexImageTarget texImageTarget,
                                        GLint level, GLenum internalFormat,
                                        GLenum format, GLenum type,
                                        mozilla::dom::Element& elt)
 {
+    if (type == LOCAL_GL_HALF_FLOAT_OES &&
+        !gl->IsExtensionSupported(gl::GLContext::OES_texture_half_float))
+    {
+        type = LOCAL_GL_HALF_FLOAT;
+    }
+
     if (!ValidateTexImageFormatAndType(format, type,
                                        WebGLTexImageFunc::TexImage,
                                        WebGLTexDimensions::Tex2D))
     {
         return false;
     }
 
     HTMLVideoElement* video = HTMLVideoElement::FromContentOrNull(&elt);
--- a/dom/html/test/test_fullscreen-api-race.html
+++ b/dom/html/test/test_fullscreen-api-race.html
@@ -107,23 +107,36 @@ function next() {
     SimpleTest.finish();
     return;
   }
   var win = test.openWinFunc();
   new Promise(resolve => {
     SimpleTest.waitForFocus(resolve, win, true);
   }).then(() => {
     return new Promise(resolve => {
-      function listener() {
+      var retried = false;
+      function listener(evt) {
+        if (!retried && evt.type == "mozfullscreenerror") {
+          todo(false, "Failed to enter fullscreen, but try again");
+          retried = true;
+          SimpleTest.waitForFocus(() => {
+            win.document.documentElement.mozRequestFullScreen();
+          }, win, true);
+          return;
+        }
         win.removeEventListener("mozfullscreenchange", listener);
+        win.removeEventListener("mozfullscreenerror", listener);
+        is(evt.type, "mozfullscreenchange", "Should get fullscreenchange");
         ok(win.document.mozFullScreen, "Should have entered fullscreen");
         ok(win.fullScreen, "The window should be in fullscreen");
         test.actionFunc(win).then(resolve);
       }
+      info("About to enter fullscreen");
       win.addEventListener("mozfullscreenchange", listener);
+      win.addEventListener("mozfullscreenerror", listener);
       win.document.documentElement.mozRequestFullScreen();
     });
   }).then(() => {
     ok(win.closed, "The window should have been closed");
     SimpleTest.waitForFocus(next);
   });
 }
 
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -677,17 +677,18 @@ IDBCursor::Delete(JSContext* aCx, ErrorR
   if (!mTransaction->IsWriteAllowed()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
     return nullptr;
   }
 
   if (IsSourceDeleted() ||
       !mHaveValue ||
       mType == Type_ObjectStoreKey ||
-      mType == Type_IndexKey) {
+      mType == Type_IndexKey ||
+      mContinueCalled) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
   MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index);
   MOZ_ASSERT(!mKey.IsUnset());
 
   IDBObjectStore* objectStore;
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1070,16 +1070,18 @@ void
 BlobDataFromBlobImpl(BlobImpl* aBlobImpl, BlobData& aBlobData)
 {
   MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
   MOZ_ASSERT(aBlobImpl);
 
   const nsTArray<nsRefPtr<BlobImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
 
   if (subBlobs) {
+    MOZ_ASSERT(subBlobs->Length());
+
     aBlobData = nsTArray<BlobData>();
 
     nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData();
     subBlobDatas.SetLength(subBlobs->Length());
 
     for (uint32_t count = subBlobs->Length(), index = 0;
          index < count;
          index++) {
@@ -1093,18 +1095,16 @@ BlobDataFromBlobImpl(BlobImpl* aBlobImpl
   if (remoteBlob) {
     BlobChild* actor = remoteBlob->GetBlobChild();
     MOZ_ASSERT(actor);
 
     aBlobData = actor->ParentID();
     return;
   }
 
-  MOZ_ASSERT(aBlobImpl->IsMemoryFile());
-
   ErrorResult rv;
   nsCOMPtr<nsIInputStream> inputStream;
   aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
   MOZ_ALWAYS_TRUE(!rv.Failed());
 
   DebugOnly<bool> isNonBlocking;
   MOZ_ASSERT(NS_SUCCEEDED(inputStream->IsNonBlocking(&isNonBlocking)));
   MOZ_ASSERT(isNonBlocking);
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -9,22 +9,24 @@
 #include "mozilla/Attributes.h"
 #include "gmp-entrypoints.h"
 #include "prlink.h"
 #include "prenv.h"
 #include "nsAutoPtr.h"
 
 #include <string>
 
-#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+#ifdef XP_WIN
+#include "windows.h"
+#ifdef MOZ_SANDBOX
 #include "mozilla/Scoped.h"
-#include "windows.h"
 #include <intrin.h>
 #include <assert.h>
 #endif
+#endif
 
 #if defined(HASH_NODE_ID_WITH_DEVICE_ID)
 // In order to provide EME plugins with a "device binding" capability,
 // in the parent we generate and store some random bytes as salt for every
 // (origin, urlBarOrigin) pair that uses EME. We store these bytes so
 // that every time we revisit the same origin we get the same salt.
 // We send this salt to the child on startup. The child collects some
 // device specific data and munges that with the salt to create the
@@ -191,37 +193,39 @@ GMPLoaderImpl::Load(const char* aUTF8Lib
       }
     }
   } else
 #endif
   {
     nodeId = std::string(aOriginSalt, aOriginSalt + aOriginSaltLen);
   }
 
-#if defined(XP_WIN) && defined(MOZ_SANDBOX)
-  // If the GMP DLL is a side-by-side assembly with static imports then the DLL
-  // loader will attempt to create an activation context which will fail because
-  // of the sandbox. If we create an activation context before we start the
-  // sandbox then this one will get picked up by the DLL loader.
+#ifdef XP_WIN
   int pathLen = MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, nullptr, 0);
   if (pathLen == 0) {
     return false;
   }
 
   nsAutoArrayPtr<wchar_t> widePath(new wchar_t[pathLen]);
   if (MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, widePath, pathLen) == 0) {
     return false;
   }
 
+#ifdef MOZ_SANDBOX
+  // If the GMP DLL is a side-by-side assembly with static imports then the DLL
+  // loader will attempt to create an activation context which will fail because
+  // of the sandbox. If we create an activation context before we start the
+  // sandbox then this one will get picked up by the DLL loader.
   ACTCTX actCtx = { sizeof(actCtx) };
   actCtx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID;
   actCtx.lpSource = widePath;
   actCtx.lpResourceName = ISOLATIONAWARE_MANIFEST_RESOURCE_ID;
   ScopedActCtxHandle actCtxHandle(CreateActCtx(&actCtx));
 #endif
+#endif
 
   // Start the sandbox now that we've generated the device bound node id.
   // This must happen after the node id is bound to the device id, as
   // generating the device id requires privileges.
   if (mSandboxStarter && !mSandboxStarter->Start(aUTF8LibPath)) {
     return false;
   }
 
--- a/dom/media/mediasource/ContainerParser.cpp
+++ b/dom/media/mediasource/ContainerParser.cpp
@@ -256,65 +256,96 @@ private:
 #ifdef MOZ_FMP4
 class MP4ContainerParser : public ContainerParser {
 public:
   explicit MP4ContainerParser(const nsACString& aType)
     : ContainerParser(aType)
     , mMonitor("MP4ContainerParser Index Monitor")
   {}
 
-  bool HasAtom(const mp4_demuxer::AtomType& aAtom, const MediaByteBuffer* aData) {
-    mp4_demuxer::ByteReader reader(aData);
-
-    while (reader.Remaining() >= 8) {
-      uint64_t size = reader.ReadU32();
-      const uint8_t* typec = reader.Peek(4);
-      uint32_t type = reader.ReadU32();
-      MSE_DEBUGV(MP4ContainerParser ,"Checking atom:'%c%c%c%c'",
-                typec[0], typec[1], typec[2], typec[3]);
-      if (mp4_demuxer::AtomType(type) == aAtom) {
-        reader.DiscardRemaining();
-        return true;
-      }
-      if (size == 1) {
-        // 64 bits size.
-        if (!reader.CanReadType<uint64_t>()) {
-          break;
-        }
-        size = reader.ReadU64();
-      } else if (size == 0) {
-        // Atom extends to the end of the buffer, it can't have what we're
-        // looking for.
-        break;
-      }
-      if (reader.Remaining() < size - 8) {
-        // Incomplete atom.
-        break;
-      }
-      reader.Read(size - 8);
-    }
-    reader.DiscardRemaining();
-    return false;
-  }
-
   bool IsInitSegmentPresent(MediaByteBuffer* aData) override
   {
     ContainerParser::IsInitSegmentPresent(aData);
     // Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
     // file is the 'ftyp' atom followed by a file type. We just check for a
     // vaguely valid 'ftyp' atom.
-    return HasAtom(mp4_demuxer::AtomType("ftyp"), aData);
+    AtomParser parser(mType, aData);
+    return parser.StartWithInitSegment();
   }
 
   bool IsMediaSegmentPresent(MediaByteBuffer* aData) override
   {
-    ContainerParser::IsMediaSegmentPresent(aData);
-    return HasAtom(mp4_demuxer::AtomType("moof"), aData);
+    AtomParser parser(mType, aData);
+    return parser.StartWithMediaSegment();
   }
 
+private:
+  class AtomParser {
+  public:
+    AtomParser(const nsACString& aType, const MediaByteBuffer* aData)
+    {
+      const nsCString mType(aType); // for logging macro.
+      mp4_demuxer::ByteReader reader(aData);
+      mp4_demuxer::AtomType initAtom("ftyp");
+      mp4_demuxer::AtomType mediaAtom("moof");
+
+      while (reader.Remaining() >= 8) {
+        uint64_t size = reader.ReadU32();
+        const uint8_t* typec = reader.Peek(4);
+        uint32_t type = reader.ReadU32();
+        MSE_DEBUGV(AtomParser ,"Checking atom:'%c%c%c%c'",
+                   typec[0], typec[1], typec[2], typec[3]);
+        if (mInitOffset.isNothing() &&
+            mp4_demuxer::AtomType(type) == initAtom) {
+          mInitOffset = Some(reader.Offset());
+        }
+        if (mMediaOffset.isNothing() &&
+            mp4_demuxer::AtomType(type) == mediaAtom) {
+          mMediaOffset = Some(reader.Offset());
+        }
+        if (mInitOffset.isSome() && mMediaOffset.isSome()) {
+          // We have everything we need.
+          break;
+        }
+        if (size == 1) {
+          // 64 bits size.
+          if (!reader.CanReadType<uint64_t>()) {
+            break;
+          }
+          size = reader.ReadU64();
+        } else if (size == 0) {
+          // Atom extends to the end of the buffer, it can't have what we're
+          // looking for.
+          break;
+        }
+        if (reader.Remaining() < size - 8) {
+          // Incomplete atom.
+          break;
+        }
+        reader.Read(size - 8);
+      }
+      reader.DiscardRemaining();
+    }
+
+    bool StartWithInitSegment()
+    {
+      return mInitOffset.isSome() &&
+        (mMediaOffset.isNothing() || mInitOffset.ref() < mMediaOffset.ref());
+    }
+    bool StartWithMediaSegment()
+    {
+      return mMediaOffset.isSome() &&
+        (mInitOffset.isNothing() || mMediaOffset.ref() < mInitOffset.ref());
+    }
+  private:
+    Maybe<size_t> mInitOffset;
+    Maybe<size_t> mMediaOffset;
+  };
+
+public:
   bool ParseStartAndEndTimestamps(MediaByteBuffer* aData,
                                   int64_t& aStart, int64_t& aEnd) override
   {
     MonitorAutoLock mon(mMonitor); // We're not actually racing against anything,
                                    // but mParser requires us to hold a monitor.
     bool initSegment = IsInitSegmentPresent(aData);
     if (initSegment) {
       mResource = new SourceBufferResource(NS_LITERAL_CSTRING("video/mp4"));
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -776,19 +776,18 @@ TrackBuffersManager::CreateDemuxerforMIM
   NS_WARNING("Not supported (yet)");
   return;
 }
 
 void
 TrackBuffersManager::InitializationSegmentReceived()
 {
   MOZ_ASSERT(mParser->HasCompleteInitData());
-  mInitData = mParser->InitData();
   mCurrentInputBuffer = new SourceBufferResource(mType);
-  mCurrentInputBuffer->AppendData(mInitData);
+  mCurrentInputBuffer->AppendData(mParser->InitData());
   uint32_t length =
     mParser->InitSegmentRange().mEnd - (mProcessedInput - mInputBuffer->Length());
   if (mInputBuffer->Length() == length) {
     mInputBuffer = nullptr;
   } else {
     mInputBuffer->RemoveElementsAt(0, length);
   }
   CreateDemuxerforMIMEType();
@@ -873,17 +872,17 @@ TrackBuffersManager::OnDemuxerInitDone(n
     mVideoTracks.mNeedRandomAccessPoint = true;
     mAudioTracks.mNeedRandomAccessPoint = true;
 
     mVideoTracks.mLongestFrameDuration = mVideoTracks.mLastFrameDuration;
     mAudioTracks.mLongestFrameDuration = mAudioTracks.mLastFrameDuration;
   }
 
   // 4. Let active track flag equal false.
-  mActiveTrack = false;
+  bool activeTrack = false;
 
   // Increase our stream id.
   uint32_t streamID = sStreamSourceID++;
 
   // 5. If the first initialization segment received flag is false, then run the following steps:
   if (!mFirstInitializationSegmentReceived) {
     mAudioTracks.mNumTracks = numAudios;
     // TODO:
@@ -906,17 +905,17 @@ TrackBuffersManager::OnDemuxerInitDone(n
       //   2. Let new audio track be a new AudioTrack object.
       //   3. Generate a unique ID and assign it to the id property on new audio track.
       //   4. Assign audio language to the language property on new audio track.
       //   5. Assign audio label to the label property on new audio track.
       //   6. Assign current audio kind to the kind property on new audio track.
       //   7. If audioTracks.length equals 0, then run the following steps:
       //     1. Set the enabled property on new audio track to true.
       //     2. Set active track flag to true.
-      mActiveTrack = true;
+      activeTrack = true;
       //   8. Add new audio track to the audioTracks attribute on this SourceBuffer object.
       //   9. Queue a task to fire a trusted event named addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object referenced by the audioTracks attribute on this SourceBuffer object.
       //   10. Add new audio track to the audioTracks attribute on the HTMLMediaElement.
       //   11. Queue a task to fire a trusted event named addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent interface, at the AudioTrackList object referenced by the audioTracks attribute on the HTMLMediaElement.
       mAudioTracks.mBuffers.AppendElement(TrackBuffer());
       // 10. Add the track description for this track to the track buffer.
       mAudioTracks.mInfo = new SharedTrackInfo(info.mAudio, streamID);
       mAudioTracks.mLastInfo = mAudioTracks.mInfo;
@@ -938,29 +937,32 @@ TrackBuffersManager::OnDemuxerInitDone(n
       //   2. Let new video track be a new VideoTrack object.
       //   3. Generate a unique ID and assign it to the id property on new video track.
       //   4. Assign video language to the language property on new video track.
       //   5. Assign video label to the label property on new video track.
       //   6. Assign current video kind to the kind property on new video track.
       //   7. If videoTracks.length equals 0, then run the following steps:
       //     1. Set the selected property on new video track to true.
       //     2. Set active track flag to true.
-      mActiveTrack = true;
+      activeTrack = true;
       //   8. Add new video track to the videoTracks attribute on this SourceBuffer object.
       //   9. Queue a task to fire a trusted event named addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object referenced by the videoTracks attribute on this SourceBuffer object.
       //   10. Add new video track to the videoTracks attribute on the HTMLMediaElement.
       //   11. Queue a task to fire a trusted event named addtrack, that does not bubble and is not cancelable, and that uses the TrackEvent interface, at the VideoTrackList object referenced by the videoTracks attribute on the HTMLMediaElement.
       mVideoTracks.mBuffers.AppendElement(TrackBuffer());
       // 10. Add the track description for this track to the track buffer.
       mVideoTracks.mInfo = new SharedTrackInfo(info.mVideo, streamID);
       mVideoTracks.mLastInfo = mVideoTracks.mInfo;
     }
     // 4. For each text track in the initialization segment, run following steps:
     // 5. If active track flag equals true, then run the following steps:
     // This is handled by SourceBuffer once the promise is resolved.
+    if (activeTrack) {
+      mActiveTrack = true;
+    }
 
     // 6. Set first initialization segment received flag to true.
     mFirstInitializationSegmentReceived = true;
   } else {
     mAudioTracks.mLastInfo = new SharedTrackInfo(info.mAudio, streamID);
     mVideoTracks.mLastInfo = new SharedTrackInfo(info.mVideo, streamID);
   }
 
@@ -980,16 +982,19 @@ TrackBuffersManager::OnDemuxerInitDone(n
     mEncrypted = true;
   }
 
   {
     MonitorAutoLock mon(mMonitor);
     mInfo = info;
   }
 
+  // We now have a valid init data ; we can store it for later use.
+  mInitData = mParser->InitData();
+
   // 3. Remove the initialization segment bytes from the beginning of the input buffer.
   // This step has already been done in InitializationSegmentReceived when we
   // transferred the content into mCurrentInputBuffer.
   mCurrentInputBuffer->EvictAll();
   RecreateParser();
 
   // 4. Set append state to WAITING_FOR_SEGMENT.
   SetAppendState(AppendState::WAITING_FOR_SEGMENT);
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -100,8 +100,10 @@ skip-if = ((os == "win" && os_version ==
 [test_TruncatedDuration.html]
 skip-if = true # bug 1182946
 [test_TruncatedDuration_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_WaitingOnMissingData.html]
 skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized.
 [test_WaitingOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
+[test_WaitingToEndedTransition_mp4.html]
+skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_WaitingToEndedTransition_mp4.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252">
+  <title>MSE: |waiting| event when source data is missing</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test"><script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+runWithMSE(function(ms, el) {
+  el.controls = true;
+  once(ms, 'sourceopen').then(function() {
+    ok(true, "Receive a sourceopen event");
+    var audiosb = ms.addSourceBuffer("audio/mp4");
+    var videosb = ms.addSourceBuffer("video/mp4");
+    fetchAndLoad(audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4')
+    .then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', ['init'], '.mp4'))
+    .then(fetchAndLoad.bind(null, audiosb, 'bipbop/bipbop_audio', range(1, 5), '.m4s'))
+    .then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', range(1, 6), '.m4s'))
+    .then(function() {
+      // HTMLMediaElement fires 'waiting' if somebody invokes |play()| before the MDSM
+      // has notified it of available data. Make sure that we get 'playing' before
+      // we starting waiting for 'waiting'.
+      info("Invoking play()");
+      var p = once(el, 'playing');
+      el.play();
+      return p;
+    }).then(function() {
+      ok(true, "Video playing. It should play for a bit, then fire 'waiting'");
+      var p = once(el, 'waiting');
+      el.play();
+      return p;
+    }).then(function() {
+      // currentTime is based on the current video frame, so if the audio ends just before
+      // the next video frame, currentTime can be up to 1 frame's worth earlier than
+      // min(audioEnd, videoEnd).
+      isfuzzy(el.currentTime, Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 1/60,
+              1/30, "Got a waiting event at " + el.currentTime);
+    }).then(function() {
+      var p = once(el, 'ended');
+      ms.endOfStream();
+      return p;
+    }).then(function() {
+      is(el.duration, 4.005, "Video has correct duration: " + el.duration);
+      is(el.currentTime, el.duration, "Video has correct currentTime.");
+      SimpleTest.finish();
+    });
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/995289.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+var r0=new AudioContext();
+var r5=r0.createOscillator();
+var r6=r0.createPeriodicWave(new Float32Array(1),new Float32Array(1));
+r5.frequency.value = 4294967295;
+r5.start(0);
+r5.setPeriodicWave(r6);
+</script>
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -62,16 +62,17 @@ load 925619-2.html
 load 926619.html
 load 933151.html
 load 933156.html
 load 944851.html
 load 952756.html
 load 966636.html
 load 986901.html
 load 990794.html
+load 995289.html
 load 1012609.html
 load 1015662.html
 load 1020205.html
 load 1041466.html
 load 1045650.html
 skip-if(Android||B2G) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
 load analyser-channels-1.html
 load buffer-source-ended-1.html
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -96,22 +96,27 @@ AudioContext::AudioContext(nsPIDOMWindow
   , mAudioContextState(AudioContextState::Suspended)
   , mNumberOfChannels(aNumberOfChannels)
   , mNodeCount(0)
   , mIsOffline(aIsOffline)
   , mIsStarted(!aIsOffline)
   , mIsShutDown(false)
   , mCloseCalled(false)
 {
-  aWindow->AddAudioContext(this);
+  bool mute = aWindow->AddAudioContext(this);
 
   // Note: AudioDestinationNode needs an AudioContext that must already be
   // bound to the window.
   mDestination = new AudioDestinationNode(this, aIsOffline, aChannel,
                                           aNumberOfChannels, aLength, aSampleRate);
+
+  // The context can't be muted until it has a destination.
+  if (mute) {
+    Mute();
+  }
 }
 
 void
 AudioContext::Init()
 {
   // We skip calling SetIsOnlyNodeForContext and the creation of the
   // audioChannelAgent during mDestination's constructor, because we can only
   // call them after mDestination has been set up.
--- a/dom/media/webaudio/WaveShaperNode.cpp
+++ b/dom/media/webaudio/WaveShaperNode.cpp
@@ -319,20 +319,24 @@ WaveShaperNode::SetCurve(const Nullable<
     floats.ComputeLengthAndData();
 
     uint32_t argLength = floats.Length();
     if (argLength < 2) {
       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
       return;
     }
 
-    mCurve = floats.Obj();
+    if (!curve.SetLength(argLength, fallible)) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
 
-    curve.SetLength(argLength);
     PodCopy(curve.Elements(), floats.Data(), floats.Length());
+
+    mCurve = floats.Obj();
   } else {
     mCurve = nullptr;
   }
 
   AudioNodeStream* ns = mStream;
   MOZ_ASSERT(ns, "Why don't we have a stream here?");
   ns->SetRawArrayData(curve);
 }
--- a/dom/media/webaudio/blink/PeriodicWave.cpp
+++ b/dom/media/webaudio/blink/PeriodicWave.cpp
@@ -142,17 +142,17 @@ void PeriodicWave::waveDataForFundamenta
     // So the lower table data will have a larger range index.
     unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
     unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
 
     lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
     higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
 
     // Ranges from 0 -> 1 to interpolate between lower -> higher.
-    tableInterpolationFactor = pitchRange - rangeIndex1;
+    tableInterpolationFactor = rangeIndex2 - pitchRange;
 }
 
 unsigned PeriodicWave::maxNumberOfPartials() const
 {
     return m_periodicWaveSize / 2;
 }
 
 unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -154,16 +154,17 @@ skip-if = (toolkit == 'gonk' && !debug) 
 [test_pannerNodeAbove.html]
 [test_pannerNodeChannelCount.html]
 [test_pannerNodeHRTFSymmetry.html]
 [test_pannerNodeTail.html]
 [test_pannerNode_maxDistance.html]
 [test_stereoPannerNode.html]
 [test_stereoPannerNodePassThrough.html]
 [test_periodicWave.html]
+[test_periodicWaveBandLimiting.html]
 [test_scriptProcessorNode.html]
 [test_scriptProcessorNodeChannelCount.html]
 [test_scriptProcessorNodePassThrough.html]
 [test_scriptProcessorNode_playbackTime1.html]
 [test_scriptProcessorNodeZeroInputOutput.html]
 [test_scriptProcessorNodeNotConnected.html]
 [test_singleSourceDest.html]
 [test_stereoPanningWithGain.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_periodicWaveBandLimiting.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<title>Test effect of band limiting on PeriodicWave signals</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const sampleRate = 48000;
+const bufferSize = 12800;
+const epsilon = 0.01;
+
+// "All implementations must support arrays up to at least 8192", but the
+// linear interpolation of the current implementation distorts the higher
+// frequency components too much to pass this test.
+const frequencyIndexMax = 100;
+
+// A set of oscillators are created near the Nyquist frequency.
+// These are factors giving each oscillator frequency relative to the Nyquist.
+// The first is an octave below Nyquist and the last is just above.
+const OCTAVE_BELOW = 0;
+const HALF_BELOW = 1;
+const NEAR_BELOW = 2;
+const ABOVE = 3;
+const oscillatorFactors = [0.5, Math.sqrt(0.5), 0.99, 1.01];
+const oscillatorCount = oscillatorFactors.length;
+
+// Return magnitude relative to unit sine wave
+function magnitude(array) {
+  var mag = 0
+  for (var i = 0; i < array.length; ++i) {
+    sample = array[i];
+    mag += sample * sample;
+  }
+  return Math.sqrt(2 * mag / array.length);
+}
+
+function test_frequency_index(frequencyIndex) {
+
+  var context =
+    new OfflineAudioContext(oscillatorCount, bufferSize, sampleRate);
+
+  var merger = context.createChannelMerger(oscillatorCount);
+  merger.connect(context.destination);
+
+  var real = new Float32Array(frequencyIndex + 1);
+  real[frequencyIndex] = 1;
+  var image = new Float32Array(real.length);
+  var wave = context.createPeriodicWave(real, image);
+
+  for (var i = 0; i < oscillatorCount; ++i) {
+    var oscillator = context.createOscillator();
+    oscillator.frequency.value =
+      oscillatorFactors[i] * sampleRate / (2 * frequencyIndex);
+    oscillator.connect(merger, 0, i);
+    oscillator.setPeriodicWave(wave);
+    oscillator.start(0);
+  }
+
+  return context.startRendering().
+    then((buffer) => {
+      assert_equals(buffer.numberOfChannels, oscillatorCount);
+      var magnitudes = [];
+      for (var i = 0; i < oscillatorCount; ++i) {
+        magnitudes[i] = magnitude(buffer.getChannelData(i));
+      }
+      // Unaffected by band-limiting one octave below Nyquist.
+      assert_approx_equals(magnitudes[OCTAVE_BELOW], 1, epsilon,
+                           "magnitude with frequency octave below Nyquist");
+      // Still at least half the amplitude at half octave below Nyquist.
+      assert_greater_than(magnitudes[HALF_BELOW], 0.5 * (1 - epsilon),
+                          "magnitude with frequency half octave below Nyquist");
+      // Approaching zero or zero near Nyquist.
+      assert_less_than(magnitudes[NEAR_BELOW], 0.1,
+                       "magnitude with frequency near Nyquist");
+      assert_equals(magnitudes[ABOVE], 0,
+                   "magnitude with frequency above Nyquist");
+    });
+}
+
+// The 5/4 ratio with rounding up provides sampling across a range of
+// octaves and offsets within octaves.
+for (var frequencyIndex = 1;
+     frequencyIndex < frequencyIndexMax;
+     frequencyIndex = Math.floor((5 * frequencyIndex + 3) / 4)) {
+  promise_test(() => test_frequency_index(frequencyIndex),
+               "Frequency " + frequencyIndex);
+}
+</script>
--- a/dom/media/webspeech/synth/windows/SapiService.cpp
+++ b/dom/media/webspeech/synth/windows/SapiService.cpp
@@ -127,16 +127,18 @@ SapiCallback::OnSpeechEvent(const SPEVEN
     mTask->DispatchBoundary(NS_LITERAL_STRING("word"),
                             GetTickCount() - mStartingTime, mCurrentIndex);
     break;
   case SPEI_SENTENCE_BOUNDARY:
     mCurrentIndex = static_cast<ULONG>(speechEvent.lParam) - mTextOffset;
     mTask->DispatchBoundary(NS_LITERAL_STRING("sentence"),
                             GetTickCount() - mStartingTime, mCurrentIndex);
     break;
+  default:
+    break;
   }
 }
 
 // static
 void __stdcall
 SapiService::SpeechEventCallback(WPARAM aWParam, LPARAM aLParam)
 {
   nsRefPtr<SapiService> service = (SapiService*) aWParam;
@@ -175,17 +177,17 @@ SapiService::~SapiService()
 
 bool
 SapiService::Init()
 {
   MOZ_ASSERT(!mInitialized);
 
   if (Preferences::GetBool("media.webspeech.synth.test")) {
     // When enabled, we shouldn't add OS backend (Bug 1160844)
-    return nullptr;
+    return false;
   }
 
   if (FAILED(CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL, IID_ISpVoice,
                               getter_AddRefs(mSapiClient)))) {
     return false;
   }
 
   // Set interest for all the events we are interested in
--- a/dom/plugins/base/npapi.h
+++ b/dom/plugins/base/npapi.h
@@ -406,17 +406,19 @@ typedef enum {
 #endif
 #if defined(XP_MACOSX)
 #ifndef NP_NO_CARBON
   , NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */
 #endif
   , NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */
   , NPNVsupportsUpdatedCocoaTextInputBool = 3002 /* TRUE if the browser supports the updated
                                                     Cocoa text input specification. */
+#endif
   , NPNVmuteAudioBool = 4000 /* Request that the browser wants to mute or unmute the plugin */
+#if defined(XP_MACOSX)
   , NPNVsupportsCompositingCoreAnimationPluginsBool = 74656 /* TRUE if the browser supports
                                                                CA model compositing */
 #endif
 } NPNVariable;
 
 typedef enum {
   NPNURLVCookie = 501,
   NPNURLVProxy
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -101,16 +101,18 @@ using mozilla::plugins::PluginModuleCont
 #include <android/log.h>
 #include "android_npapi.h"
 #include "ANPBase.h"
 #include "AndroidBridge.h"
 #undef LOG
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args)
 #endif
 
+#include "nsIAudioChannelAgent.h"
+
 using namespace mozilla;
 using namespace mozilla::plugins::parent;
 
 // We should make this const...
 static NPNetscapeFuncs sBrowserFuncs = {
   sizeof(sBrowserFuncs),
   (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR,
   _geturl,
@@ -2397,16 +2399,56 @@ NPError
       return NPERR_NO_ERROR;
     }
 
     case NPPVpluginUsesDOMForCursorBool: {
       bool useDOMForCursor = (result != nullptr);
       return inst->SetUsesDOMForCursor(useDOMForCursor);
     }
 
+    case NPPVpluginIsPlayingAudio: {
+      bool isMuted = !result;
+
+      nsNPAPIPluginInstance* inst = (nsNPAPIPluginInstance*) npp->ndata;
+      MOZ_ASSERT(inst);
+
+      if (isMuted && !inst->HasAudioChannelAgent()) {
+        return NPERR_NO_ERROR;
+      }
+
+      nsCOMPtr<nsIAudioChannelAgent> agent;
+      nsresult rv = inst->GetOrCreateAudioChannelAgent(getter_AddRefs(agent));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NPERR_NO_ERROR;
+      }
+
+      MOZ_ASSERT(agent);
+
+      if (isMuted) {
+        rv = agent->NotifyStoppedPlaying();
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NPERR_NO_ERROR;
+        }
+      } else {
+        float volume = 0.0;
+        bool muted = true;
+        rv = agent->NotifyStartedPlaying(&volume, &muted);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NPERR_NO_ERROR;
+        }
+
+        rv = inst->WindowVolumeChanged(volume, muted);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NPERR_NO_ERROR;
+        }
+      }
+
+      return NPERR_NO_ERROR;
+    }
+
 #ifndef MOZ_WIDGET_ANDROID
     // On android, their 'drawing model' uses the same constant!
     case NPPVpluginDrawingModel: {
       if (inst) {
         inst->SetDrawingModel((NPDrawingModel)NS_PTR_TO_INT32(result));
         return NPERR_NO_ERROR;
       }
       else {
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -35,16 +35,17 @@
 #include "nsSize.h"
 #include "nsNetCID.h"
 #include "nsIContent.h"
 #include "nsVersionComparator.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "nsILoadContext.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
+#include "AudioChannelService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "ANPBase.h"
 #include <android/log.h>
 #include "android_npapi.h"
@@ -164,17 +165,17 @@ static std::map<NPP, nsNPAPIPluginInstan
 #endif
 
 using namespace mozilla;
 using namespace mozilla::plugins::parent;
 using namespace mozilla::layers;
 
 static NS_DEFINE_IID(kIOutputStreamIID, NS_IOUTPUTSTREAM_IID);
 
-NS_IMPL_ISUPPORTS0(nsNPAPIPluginInstance)
+NS_IMPL_ISUPPORTS(nsNPAPIPluginInstance, nsIAudioChannelAgentCallback)
 
 nsNPAPIPluginInstance::nsNPAPIPluginInstance()
   : mDrawingModel(kDefaultDrawingModel)
 #ifdef MOZ_WIDGET_ANDROID
   , mANPDrawingModel(0)
   , mFullScreenOrientation(dom::eScreenOrientation_LandscapePrimary)
   , mWakeLocked(false)
   , mFullScreen(false)
@@ -248,16 +249,17 @@ nsNPAPIPluginInstance::~nsNPAPIPluginIns
 
 uint32_t nsNPAPIPluginInstance::gInUnsafePluginCalls = 0;
 
 void
 nsNPAPIPluginInstance::Destroy()
 {
   Stop();
   mPlugin = nullptr;
+  mAudioChannelAgent = nullptr;
 
 #if MOZ_WIDGET_ANDROID
   if (mContentSurface)
     mContentSurface->SetFrameAvailableCallback(nullptr);
 
   mContentTexture = nullptr;
   mContentSurface = nullptr;
 
@@ -1783,8 +1785,76 @@ nsNPAPIPluginInstance::GetRunID(uint32_t
 
   PluginLibrary* library = mPlugin->GetLibrary();
   if (!library) {
     return NS_ERROR_FAILURE;
   }
 
   return library->GetRunID(aRunID);
 }
+
+nsresult
+nsNPAPIPluginInstance::GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent)
+{
+  if (!mAudioChannelAgent) {
+    nsresult rv;
+    mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
+    if (NS_WARN_IF(!mAudioChannelAgent)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsPIDOMWindow> window = GetDOMWindow();
+    if (NS_WARN_IF(!window)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    rv = mAudioChannelAgent->Init(window->GetCurrentInnerWindow(),
+                                 (int32_t)AudioChannelService::GetDefaultAudioChannel(),
+                                 this);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  nsCOMPtr<nsIAudioChannelAgent> agent = mAudioChannelAgent;
+  agent.forget(aAgent);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowVolumeChanged(float aVolume, bool aMuted)
+{
+  // We just support mute/unmute
+  nsresult rv = SetMuted(aMuted);
+  NS_WARN_IF(NS_FAILED(rv));
+  return rv;
+}
+
+NS_IMETHODIMP
+nsNPAPIPluginInstance::WindowAudioCaptureChanged()
+{
+  return NS_OK;
+}
+
+nsresult
+nsNPAPIPluginInstance::SetMuted(bool aIsMuted)
+{
+  if (RUNNING != mRunning)
+    return NS_OK;
+
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance informing plugin of mute state change this=%p\n",this));
+
+  if (!mPlugin || !mPlugin->GetLibrary())
+    return NS_ERROR_FAILURE;
+
+  NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
+
+  if (!pluginFunctions->setvalue)
+    return NS_ERROR_FAILURE;
+
+  PluginDestructionGuard guard(this);
+
+  NPError error;
+  NPBool value = static_cast<NPBool>(aIsMuted);
+  NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVmuteAudioBool, &value), this,
+                          NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
+  return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
+}
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -12,16 +12,17 @@
 #include "nsPIDOMWindow.h"
 #include "nsITimer.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsIURI.h"
 #include "nsIChannel.h"
 #include "nsHashKeys.h"
 #include <prinrval.h>
 #include "js/TypeDecls.h"
+#include "nsIAudioChannelAgent.h"
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsAutoPtr.h"
 #include "nsIRunnable.h"
 #include "GLContextTypes.h"
 #include "AndroidSurfaceTexture.h"
 #include "AndroidBridge.h"
 #include <map>
 class PluginEventRunnable;
@@ -69,23 +70,24 @@ public:
   NPP npp;
   uint32_t id;
   nsCOMPtr<nsITimer> timer;
   void (*callback)(NPP npp, uint32_t timerID);
   bool inCallback;
   bool needUnschedule;
 };
 
-class nsNPAPIPluginInstance : public nsISupports
+class nsNPAPIPluginInstance final : public nsIAudioChannelAgentCallback
 {
 private:
   typedef mozilla::PluginLibrary PluginLibrary;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
 
   nsresult Initialize(nsNPAPIPlugin *aPlugin, nsPluginInstanceOwner* aOwner, const nsACString& aMIMEType);
   nsresult Start();
   nsresult Stop();
   nsresult SetWindow(NPWindow* window);
   nsresult NewStreamFromPlugin(const char* type, const char* target, nsIOutputStream* *result);
   nsresult Print(NPPrint* platformPrint);
   nsresult HandleEvent(void* event, int16_t* result,
@@ -112,16 +114,25 @@ public:
   nsresult GetPluginAPIVersion(uint16_t* version);
   nsresult InvalidateRect(NPRect *invalidRect);
   nsresult InvalidateRegion(NPRegion invalidRegion);
   nsresult GetMIMEType(const char* *result);
   nsresult GetJSContext(JSContext* *outContext);
   nsPluginInstanceOwner* GetOwner();
   void SetOwner(nsPluginInstanceOwner *aOwner);
 
+  bool HasAudioChannelAgent() const
+  {
+    return !!mAudioChannelAgent;
+  }
+
+  nsresult GetOrCreateAudioChannelAgent(nsIAudioChannelAgent** aAgent);
+
+  nsresult SetMuted(bool aIsMuted);
+
   nsNPAPIPlugin* GetPlugin();
 
   nsresult GetNPP(NPP * aNPP);
 
   NPError SetWindowless(bool aWindowless);
 
   NPError SetTransparent(bool aTransparent);
 
@@ -399,16 +410,18 @@ private:
 
   static uint32_t gInUnsafePluginCalls;
 
   // The arrays can only be released when the plugin instance is destroyed,
   // because the plugin, in in-process mode, might keep a reference to them.
   uint32_t mCachedParamLength;
   char **mCachedParamNames;
   char **mCachedParamValues;
+
+  nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
 };
 
 // On Android, we need to guard against plugin code leaking entries in the local
 // JNI ref table. See https://bugzilla.mozilla.org/show_bug.cgi?id=780831#c21
 #ifdef MOZ_WIDGET_ANDROID
   #define MAIN_THREAD_JNI_REF_GUARD mozilla::AutoLocalJNIFrame jniFrame
 #else
   #define MAIN_THREAD_JNI_REF_GUARD
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -82,16 +82,18 @@ child:
 
   intr NPP_GetValue_NPPVpluginScriptableNPObject()
     returns (nullable PPluginScriptableObject value, NPError result);
 
   intr NPP_SetValue_NPNVprivateModeBool(bool value) returns (NPError result);
   intr NPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId()
     returns (nsCString plug_id, NPError result);
 
+  intr NPP_SetValue_NPNVmuteAudioBool(bool muted) returns (NPError result);
+
   intr NPP_HandleEvent(NPRemoteEvent event)
     returns (int16_t handled);
   // special cases where we need to a shared memory buffer
   intr NPP_HandleEvent_Shmem(NPRemoteEvent event, Shmem buffer)
     returns (int16_t handled, Shmem rtnbuffer);
   // special cases where we need an iosurface
   intr NPP_HandleEvent_IOSurface(NPRemoteEvent event, uint32_t surfaceid)
     returns (int16_t handled);
@@ -145,16 +147,18 @@ parent:
   intr NPN_SetValue_NPPVpluginTransparent(bool transparent)
     returns (NPError result);
   intr NPN_SetValue_NPPVpluginUsesDOMForCursor(bool useDOMForCursor)
     returns (NPError result);
   intr NPN_SetValue_NPPVpluginDrawingModel(int drawingModel)
     returns (NPError result);
   intr NPN_SetValue_NPPVpluginEventModel(int eventModel)
     returns (NPError result);
+  intr NPN_SetValue_NPPVpluginIsPlayingAudio(bool isAudioPlaying)
+    returns (NPError result);
 
   intr NPN_GetURL(nsCString url, nsCString target)
     returns (NPError result);
   intr NPN_PostURL(nsCString url, nsCString target, nsCString buffer, bool file)
     returns (NPError result);
 
   /**
    * Covers both NPN_GetURLNotify and NPN_PostURLNotify.
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -621,16 +621,24 @@ PluginInstanceChild::NPN_SetValue(NPPVar
 
         PLUGIN_LOG_DEBUG(("  Plugin requested event model id # %i\n",
             eventModel));
 
         return rv;
     }
 #endif
 
+    case NPPVpluginIsPlayingAudio: {
+        NPError rv = NPERR_GENERIC_ERROR;
+        if (!CallNPN_SetValue_NPPVpluginIsPlayingAudio((NPBool)(intptr_t)aValue, &rv)) {
+            return NPERR_GENERIC_ERROR;
+        }
+        return rv;
+    }
+
     default:
         MOZ_LOG(GetPluginLog(), LogLevel::Warning,
                ("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i (%s)",
                 (int) aVar, NPPVariableToString(aVar)));
         return NPERR_GENERIC_ERROR;
     }
 }
 
@@ -761,16 +769,30 @@ PluginInstanceChild::AnswerNPP_SetValue_
     }
 
     NPBool v = value;
     *result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v);
     return true;
 }
 
 bool
+PluginInstanceChild::AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value,
+                                                          NPError* result)
+{
+    if (!mPluginIface->setvalue) {
+        *result = NPERR_GENERIC_ERROR;
+        return true;
+    }
+
+    NPBool v = value;
+    *result = mPluginIface->setvalue(GetNPP(), NPNVmuteAudioBool, &v);
+    return true;
+}
+
+bool
 PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event,
                                            int16_t* handled)
 {
     PLUGIN_LOG_DEBUG_FUNCTION;
     AssertPluginThread();
 
 #if defined(MOZ_X11) && defined(DEBUG)
     if (GraphicsExpose == event.event.type)
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -75,16 +75,18 @@ protected:
     virtual bool
     AnswerNPP_GetValue_NPPVpluginScriptableNPObject(PPluginScriptableObjectChild** value,
                                                     NPError* result) override;
     virtual bool
     AnswerNPP_GetValue_NPPVpluginNativeAccessibleAtkPlugId(nsCString* aPlugId,
                                                            NPError* aResult) override;
     virtual bool
     AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value, NPError* result) override;
+    virtual bool
+    AnswerNPP_SetValue_NPNVmuteAudioBool(const bool& value, NPError* result) override;
 
     virtual bool
     AnswerNPP_HandleEvent(const NPRemoteEvent& event, int16_t* handled) override;
     virtual bool
     AnswerNPP_HandleEvent_Shmem(const NPRemoteEvent& event,
                                 Shmem&& mem,
                                 int16_t* handled,
                                 Shmem* rtnmem) override;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -436,16 +436,25 @@ PluginInstanceParent::AnswerNPN_SetValue
     return true;
 #else
     *result = NPERR_GENERIC_ERROR;
     return true;
 #endif
 }
 
 bool
+PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(
+    const bool& isAudioPlaying, NPError* result)
+{
+    *result = mNPNIface->setvalue(mNPP, NPPVpluginIsPlayingAudio,
+                                  (void*)(intptr_t)isAudioPlaying);
+    return true;
+}
+
+bool
 PluginInstanceParent::AnswerNPN_GetURL(const nsCString& url,
                                        const nsCString& target,
                                        NPError* result)
 {
     *result = mNPNIface->geturl(mNPP,
                                 NullableStringGet(url),
                                 NullableStringGet(target));
     return true;
@@ -1137,25 +1146,32 @@ PluginInstanceParent::NPP_GetValue(NPPVa
                 (int) aVariable, NPPVariableToString(aVariable)));
         return NPERR_GENERIC_ERROR;
     }
 }
 
 NPError
 PluginInstanceParent::NPP_SetValue(NPNVariable variable, void* value)
 {
+    NPError result;
     switch (variable) {
     case NPNVprivateModeBool:
-        NPError result;
         if (!CallNPP_SetValue_NPNVprivateModeBool(*static_cast<NPBool*>(value),
                                                   &result))
             return NPERR_GENERIC_ERROR;
 
         return result;
 
+    case NPNVmuteAudioBool:
+        if (!CallNPP_SetValue_NPNVmuteAudioBool(*static_cast<NPBool*>(value),
+                                                &result))
+            return NPERR_GENERIC_ERROR;
+
+        return result;
+
     default:
         NS_ERROR("Unhandled NPNVariable in NPP_SetValue");
         MOZ_LOG(GetPluginLog(), LogLevel::Warning,
                ("In PluginInstanceParent::NPP_SetValue: Unhandled NPNVariable %i (%s)",
                 (int) variable, NPNVariableToString(variable)));
         return NPERR_GENERIC_ERROR;
     }
 }
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -123,16 +123,19 @@ public:
     AnswerNPN_SetValue_NPPVpluginUsesDOMForCursor(const bool& useDOMForCursor,
                                                   NPError* result) override;
     virtual bool
     AnswerNPN_SetValue_NPPVpluginDrawingModel(const int& drawingModel,
                                               NPError* result) override;
     virtual bool
     AnswerNPN_SetValue_NPPVpluginEventModel(const int& eventModel,
                                              NPError* result) override;
+    virtual bool
+    AnswerNPN_SetValue_NPPVpluginIsPlayingAudio(const bool& isAudioPlaying,
+                                                NPError* result) override;
 
     virtual bool
     AnswerNPN_GetURL(const nsCString& url, const nsCString& target,
                      NPError *result) override;
 
     virtual bool
     AnswerNPN_PostURL(const nsCString& url, const nsCString& target,
                       const nsCString& buffer, const bool& file,
--- a/dom/plugins/test/testplugin/README
+++ b/dom/plugins/test/testplugin/README
@@ -414,8 +414,19 @@ x86-only on some OSes:
   as evil plugins or extensions might do.
 
 == HiDPI Mode ==
 
 * queryContentsScaleFactor()
 Returns the contents scale factor. On platforms without support for this query
 always returns 1.0 (a double value). Likewise on hardware without HiDPI mode
 support.
+
+== Plugin audio channel support ==
+
+* startAudioPlayback()
+Simulates the plugin starting to play back audio.
+
+* stopAudioPlayback()
+Simulates the plugin stopping to play back audio.
+
+* audioMuted()
+Returns the last value set by NPP_SetValue(NPNVmuteAudioBool).
--- a/dom/plugins/test/testplugin/nptest.cpp
+++ b/dom/plugins/test/testplugin/nptest.cpp
@@ -163,16 +163,19 @@ static bool getWindowPosition(NPObject* 
 static bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getLastKeyText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getNPNVdocumentOrigin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getMouseUpEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool queryContentsScaleFactor(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool echoString(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool startAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool stopAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool getAudioMuted(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 
 static const NPUTF8* sPluginMethodIdentifierNames[] = {
   "npnEvaluateTest",
   "npnInvokeTest",
   "npnInvokeDefaultTest",
   "setUndefinedValueTest",
   "identifierToStringTest",
   "timerTest",
@@ -229,16 +232,19 @@ static const NPUTF8* sPluginMethodIdenti
   "constructObject",
   "setSitesWithData",
   "setSitesWithDataCapabilities",
   "getLastKeyText",
   "getNPNVdocumentOrigin",
   "getMouseUpEventCount",
   "queryContentsScaleFactor",
   "echoString",
+  "startAudioPlayback",
+  "stopAudioPlayback",
+  "audioMuted",
 };
 static NPIdentifier sPluginMethodIdentifiers[MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames)];
 static const ScriptableFunction sPluginMethodFunctions[] = {
   npnEvaluateTest,
   npnInvokeTest,
   npnInvokeDefaultTest,
   setUndefinedValueTest,
   identifierToStringTest,
@@ -296,16 +302,19 @@ static const ScriptableFunction sPluginM
   constructObject,
   setSitesWithData,
   setSitesWithDataCapabilities,
   getLastKeyText,
   getNPNVdocumentOrigin,
   getMouseUpEventCount,
   queryContentsScaleFactor,
   echoString,
+  startAudioPlayback,
+  stopAudioPlayback,
+  getAudioMuted,
 };
 
 static_assert(MOZ_ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
               MOZ_ARRAY_LENGTH(sPluginMethodFunctions),
               "Arrays should have the same size");
 
 static const NPUTF8* sPluginPropertyIdentifierNames[] = {
   "propertyAndMethod"
@@ -779,16 +788,18 @@ NPP_New(NPMIMEType pluginType, NPP insta
   instanceData->throwOnNextInvoke = false;
   instanceData->runScriptOnPaint = false;
   instanceData->dontTouchElement = false;
   instanceData->testrange = nullptr;
   instanceData->hasWidget = false;
   instanceData->npnNewStream = false;
   instanceData->invalidateDuringPaint = false;
   instanceData->slowPaint = false;
+  instanceData->playingAudio = false;
+  instanceData->audioMuted = false;
   instanceData->writeCount = 0;
   instanceData->writeReadyCount = 0;
   memset(&instanceData->window, 0, sizeof(instanceData->window));
   instanceData->crashOnDestroy = false;
   instanceData->cleanupWidget = true; // only used by nptest_gtk
   instanceData->topLevelWindowActivationState = ACTIVATION_STATE_UNKNOWN;
   instanceData->topLevelWindowActivationEventCount = 0;
   instanceData->focusState = ACTIVATION_STATE_UNKNOWN;
@@ -1435,16 +1446,21 @@ NPP_GetValue(NPP instance, NPPVariable v
 NPError
 NPP_SetValue(NPP instance, NPNVariable variable, void* value)
 {
   if (variable == NPNVprivateModeBool) {
     InstanceData* instanceData = (InstanceData*)(instance->pdata);
     instanceData->lastReportedPrivateModeState = bool(*static_cast<NPBool*>(value));
     return NPERR_NO_ERROR;
   }
+  if (variable == NPNVmuteAudioBool) {
+    InstanceData* instanceData = (InstanceData*)(instance->pdata);
+    instanceData->audioMuted = bool(*static_cast<NPBool*>(value));
+    return NPERR_NO_ERROR;
+  }
   return NPERR_GENERIC_ERROR;
 }
 
 void
 NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status, void* notifyData)
 {
   if (notifyData) {
     URLNotifyData* nd = static_cast<URLNotifyData*>(notifyData);
@@ -3702,8 +3718,50 @@ bool echoString(NPObject* npobj, const N
   }
 
   std::copy(arg.UTF8Characters, arg.UTF8Characters + arg.UTF8Length, buffer);
   STRINGN_TO_NPVARIANT(buffer, arg.UTF8Length, *result);
 
   return true;
 }
 
+static bool
+toggleAudioPlayback(NPObject* npobj, uint32_t argCount, bool playingAudio, NPVariant* result)
+{
+  if (argCount != 0) {
+    return false;
+  }
+
+  NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+  InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+  id->playingAudio = playingAudio;
+
+  NPN_SetValue(npp, NPPVpluginIsPlayingAudio, (void*)playingAudio);
+
+  VOID_TO_NPVARIANT(*result);
+  return true;
+}
+
+static bool
+startAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  return toggleAudioPlayback(npobj, argCount, true, result);
+}
+
+static bool
+stopAudioPlayback(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  return toggleAudioPlayback(npobj, argCount, false, result);
+}
+
+static bool
+getAudioMuted(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result)
+{
+  if (argCount != 0) {
+    return false;
+  }
+
+  NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+  InstanceData* id = static_cast<InstanceData*>(npp->pdata);
+  BOOLEAN_TO_NPVARIANT(id->audioMuted, *result);
+  return true;
+}
+
--- a/dom/plugins/test/testplugin/nptest.h
+++ b/dom/plugins/test/testplugin/nptest.h
@@ -101,16 +101,18 @@ typedef struct InstanceData {
   bool throwOnNextInvoke;
   bool runScriptOnPaint;
   bool dontTouchElement;
   uint32_t timerID[2];
   bool timerTestResult;
   bool asyncCallbackResult;
   bool invalidateDuringPaint;
   bool slowPaint;
+  bool playingAudio;
+  bool audioMuted;
   int32_t winX;
   int32_t winY;
   int32_t lastMouseX;
   int32_t lastMouseY;
   int32_t widthAtLastPaint;
   int32_t paintCount;
   int32_t writeCount;
   int32_t writeReadyCount;
--- a/dom/push/PushServiceChildPreload.jsm
+++ b/dom/push/PushServiceChildPreload.jsm
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
+this.EXPORTED_SYMBOLS = [];
+
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "swm",
                                    "@mozilla.org/serviceworkers/manager;1",
--- a/dom/webidl/IDBCursor.webidl
+++ b/dom/webidl/IDBCursor.webidl
@@ -34,12 +34,13 @@ interface IDBCursor {
 
     [Throws]
     void       continue (optional any key);
 
     [Throws]
     IDBRequest delete ();
 };
 
+[Exposed=(Window,Worker)]
 interface IDBCursorWithValue : IDBCursor {
     [Throws]
     readonly    attribute any value;
 };
--- a/dom/workers/Performance.cpp
+++ b/dom/workers/Performance.cpp
@@ -59,16 +59,26 @@ Performance::GetPerformanceTimingFromStr
   if (aProperty.EqualsLiteral("navigationStart")) {
     return mWorkerPrivate->NowBaseTimeHighRes();
   }
 
   MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
   return 0;
 }
 
+void
+Performance::InsertUserEntry(PerformanceEntry* aEntry)
+{
+  if (mWorkerPrivate->PerformanceLoggingEnabled()) {
+    PerformanceBase::LogEntry(aEntry,
+                              NS_ConvertUTF16toUTF8(mWorkerPrivate->ScriptURL()));
+  }
+  PerformanceBase::InsertUserEntry(aEntry);
+}
+
 DOMHighResTimeStamp
 Performance::DeltaFromNavigationStart(DOMHighResTimeStamp aTime)
 {
   if (aTime == 0) {
     return 0;
   }
 
   return aTime - mWorkerPrivate->NowBaseTimeHighRes();
--- a/dom/workers/Performance.h
+++ b/dom/workers/Performance.h
@@ -21,16 +21,18 @@ class WorkerPrivate;
 class Performance final : public PerformanceBase
 {
 public:
   explicit Performance(WorkerPrivate* aWorkerPrivate);
 
 private:
   ~Performance();
 
+  void InsertUserEntry(PerformanceEntry* aEntry) override;
+
   WorkerPrivate* mWorkerPrivate;
 
 public:
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL (public APIs)
   DOMHighResTimeStamp Now() const override;
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -153,16 +153,17 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 
 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
 #define DUMP_CONTROLLED_BY_PREF 1
 #define PREF_DOM_WINDOW_DUMP_ENABLED "browser.dom.window.dump.enabled"
 #endif
 
 #define PREF_DOM_CACHES_ENABLED        "dom.caches.enabled"
 #define PREF_DOM_CACHES_TESTING_ENABLED "dom.caches.testing.enabled"
+#define PREF_WORKERS_PERFORMANCE_LOGGING_ENABLED "dom.performance.enable_user_timing_logging"
 #define PREF_DOM_WORKERNOTIFICATION_ENABLED  "dom.webnotifications.enabled"
 #define PREF_WORKERS_LATEST_JS_VERSION "dom.workers.latestJSVersion"
 #define PREF_INTL_ACCEPT_LANGUAGES     "intl.accept_languages"
 #define PREF_SERVICEWORKERS_ENABLED    "dom.serviceWorkers.enabled"
 #define PREF_SERVICEWORKERS_TESTING_ENABLED "dom.serviceWorkers.testing.enabled"
 #define PREF_INTERCEPTION_ENABLED      "dom.serviceWorkers.interception.enabled"
 #define PREF_INTERCEPTION_OPAQUE_ENABLED "dom.serviceWorkers.interception.opaque.enabled"
 
@@ -1937,16 +1938,20 @@ RuntimeService::Init()
                                   PREF_INTERCEPTION_OPAQUE_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_OPAQUE_ENABLED))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_CACHES_TESTING))) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                   WorkerPrefChanged,
+                                  PREF_WORKERS_PERFORMANCE_LOGGING_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_PERFORMANCE_LOGGING_ENABLED))) ||
+      NS_FAILED(Preferences::RegisterCallbackAndCall(
+                                  WorkerPrefChanged,
                                   PREF_SERVICEWORKERS_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS_TESTING))) ||
       NS_FAILED(Preferences::RegisterCallback(LoadRuntimeOptions,
                                               PREF_JS_OPTIONS_PREFIX,
                                               nullptr)) ||
       NS_FAILED(Preferences::RegisterCallbackAndCall(
                                                    LoadRuntimeOptions,
                                                    PREF_WORKERS_OPTIONS_PREFIX,
@@ -2144,16 +2149,20 @@ RuntimeService::Cleanup()
                                   PREF_SERVICEWORKERS_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_SERVICEWORKERS_TESTING))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_DOM_CACHES_TESTING_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_DOM_CACHES_TESTING))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
+                                  PREF_WORKERS_PERFORMANCE_LOGGING_ENABLED,
+                                  reinterpret_cast<void *>(WORKERPREF_PERFORMANCE_LOGGING_ENABLED))) ||
+        NS_FAILED(Preferences::UnregisterCallback(
+                                  WorkerPrefChanged,
                                   PREF_INTERCEPTION_OPAQUE_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_OPAQUE_ENABLED))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
                                   PREF_INTERCEPTION_ENABLED,
                                   reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_ENABLED))) ||
         NS_FAILED(Preferences::UnregisterCallback(
                                   WorkerPrefChanged,
@@ -2703,16 +2712,17 @@ RuntimeService::WorkerPrefChanged(const 
 
   const WorkerPreference key =
     static_cast<WorkerPreference>(reinterpret_cast<uintptr_t>(aClosure));
 
   switch (key) {
     case WORKERPREF_DOM_CACHES:
     case WORKERPREF_DOM_CACHES_TESTING:
     case WORKERPREF_DOM_WORKERNOTIFICATION:
+    case WORKERPREF_PERFORMANCE_LOGGING_ENABLED:
 #ifdef DUMP_CONTROLLED_BY_PREF
     case WORKERPREF_DUMP:
 #endif
     case WORKERPREF_INTERCEPTION_ENABLED:
     case WORKERPREF_INTERCEPTION_OPAQUE_ENABLED:
     case WORKERPREF_SERVICEWORKERS:
     case WORKERPREF_SERVICEWORKERS_TESTING:
       sDefaultPreferences[key] = Preferences::GetBool(aPrefName, false);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1560,17 +1560,17 @@ public:
     // they show up in the error console.
     if (!JSREPORT_IS_WARNING(aFlags)) {
       // First fire an ErrorEvent at the worker.
       RootedDictionary<ErrorEventInit> init(aCx);
       init.mMessage = aMessage;
       init.mFilename = aFilename;
       init.mLineno = aLineNumber;
       init.mCancelable = true;
-      init.mBubbles = true;
+      init.mBubbles = false;
 
       if (aTarget) {
         nsRefPtr<ErrorEvent> event =
           ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
         event->SetTrusted(true);
 
         nsEventStatus status = nsEventStatus_eIgnore;
         aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1317,16 +1317,23 @@ public:
   bool
   DOMCachesTestingEnabled() const
   {
     AssertIsOnWorkerThread();
     return mPreferences[WORKERPREF_DOM_CACHES_TESTING];
   }
 
   bool
+  PerformanceLoggingEnabled() const
+  {
+    AssertIsOnWorkerThread();
+    return mPreferences[WORKERPREF_PERFORMANCE_LOGGING_ENABLED];
+  }
+
+  bool
   OnLine() const
   {
     AssertIsOnWorkerThread();
     return mOnLine;
   }
 
   void
   StopSyncLoop(nsIEventTarget* aSyncLoopTarget, bool aResult);
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -200,16 +200,17 @@ enum WorkerPreference
   WORKERPREF_DUMP = 0, // browser.dom.window.dump.enabled
   WORKERPREF_DOM_CACHES, // dom.caches.enabled
   WORKERPREF_SERVICEWORKERS, // dom.serviceWorkers.enabled
   WORKERPREF_INTERCEPTION_ENABLED, // dom.serviceWorkers.interception.enabled
   WORKERPREF_DOM_WORKERNOTIFICATION, // dom.webnotifications.workers.enabled
   WORKERPREF_DOM_CACHES_TESTING, // dom.caches.testing.enabled
   WORKERPREF_SERVICEWORKERS_TESTING, // dom.serviceWorkers.testing.enabled
   WORKERPREF_INTERCEPTION_OPAQUE_ENABLED, // dom.serviceWorkers.interception.opaque.enabled
+  WORKERPREF_PERFORMANCE_LOGGING_ENABLED, // dom.performance.enable_user_timing_logging
   WORKERPREF_COUNT
 };
 
 // Implemented in WorkerPrivate.cpp
 
 struct WorkerLoadInfo
 {
   // All of these should be released in WorkerPrivateParent::ForgetMainThreadObjects.
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/1158031.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script>
+
+function boom()
+{
+    var w = new Worker("data:text/javascript;charset=UTF-8,");
+    w.postMessage(new Blob([], {}));
+}
+
+</script>
+<body onload="boom();"></body>
--- a/dom/workers/test/crashtests.list
+++ b/dom/workers/test/crashtests.list
@@ -1,1 +1,2 @@
 load 943516.html
+load 1158031.html
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -127,16 +127,18 @@ var interfaceNamesInGlobalScope =
     "FileReaderSync",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FormData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Headers",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBCursor",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursorWithValue",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBDatabase",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBFactory",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBIndex",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBKeyRange",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -119,16 +119,18 @@ var interfaceNamesInGlobalScope =
     "FileReaderSync",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "FormData",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Headers",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBCursor",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursorWithValue",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBDatabase",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBFactory",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBIndex",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "IDBKeyRange",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/xbl/nsXBLDocumentInfo.cpp
+++ b/dom/xbl/nsXBLDocumentInfo.cpp
@@ -72,17 +72,17 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsX
       iter.UserData()->Trace(aCallbacks, aClosure);
     }
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 static void
 UnmarkXBLJSObject(JS::GCCellPtr aPtr, const char* aName, void* aClosure)
 {
-  JS::ExposeObjectToActiveJS(aPtr.toObject());
+  JS::ExposeObjectToActiveJS(&aPtr.as<JSObject>());
 }
 
 static PLDHashOperator
 UnmarkProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
 {
   aProto->Trace(TraceCallbackFunc(UnmarkXBLJSObject), nullptr);
   return PL_DHASH_NEXT;
 }
--- a/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp
+++ b/extensions/pref/autoconfig/src/nsJSConfigTriggers.cpp
@@ -102,14 +102,14 @@ nsresult EvaluateAdminConfigScript(const
     }
 
     AutoSafeJSContext cx;
     JSAutoCompartment ac(cx, autoconfigSb);
 
     nsAutoCString script(js_buffer, length);
     JS::RootedValue v(cx);
     rv = xpc->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script), filename, cx,
-                                  autoconfigSb, &v);
+                                  autoconfigSb, JSVERSION_LATEST, &v);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -520,33 +520,16 @@ GfxPatternToCairoPattern(const Pattern& 
 
       MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
       GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
       cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
 
       matrix = &pattern.mMatrix;
 
       const std::vector<GradientStop>& stops = cairoStops->GetStops();
-      if (stops.size() >= 2 && stops.front().offset == stops.back().offset) {
-        // Certain Cairo backends that use pixman to implement gradients can have jagged
-        // edges occur with hard stops. Such hard stops are used for implementing certain
-        // types of CSS borders. Work around this by turning these hard-stops into half-pixel
-        // gradients to anti-alias them. See bug 1033375
-        Matrix patternToDevice = aTransform * pattern.mMatrix;
-        Float gradLength = (patternToDevice * pattern.mEnd - patternToDevice * pattern.mBegin).Length();
-        if (gradLength > 0) {
-          Float aaOffset = 0.25 / gradLength;
-          CairoPatternAddGradientStop(pat, stops.front(), -aaOffset);
-          for (size_t i = 1; i < stops.size()-1; ++i) {
-            CairoPatternAddGradientStop(pat, stops[i]);
-          }
-          CairoPatternAddGradientStop(pat, stops.back(), aaOffset);
-          break;
-        }
-      }
       for (size_t i = 0; i < stops.size(); ++i) {
         CairoPatternAddGradientStop(pat, stops[i]);
       }
 
       break;
     }
     case PatternType::RADIAL_GRADIENT:
     {
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -616,17 +616,19 @@ Factory::CreateDualDrawTargetForD3D10Tex
 }
 
 void
 Factory::SetDirect3D10Device(ID3D10Device1 *aDevice)
 {
   // do not throw on failure; return error codes and disconnect the device
   // On Windows 8 error codes are the default, but on Windows 7 the
   // default is to throw (or perhaps only with some drivers?)
-  aDevice->SetExceptionMode(0);
+  if (aDevice) {
+    aDevice->SetExceptionMode(0);
+  }
   mD3D10Device = aDevice;
 }
 
 ID3D10Device1*
 Factory::GetDirect3D10Device()
 {
 #ifdef DEBUG
   if (mD3D10Device) {
--- a/gfx/cairo/cairo/src/cairo-rectangle.c
+++ b/gfx/cairo/cairo/src/cairo-rectangle.c
@@ -208,18 +208,17 @@ cairo_bool_t
 	    t1 = B1x - P1x;
 	    t2 = B2x - P1x;
 	} else {
 	    t1 = P1x - B2x;
 	    t2 = P1x - B1x;
 	    xlen = - xlen;
 	}
 
-	if ((t1 < 0 || t1 > xlen) &&
-	    (t2 < 0 || t2 > xlen))
+        if (t1 > xlen || t2 < 0)
 	    return FALSE;
     } else {
 	/* Fully vertical line -- check that X is in bounds */
 	if (P1x < B1x || P1x > B2x)
 	    return FALSE;
     }
 
     if (ylen) {
@@ -227,18 +226,17 @@ cairo_bool_t
 	    t3 = B1y - P1y;
 	    t4 = B2y - P1y;
 	} else {
 	    t3 = P1y - B2y;
 	    t4 = P1y - B1y;
 	    ylen = - ylen;
 	}
 
-	if ((t3 < 0 || t3 > ylen) &&
-	    (t4 < 0 || t4 > ylen))
+        if (t3 > ylen || t4 < 0)
 	    return FALSE;
     } else {
 	/* Fully horizontal line -- check Y */
 	if (P1y < B1y || P1y > B2y)
 	    return FALSE;
     }
 
     /* If we had a horizontal or vertical line, then it's already been checked */
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -337,21 +337,21 @@ TextureClient::CreateForDrawing(ISurface
 
 #if defined(MOZ_WIDGET_GONK) || defined(XP_WIN)
   int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
 #endif
 
 #ifdef XP_WIN
   LayersBackend parentBackend = aAllocator->GetCompositorBackendType();
   if (parentBackend == LayersBackend::LAYERS_D3D11 &&
-      (aMoz2DBackend == gfx::BackendType::DIRECT2D ||
-        aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) &&
-      gfxWindowsPlatform::GetPlatform()->GetD3D10Device() &&
+      ((aMoz2DBackend == gfx::BackendType::DIRECT2D && Factory::GetDirect3D10Device()) ||
+       (aMoz2DBackend == gfx::BackendType::DIRECT2D1_1 && Factory::GetDirect3D11Device())) &&
       aSize.width <= maxTextureSize &&
-      aSize.height <= maxTextureSize) {
+      aSize.height <= maxTextureSize)
+  {
     texture = new TextureClientD3D11(aAllocator, aFormat, aTextureFlags);
   }
   if (parentBackend == LayersBackend::LAYERS_D3D9 &&
       aMoz2DBackend == gfx::BackendType::CAIRO &&
       aAllocator->IsSameProcess() &&
       aSize.width <= maxTextureSize &&
       aSize.height <= maxTextureSize &&
       NS_IsMainThread()) {
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -494,22 +494,16 @@ gfxASurface::GetSubpixelAntialiasingEnab
       return false;
 #ifdef MOZ_TREE_CAIRO
     return cairo_surface_get_subpixel_antialiasing(mSurface) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
 #else
     return true;
 #endif
 }
 
-gfxMemoryLocation
-gfxASurface::GetMemoryLocation() const
-{
-    return gfxMemoryLocation::IN_PROCESS_HEAP;
-}
-
 int32_t
 gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
 {
     switch (format) {
         case gfxImageFormat::ARGB32:
         case gfxImageFormat::RGB24:
             return 4;
         case gfxImageFormat::RGB16_565:
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -154,22 +154,16 @@ public:
     // is capable of measuring its own size accurately.  If not, the caller
     // must fall back to a computed size.  (Note that gfxASurface can actually
     // measure itself, but we must |return false| here because it serves as the
     // (conservative) default for all the sub-classes.  Therefore, this
     // function should only be called on a |gfxASurface*| that actually points
     // to a sub-class of gfxASurface.)
     virtual bool SizeOfIsMeasured() const { return false; }
 
-    /**
-     * Where does this surface's memory live?  By default, we say it's in this
-     * process's heap.
-     */
-    virtual gfxMemoryLocation GetMemoryLocation() const;
-
     static int32_t BytePerPixelFromFormat(gfxImageFormat format);
 
     virtual const mozilla::gfx::IntSize GetSize() const;
 
     virtual mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
     void SetOpaqueRect(const gfxRect& aRect);
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2232,17 +2232,17 @@ gfxPlatform::ShouldUseLayersAcceleration
       InSafeMode() ||
       (acceleratedEnv && *acceleratedEnv == '0'))
   {
     return false;
   }
   if (gfxPrefs::LayersAccelerationForceEnabled()) {
     return true;
   }
-  if (gfxPlatform::GetPlatform()->AccelerateLayersByDefault()) {
+  if (AccelerateLayersByDefault()) {
     return true;
   }
   if (acceleratedEnv && *acceleratedEnv != '0') {
     return true;
   }
   return false;
 }
 
--- a/gfx/thebes/gfxTypes.h
+++ b/gfx/thebes/gfxTypes.h
@@ -87,20 +87,9 @@ enum class gfxSurfaceType {
 
 enum class gfxContentType {
   COLOR       = 0x1000,
   ALPHA       = 0x2000,
   COLOR_ALPHA = 0x3000,
   SENTINEL    = 0xffff
 };
 
-/**
-  * The memory used by a gfxASurface (as reported by KnownMemoryUsed()) can
-  * either live in this process's heap, in this process but outside the
-  * heap, or in another process altogether.
-  */
-enum class gfxMemoryLocation {
-  IN_PROCESS_HEAP,
-  IN_PROCESS_NONHEAP,
-  OUT_OF_PROCESS
-};
-
 #endif /* GFX_TYPES_H */
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -368,39 +368,45 @@ public:
                               gfxWindowsPlatform::sD3D9SharedTextureUsed,
                               "Memory used for D3D9 shared textures");
   }
 };
 
 NS_IMPL_ISUPPORTS(D3D9SharedTextureReporter, nsIMemoryReporter)
 
 gfxWindowsPlatform::gfxWindowsPlatform()
-  : mD3D11DeviceInitialized(false)
+  : mRenderMode(RENDER_GDI)
   , mIsWARP(false)
   , mHasDeviceReset(false)
   , mDoesD3D11TextureSharingWork(false)
+  , mAcceleration(FeatureStatus::Unused)
   , mD3D11Status(FeatureStatus::Unused)
   , mD2DStatus(FeatureStatus::Unused)
+  , mD2D1Status(FeatureStatus::Unused)
 {
     mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
     mUseClearTypeAlways = UNINITIALIZED_VALUE;
 
-    mUsingGDIFonts = false;
-
     /* 
      * Initialize COM 
      */ 
     CoInitialize(nullptr); 
 
     RegisterStrongMemoryReporter(new GfxD2DVramReporter());
 
-    if (gfxPrefs::Direct2DUse1_1()) {
-      InitD3D11Devices();
+    // Set up the D3D11 feature levels we can ask for.
+    if (IsWin8OrLater()) {
+      mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
     }
+    mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
+    mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
+    mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
+    mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
 
+    InitializeDevices();
     UpdateRenderMode();
 
     RegisterStrongMemoryReporter(new GPUAdapterReporter());
     RegisterStrongMemoryReporter(new D3D11TextureReporter());
     RegisterStrongMemoryReporter(new D3D9TextureReporter());
     RegisterStrongMemoryReporter(new D3D9SurfaceImageReporter());
     RegisterStrongMemoryReporter(new D3D9SharedTextureReporter());
 }
@@ -433,171 +439,116 @@ bool
 gfxWindowsPlatform::CanUseHardwareVideoDecoding()
 {
     if (!gfxPrefs::LayersPreferD3D9() && !mDoesD3D11TextureSharingWork) {
         return false;
     }
     return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding();
 }
 
-FeatureStatus
-gfxWindowsPlatform::InitD2DSupport()
-{
-#ifdef CAIRO_HAS_D2D_SURFACE
-  bool d2dBlocked = false;
-  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
-  if (gfxInfo) {
-    int32_t status;
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
-      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-        d2dBlocked = true;
-      }
-    }
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
-      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-        d2dBlocked = true;
-      }
-    }
-  }
-
-  // If D2D is blocked or D3D9 is prefered, and D2D is not force-enabled, then
-  // we don't attempt to use D2D.
-  if (!gfxPrefs::Direct2DForceEnabled()) {
-    if (d2dBlocked) {
-      return FeatureStatus::Blacklisted;
-    }
-    if (gfxPrefs::LayersPreferD3D9()) {
-      return FeatureStatus::Disabled;
-    }
-  }
-
-  // Do not ever try to use D2D if it's explicitly disabled or if we're not
-  // using DWrite fonts.
-  if (gfxPrefs::Direct2DDisabled() || mUsingGDIFonts) {
-    return FeatureStatus::Disabled;
-  }
-
-  if (!IsVistaOrLater() || !GetD3D11Device()) {
-    return FeatureStatus::Unavailable;
-  }
-  if (!mDoesD3D11TextureSharingWork) {
-    return FeatureStatus::Failed;
-  }
-  if (InSafeMode()) {
-    return FeatureStatus::Blocked;
-  }
-
-  VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled());
-  if (!mD3D10Device || !GetD3D11Device()) {
-    return FeatureStatus::Failed;
-  }
-
-  mRenderMode = RENDER_DIRECT2D;
-  mUseDirectWrite = true;
-  return FeatureStatus::Available;
-#else
-  return FeatureStatus::Unavailable;
-#endif
-}
-
-void
+bool
 gfxWindowsPlatform::InitDWriteSupport()
 {
-#ifdef CAIRO_HAS_DWRITE_FONT
-  // Enable when it's preffed on -and- we're using Vista or higher. Or when
-  // we're going to use D2D.
-  if (mDWriteFactory || (!mUseDirectWrite || !IsVistaOrLater())) {
-    return;
-  }
+  MOZ_ASSERT(!mDWriteFactory && IsVistaOrLater());
 
   mozilla::ScopedGfxFeatureReporter reporter("DWrite");
   decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)
       GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory");
-
   if (!createDWriteFactory) {
-    return;
+    return false;
   }
 
   // I need a direct pointer to be able to cast to IUnknown**, I also need to
   // remember to release this because the nsRefPtr will AddRef it.
-  IDWriteFactory *factory;
+  RefPtr<IDWriteFactory> factory;
   HRESULT hr = createDWriteFactory(
       DWRITE_FACTORY_TYPE_SHARED,
       __uuidof(IDWriteFactory),
-      reinterpret_cast<IUnknown**>(&factory));
+      (IUnknown **)((IDWriteFactory **)byRef(factory)));
+  if (FAILED(hr) || !factory) {
+    return false;
+  }
+
+  mDWriteFactory = factory;
 
-  if (SUCCEEDED(hr) && factory) {
-    mDWriteFactory = factory;
-    factory->Release();
-    hr = mDWriteFactory->CreateTextAnalyzer(getter_AddRefs(mDWriteAnalyzer));
+  SetupClearTypeParams();
+  reporter.SetSuccessful();
+  return true;
+}
+
+bool
+gfxWindowsPlatform::HandleDeviceReset()
+{
+  DeviceResetReason resetReason = DeviceResetReason::OK;
+  if (!DidRenderingDeviceReset(&resetReason)) {
+    return false;
   }
 
-  SetupClearTypeParams();
+  Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
+
+  // Remove devices and adapters.
+  mD3D10Device = nullptr;
+  mD3D11Device = nullptr;
+  mD3D11ContentDevice = nullptr;
+  mD3D11ImageBridgeDevice = nullptr;
+  mAdapter = nullptr;
+  Factory::SetDirect3D11Device(nullptr);
+  Factory::SetDirect3D10Device(nullptr);
+
+  // Reset local state. Note: we leave feature status variables as-is. They
+  // will be recomputed by InitializeDevices().
+  mIsWARP = false;
+  mHasDeviceReset = false;
+  mDoesD3D11TextureSharingWork = false;
+  mDeviceResetReason = DeviceResetReason::OK;
+
+  imgLoader::Singleton()->ClearCache(true);
+  imgLoader::Singleton()->ClearCache(false);
+  gfxAlphaBoxBlur::ShutdownBlurCache();
 
-  if (hr == S_OK) {
-    reporter.SetSuccessful();
+  InitializeDevices();
+  return true;
+}
+
+void
+gfxWindowsPlatform::UpdateBackendPrefs()
+{
+  uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
+  uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
+  BackendType defaultBackend = BackendType::CAIRO;
+  if (GetD2DStatus() == FeatureStatus::Available) {
+    mRenderMode = RENDER_DIRECT2D;
+    canvasMask |= BackendTypeBit(BackendType::DIRECT2D);
+    contentMask |= BackendTypeBit(BackendType::DIRECT2D);
+    if (GetD2D1Status() == FeatureStatus::Available) {
+      contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
+      canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
+      defaultBackend = BackendType::DIRECT2D1_1;
+    } else {
+      defaultBackend = BackendType::DIRECT2D;
+    }
+  } else {
+    mRenderMode = RENDER_GDI;
+    canvasMask |= BackendTypeBit(BackendType::SKIA);
   }
-#endif
+  contentMask |= BackendTypeBit(BackendType::SKIA);
+  InitBackendPrefs(canvasMask, defaultBackend, contentMask, defaultBackend);
 }
 
 void
 gfxWindowsPlatform::UpdateRenderMode()
 {
-/* Pick the default render mode for
- * desktop.
- */
-    bool didReset = false;
-    DeviceResetReason resetReason = DeviceResetReason::OK;
-    if (DidRenderingDeviceReset(&resetReason)) {
-      Telemetry::Accumulate(Telemetry::DEVICE_RESET_REASON, uint32_t(resetReason));
-      mD3D11DeviceInitialized = false;
-      mD3D11Device = nullptr;
-      mD3D11ContentDevice = nullptr;
-      mAdapter = nullptr;
-      mDeviceResetReason = DeviceResetReason::OK;
-      mHasDeviceReset = false;
+  bool didReset = HandleDeviceReset();
 
-      imgLoader::Singleton()->ClearCache(true);
-      imgLoader::Singleton()->ClearCache(false);
-      gfxAlphaBoxBlur::ShutdownBlurCache();
-      Factory::SetDirect3D11Device(nullptr);
-
-      didReset = true;
-    }
-
-    mRenderMode = RENDER_GDI;
-    mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled();
+  UpdateBackendPrefs();
 
-    mD2DStatus = InitD2DSupport();
-    InitDWriteSupport();
-
-    uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO);
-    uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
-    BackendType defaultBackend = BackendType::CAIRO;
-    if (mRenderMode == RENDER_DIRECT2D) {
-      canvasMask |= BackendTypeBit(BackendType::DIRECT2D);
-      contentMask |= BackendTypeBit(BackendType::DIRECT2D);
-      if (gfxPrefs::Direct2DUse1_1() && Factory::SupportsD2D1() &&
-          GetD3D11ContentDevice()) {
-        contentMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
-        canvasMask |= BackendTypeBit(BackendType::DIRECT2D1_1);
-        defaultBackend = BackendType::DIRECT2D1_1;
-      } else {
-        defaultBackend = BackendType::DIRECT2D;
-      }
-    } else {
-      canvasMask |= BackendTypeBit(BackendType::SKIA);
-    }
-    contentMask |= BackendTypeBit(BackendType::SKIA);
-    InitBackendPrefs(canvasMask, defaultBackend,
-                     contentMask, defaultBackend);
-
-    if (didReset) {
-      mScreenReferenceDrawTarget = CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
-    }
+  if (didReset) {
+    mScreenReferenceDrawTarget =
+      CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
+  }
 }
 
 #ifdef CAIRO_HAS_D2D_SURFACE
 HRESULT
 gfxWindowsPlatform::CreateDevice(nsRefPtr<IDXGIAdapter1> &adapter1,
                                  int featureLevelIndex)
 {
   nsModuleHandle d3d10module(LoadLibrarySystem32(L"d3d10_1.dll"));
@@ -639,21 +590,16 @@ gfxWindowsPlatform::CreateDevice(nsRefPt
   return device ? S_OK : hr;
 }
 #endif
 
 void
 gfxWindowsPlatform::VerifyD2DDevice(bool aAttemptForce)
 {
 #ifdef CAIRO_HAS_D2D_SURFACE
-    DriverInitCrashDetection detectCrashes;
-    if (detectCrashes.DisableAcceleration()) {
-      return;
-    }
-
     if (mD3D10Device) {
         if (SUCCEEDED(mD3D10Device->GetDeviceRemovedReason())) {
             return;
         }
         mD3D10Device = nullptr;
 
         // Surface cache needs to be invalidated since it may contain vector
         // images rendered with our old, broken D2D device.
@@ -711,44 +657,58 @@ gfxWindowsPlatform::VerifyD2DDevice(bool
       reporter1_1.SetSuccessful();
     }
 #endif
 }
 
 gfxPlatformFontList*
 gfxWindowsPlatform::CreatePlatformFontList()
 {
-    mUsingGDIFonts = false;
     gfxPlatformFontList *pfl;
 #ifdef CAIRO_HAS_DWRITE_FONT
     // bug 630201 - older pre-RTM versions of Direct2D/DirectWrite cause odd
     // crashers so blacklist them altogether
     if (IsNotWin7PreRTM() && GetDWriteFactory()) {
         pfl = new gfxDWriteFontList();
         if (NS_SUCCEEDED(pfl->InitFontList())) {
             return pfl;
         }
         // DWrite font initialization failed! Don't know why this would happen,
         // but apparently it can - see bug 594865.
         // So we're going to fall back to GDI fonts & rendering.
         gfxPlatformFontList::Shutdown();
-        SetRenderMode(RENDER_GDI);
+        DisableD2D();
     }
 #endif
     pfl = new gfxGDIFontList();
-    mUsingGDIFonts = true;
 
     if (NS_SUCCEEDED(pfl->InitFontList())) {
         return pfl;
     }
 
     gfxPlatformFontList::Shutdown();
     return nullptr;
 }
 
+// This function will permanently disable D2D for the session. It's intended to
+// be used when, after initially chosing to use Direct2D, we encounter a
+// scenario we can't support.
+//
+// This is called during gfxPlatform::Init() so at this point there should be no
+// DrawTargetD2D/1 instances.
+void
+gfxWindowsPlatform::DisableD2D()
+{
+  mD2DStatus = FeatureStatus::Failed;
+  mD2D1Status = FeatureStatus::Failed;
+  Factory::SetDirect3D11Device(nullptr);
+  Factory::SetDirect3D10Device(nullptr);
+  UpdateBackendPrefs();
+}
+
 already_AddRefed<gfxASurface>
 gfxWindowsPlatform::CreateOffscreenSurface(const IntSize& aSize,
                                            gfxImageFormat aFormat)
 {
     nsRefPtr<gfxASurface> surf = nullptr;
 
 #ifdef CAIRO_HAS_WIN32_SURFACE
     if (mRenderMode == RENDER_GDI || mRenderMode == RENDER_DIRECT2D)
@@ -1551,46 +1511,28 @@ gfxWindowsPlatform::GetD3D9DeviceManager
   }
 
   return mDeviceManager;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11Device()
 {
-  if (mD3D11DeviceInitialized) {
-    return mD3D11Device;
-  }
-
-  InitD3D11Devices();
-
   return mD3D11Device;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11ContentDevice()
 {
-  if (mD3D11DeviceInitialized) {
-    return mD3D11ContentDevice;
-  }
-
-  InitD3D11Devices();
-
   return mD3D11ContentDevice;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11ImageBridgeDevice()
 {
-  if (mD3D11DeviceInitialized) {
-    return mD3D11ImageBridgeDevice;
-  }
-
-  InitD3D11Devices();
-
   return mD3D11ImageBridgeDevice;
 }
 
 ID3D11Device*
 gfxWindowsPlatform::GetD3D11DeviceForCurrentThread()
 {
   if (NS_IsMainThread()) {
     return GetD3D11ContentDevice();
@@ -1913,65 +1855,76 @@ bool DoesD3D11TextureSharingWork(ID3D11D
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
 }
 
 bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device)
 {
   return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE);
 }
 
+static inline bool
+CanUseWARP()
+{
+  if (gfxPrefs::LayersD3D11ForceWARP()) {
+    return true;
+  }
+
+  // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703.
+  if (!IsWin8OrLater() ||
+      gfxPrefs::LayersD3D11DisableWARP() ||
+      GetModuleHandleA("nvdxgiwrap.dll"))
+  {
+    return false;
+  }
+  return true;
+}
+
 auto
 gfxWindowsPlatform::CheckD3D11Support() -> D3D11Status
 {
   if (gfxPrefs::LayersD3D11ForceWARP()) {
-    return D3D11Status::ForceWARP;
+    return D3D11Status::OnlyWARP;
   }
-
   if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) {
     int32_t status;
     if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
       if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
-        // See if we can use WARP instead.
-        //
-        // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703 for more.
-        if (gfxPrefs::LayersD3D11DisableWARP() || GetModuleHandleA("nvdxgiwrap.dll")) {
+        if (!CanUseWARP()) {
           return D3D11Status::Blocked;
         }
-        return D3D11Status::TryWARP;
+        return D3D11Status::OnlyWARP;
       }
     }
   }
-
-  // Either nsIGfxInfo was bugged or we're not blacklisted.
   if (!GetDXGIAdapter()) {
-    return D3D11Status::TryWARP;
+    return D3D11Status::OnlyWARP;
   }
   return D3D11Status::Ok;
 }
 
 // We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
 // since it doesn't include d3d11.h, so we use a static here. It should only
 // be used within InitD3D11Devices.
 decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
 
 bool
-gfxWindowsPlatform::AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+gfxWindowsPlatform::AttemptD3D11DeviceCreation()
 {
   RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter();
   MOZ_ASSERT(adapter);
 
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY {
     hr =
       sD3D11CreateDeviceFn(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                            // Use
                            // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
                            // to prevent bug 1092260. IE 11 also uses this flag.
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
-                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     gfxCriticalError() << "Crash during D3D11 device creation";
     return false;
   }
 
   if (FAILED(hr) || !DoesD3D11DeviceWork(mD3D11Device)) {
     gfxCriticalError() << "D3D11 device creation failed" << hexa(hr);
@@ -1981,195 +1934,296 @@ gfxWindowsPlatform::AttemptD3D11DeviceCr
     return false;
   }
 
   CheckIfRenderTargetViewNeedsRecreating(mD3D11Device);
 
   // Only test this when not using WARP since it can fail and cause
   // GetDeviceRemovedReason to return weird values.
   mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
+  mD3D11Device->SetExceptionMode(0);
   mIsWARP = false;
   return true;
 }
 
 bool
-gfxWindowsPlatform::AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+gfxWindowsPlatform::AttemptWARPDeviceCreation()
 {
-  MOZ_ASSERT(!mD3D11Device);
-
   ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP());
 
   MOZ_SEH_TRY {
     HRESULT hr =
       sD3D11CreateDeviceFn(nullptr, D3D_DRIVER_TYPE_WARP, nullptr,
                            // Use
                            // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
                            // to prevent bug 1092260. IE 11 also uses this flag.
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
 
     if (FAILED(hr)) {
       // This should always succeed... in theory.
       gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr);
       return false;
     }
 
     reporterWARP.SetSuccessful();
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!";
     return false;
+
   }
 
+  // Only test for texture sharing on Windows 8 since it puts the device into
+  // an unusable state if used on Windows 7
+  if (IsWin8OrLater()) {
+    mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
+  }
+  mD3D11Device->SetExceptionMode(0);
   mIsWARP = true;
   return true;
 }
 
 bool
-gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation()
 {
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY {
     hr =
       sD3D11CreateDeviceFn(mIsWARP ? nullptr : GetDXGIAdapter(),
                            mIsWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN,
                            nullptr,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
     return false;
   }
 
-  return SUCCEEDED(hr);
+  if (FAILED(hr)) {
+    return false;
+  }
+
+  mD3D11ContentDevice->SetExceptionMode(0);
+
+  nsRefPtr<ID3D10Multithread> multi;
+  mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
+  multi->SetMultithreadProtected(TRUE);
+
+  Factory::SetDirect3D11Device(mD3D11ContentDevice);
+  return true;
 }
 
 bool
-gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels)
+gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation()
 {
   HRESULT hr = E_INVALIDARG;
   MOZ_SEH_TRY{
     hr =
       sD3D11CreateDeviceFn(GetDXGIAdapter(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
                            D3D11_CREATE_DEVICE_BGRA_SUPPORT,
-                           aFeatureLevels.Elements(), aFeatureLevels.Length(),
+                           mFeatureLevels.Elements(), mFeatureLevels.Length(),
                            D3D11_SDK_VERSION, byRef(mD3D11ImageBridgeDevice), nullptr, nullptr);
   } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
     return false;
   }
 
   if (FAILED(hr)) {
     return false;
   }
 
   mD3D11ImageBridgeDevice->SetExceptionMode(0);
-
-  return DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice);
+  if (!DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice)) {
+    mD3D11ImageBridgeDevice = nullptr;
+    return false;
+  }
+  return true;
 }
 
 void
-gfxWindowsPlatform::InitD3D11Devices()
+gfxWindowsPlatform::InitializeDevices()
 {
-  // This function attempts to initialize our D3D11 devices. If the hardware
-  // is not blacklisted for D3D11 layers. This will first attempt to create a
-  // hardware accelerated device. If this creation fails or the hardware is
+  // If we previously crashed initializing devices, or if we're in safe mode,
+  // bail out now.
+  DriverInitCrashDetection detectCrashes;
+  if (detectCrashes.DisableAcceleration() || InSafeMode()) {
+    mAcceleration = FeatureStatus::Blocked;
+    return;
+  }
+
+  // If acceleration is disabled, we refuse to initialize anything.
+  if (!ShouldUseLayersAcceleration()) {
+    mAcceleration = FeatureStatus::Disabled;
+    return;
+  }
+
+  // At this point, as far as we know, we can probably accelerate.
+  mAcceleration = FeatureStatus::Available;
+
+  // If we're going to prefer D3D9, stop here. The rest of this function
+  // attempts to use D3D11 features.
+  if (gfxPrefs::LayersPreferD3D9()) {
+    mD3D11Status = FeatureStatus::Disabled;
+    return;
+  }
+
+  // First, initialize D3D11. If this succeeds we attempt to use Direct2D.
+  InitializeD3D11();
+  if (mD3D11Status == FeatureStatus::Available) {
+    InitializeD2D();
+  }
+}
+
+void
+gfxWindowsPlatform::InitializeD3D11()
+{
+  // This function attempts to initialize our D3D11 devices, if the hardware
+  // is not blacklisted for D3D11 layers. This first attempt will try to create
+  // a hardware accelerated device. If this creation fails or the hardware is
   // blacklisted, then this function will abort if WARP is disabled, causing us
   // to fallback to D3D9 or Basic layers. If WARP is not disabled it will use
   // a WARP device which should always be available on Windows 7 and higher.
-  mD3D11DeviceInitialized = true;
-  mDoesD3D11TextureSharingWork = false;
 
-  MOZ_ASSERT(!mD3D11Device);
-
-  DriverInitCrashDetection detectCrashes;
-  if (InSafeMode() || detectCrashes.DisableAcceleration()) {
-    mD3D11Status = FeatureStatus::Blocked;
-    return;
-  }
-
-  D3D11Status status = CheckD3D11Support();
-  if (status == D3D11Status::Blocked) {
+  // Check if D3D11 is supported on this hardware.
+  D3D11Status support = CheckD3D11Support();
+  if (support == D3D11Status::Blocked) {
     mD3D11Status = FeatureStatus::Blacklisted;
     return;
   }
 
+  // Check if D3D11 is available on this system.
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
   sD3D11CreateDeviceFn =
     (decltype(D3D11CreateDevice)*)GetProcAddress(d3d11Module, "D3D11CreateDevice");
-
   if (!sD3D11CreateDeviceFn) {
     // We should just be on Windows Vista or XP in this case.
     mD3D11Status = FeatureStatus::Unavailable;
     return;
   }
 
-  nsTArray<D3D_FEATURE_LEVEL> featureLevels;
-  if (IsWin8OrLater()) {
-    featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
-  }
-  featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
-  featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
-  featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
-  featureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
-
-  if (status == D3D11Status::Ok) {
-    if (!AttemptD3D11DeviceCreation(featureLevels)) {
-      status = D3D11Status::TryWARP;
-    }
+  // If hardware acceleration is allowed, attempt to create a device. If this
+  // fails, we fall back to WARP.
+  if (support == D3D11Status::Ok && !AttemptD3D11DeviceCreation()) {
+    support = D3D11Status::OnlyWARP;
   }
-
-  if (IsWin8OrLater() &&
-      !gfxPrefs::LayersD3D11DisableWARP() &&
-      (status == D3D11Status::TryWARP || status == D3D11Status::ForceWARP))
-  {
-    AttemptWARPDeviceCreation(featureLevels);
-    mD3D11Status = FeatureStatus::Failed;
-  }
-
-  // Only test for texture sharing on Windows 8 since it puts the device into
-  // an unusable state if used on Windows 7
-  if (mD3D11Device && IsWin8OrLater()) {
-    mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device);
+  if (support == D3D11Status::OnlyWARP && CanUseWARP()) {
+    AttemptWARPDeviceCreation();
   }
 
   if (!mD3D11Device) {
-    // We could not get a D3D11 compositor, and there's nothing more we can try.
+    // Nothing more we can do.
+    mD3D11Status = FeatureStatus::Failed;
+    return;
+  }
+
+  // If we got here, we successfully got a D3D11 device.
+  mD3D11Status = FeatureStatus::Available;
+  MOZ_ASSERT(mD3D11Device);
+
+  if (!mIsWARP) {
+    AttemptD3D11ImageBridgeDeviceCreation();
+  }
+
+  // We leak these everywhere and we need them our entire runtime anyway, let's
+  // leak it here as well. We keep the pointer to sD3D11CreateDeviceFn around
+  // as well for D2D1 and device resets.
+  d3d11Module.disown();
+}
+
+static bool
+IsD2DBlacklisted()
+{
+  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+  if (gfxInfo) {
+    int32_t status;
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
+      if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+void
+gfxWindowsPlatform::InitializeD2D()
+{
+  if (!gfxPrefs::Direct2DForceEnabled()) {
+    if (IsD2DBlacklisted()) {
+      mD2DStatus = FeatureStatus::Blacklisted;
+      return;
+    }
+  }
+
+  // Do not ever try to use D2D if it's explicitly disabled.
+  if (gfxPrefs::Direct2DDisabled()) {
+    mD2DStatus = FeatureStatus::Disabled;
     return;
   }
 
-  mD3D11Device->SetExceptionMode(0);
-  mD3D11Status = FeatureStatus::Available;
+  // Direct2D is only Vista or higher, but we require a D3D11 compositor to
+  // use it. (This check may be implied by the fact that we do not get here
+  // without a D3D11 compositor device.)
+  if (!IsVistaOrLater()) {
+    mD2DStatus = FeatureStatus::Unavailable;
+    return;
+  }
+  if (!mDoesD3D11TextureSharingWork) {
+    mD2DStatus = FeatureStatus::Failed;
+    return;
+  }
 
-  // We create our device for D2D content drawing here. Normally we don't use
-  // D2D content drawing when using WARP. However when WARP is forced by
-  // default we will let Direct2D use WARP as well.
-  if (Factory::SupportsD2D1() && (!mIsWARP || (status == D3D11Status::ForceWARP))) {
-    if (!AttemptD3D11ContentDeviceCreation(featureLevels)) {
-      mD3D11ContentDevice = nullptr;
-      d3d11Module.disown();
-      return;
-    }
+  // Using Direct2D depends on DWrite support.
+  if (!mDWriteFactory && !InitDWriteSupport()) {
+    mD2DStatus = FeatureStatus::Failed;
+    return;
+  }
 
-    mD3D11ContentDevice->SetExceptionMode(0);
-    nsRefPtr<ID3D10Multithread> multi;
-    mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
-    multi->SetMultithreadProtected(TRUE);
+  // Initialize D2D 1.1.
+  InitializeD2D1();
 
-    Factory::SetDirect3D11Device(mD3D11ContentDevice);
+  // Initialize D2D 1.0.
+  VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled());
+  if (!mD3D10Device) {
+    mD2DStatus = FeatureStatus::Failed;
+    return;
   }
 
-  if (!mIsWARP) {
-    if (!AttemptD3D11ImageBridgeDeviceCreation(featureLevels)) {
-      mD3D11ImageBridgeDevice = nullptr;
-    }
+  mD2DStatus = FeatureStatus::Available;
+}
+
+bool
+gfxWindowsPlatform::InitializeD2D1()
+{
+  ScopedGfxFeatureReporter d2d1_1("D2D1.1");
+
+  if (!Factory::SupportsD2D1()) {
+    mD2D1Status = FeatureStatus::Unavailable;
+    return false;
+  }
+  if (!gfxPrefs::Direct2DUse1_1()) {
+    mD2D1Status = FeatureStatus::Disabled;
+    return false;
   }
 
-  // We leak these everywhere and we need them our entire runtime anyway, let's
-  // leak it here as well.
-  d3d11Module.disown();
+  // Normally we don't use D2D content drawing when using WARP. However if
+  // WARP is force-enabled, we wlil let Direct2D use WARP as well.
+  if (mIsWARP && !gfxPrefs::LayersD3D11ForceWARP()) {
+    mD2D1Status = FeatureStatus::Blocked;
+    return false;
+  }
+
+  if (!AttemptD3D11ContentDeviceCreation()) {
+    mD2D1Status = FeatureStatus::Failed;
+    return false;
+  }
+
+  mD2D1Status = FeatureStatus::Available;
+  d2d1_1.SetSuccessful();
+  return true;
 }
 
 already_AddRefed<ID3D11Device>
 gfxWindowsPlatform::CreateD3D11DecoderDevice()
 {
   nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll"));
   decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*)
     GetProcAddress(d3d11Module, "D3D11CreateDevice");
@@ -2437,33 +2491,43 @@ gfxWindowsPlatform::GetAcceleratedCompos
     if (gfxPlatform::CanUseDirect3D9()) {
       aBackends.AppendElement(LayersBackend::LAYERS_D3D9);
     } else {
       NS_WARNING("Direct3D 9-accelerated layers are not supported on this system.");
     }
   }
 }
 
+// Some features are dependent on other features. If this is the case, we
+// try to propagate the status of the parent feature if it wasn't available.
 FeatureStatus
-gfxWindowsPlatform::GetD2D1Status()
+gfxWindowsPlatform::GetD3D11Status() const
 {
-  if (GetD2DStatus() != FeatureStatus::Available ||
-      !Factory::SupportsD2D1())
-  {
+  if (mAcceleration != FeatureStatus::Available) {
+    return mAcceleration;
+  }
+  return mD3D11Status;
+}
+
+FeatureStatus
+gfxWindowsPlatform::GetD2DStatus() const
+{
+  if (GetD3D11Status() != FeatureStatus::Available) {
     return FeatureStatus::Unavailable;
   }
-
-  if (!GetD3D11ContentDevice()) {
-    return FeatureStatus::Failed;
-  }
+  return mD2DStatus;
+}
 
-  if (!gfxPrefs::Direct2DUse1_1()) {
-    return FeatureStatus::Disabled;
+FeatureStatus
+gfxWindowsPlatform::GetD2D1Status() const
+{
+  if (GetD3D11Status() != FeatureStatus::Available) {
+    return FeatureStatus::Unavailable;
   }
-  return FeatureStatus::Available;
+  return mD2D1Status;
 }
 
 unsigned
 gfxWindowsPlatform::GetD3D11Version()
 {
   ID3D11Device* device = GetD3D11Device();
   if (!device) {
     return 0;
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -224,19 +224,18 @@ public:
     static void GetCleartypeParams(nsTArray<ClearTypeParameterInfo>& aParams);
 
     virtual void FontsPrefsChanged(const char *aPref);
 
     void SetupClearTypeParams();
 
 #ifdef CAIRO_HAS_DWRITE_FONT
     IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; }
-    inline bool DWriteEnabled() { return mUseDirectWrite; }
+    inline bool DWriteEnabled() { return !!mDWriteFactory; }
     inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; }
-    IDWriteTextAnalyzer *GetDWriteAnalyzer() { return mDWriteAnalyzer; }
 
     IDWriteRenderingParams *GetRenderingParams(TextRenderingMode aRenderMode)
     { return mRenderingParams[aRenderMode]; }
 #else
     inline bool DWriteEnabled() { return false; }
 #endif
     void OnDeviceManagerDestroy(mozilla::layers::DeviceManagerD3D9* aDeviceManager);
     mozilla::layers::DeviceManagerD3D9* GetD3D9DeviceManager();
@@ -258,92 +257,94 @@ public:
     bool IsWARP() { return mIsWARP; }
     bool DoesD3D11TextureSharingWork() { return mDoesD3D11TextureSharingWork; }
 
     bool SupportsApzWheelInput() const override {
       return true;
     }
     bool SupportsApzTouchInput() const override;
 
+    // Recreate devices as needed for a device reset. Returns true if a device
+    // reset occurred.
+    bool HandleDeviceReset();
+    void UpdateBackendPrefs();
+
     // Return the diagnostic status of DirectX initialization. If
     // initialization has not been attempted, this returns
     // FeatureStatus::Unused.
-    mozilla::gfx::FeatureStatus GetD3D11Status() const {
-      return mD3D11Status;
-    }
-    mozilla::gfx::FeatureStatus GetD2DStatus() const {
-      return mD2DStatus;
-    }
+    mozilla::gfx::FeatureStatus GetD3D11Status() const;
+    mozilla::gfx::FeatureStatus GetD2DStatus() const;
+    mozilla::gfx::FeatureStatus GetD2D1Status() const;
     unsigned GetD3D11Version();
-    mozilla::gfx::FeatureStatus GetD2D1Status();
 
     virtual already_AddRefed<mozilla::gfx::VsyncSource> CreateHardwareVsyncSource() override;
     static mozilla::Atomic<size_t> sD3D11MemoryUsed;
     static mozilla::Atomic<size_t> sD3D9MemoryUsed;
     static mozilla::Atomic<size_t> sD3D9SurfaceImageUsed;
     static mozilla::Atomic<size_t> sD3D9SharedTextureUsed;
 
 protected:
     bool AccelerateLayersByDefault() override {
       return true;
     }
     void GetAcceleratedCompositorBackends(nsTArray<mozilla::layers::LayersBackend>& aBackends);
+    virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
 
 protected:
     RenderMode mRenderMode;
 
     int8_t mUseClearTypeForDownloadableFonts;
     int8_t mUseClearTypeAlways;
 
 private:
     void Init();
 
-    void InitD3D11Devices();
+    void InitializeDevices();
+    void InitializeD3D11();
+    void InitializeD2D();
+    bool InitializeD2D1();
+    bool InitDWriteSupport();
 
-    // Used by InitD3D11Devices().
+    void DisableD2D();
+
+    // Used by InitializeD3D11().
     enum class D3D11Status {
       Ok,
-      TryWARP,
-      ForceWARP,
+      OnlyWARP,
       Blocked
     };
     D3D11Status CheckD3D11Support();
-    bool AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
-    bool AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
-    bool AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
-    bool AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels);
-
-    // Used by UpdateRenderMode().
-    mozilla::gfx::FeatureStatus InitD2DSupport();
-    void InitDWriteSupport();
+    bool AttemptD3D11DeviceCreation();
+    bool AttemptWARPDeviceCreation();
+    bool AttemptD3D11ImageBridgeDeviceCreation();
+    bool AttemptD3D11ContentDeviceCreation();
 
     IDXGIAdapter1 *GetDXGIAdapter();
     bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason);
 
-    bool mUseDirectWrite;
-    bool mUsingGDIFonts;
-
 #ifdef CAIRO_HAS_DWRITE_FONT
     nsRefPtr<IDWriteFactory> mDWriteFactory;
-    nsRefPtr<IDWriteTextAnalyzer> mDWriteAnalyzer;
     nsRefPtr<IDWriteRenderingParams> mRenderingParams[TEXT_RENDERING_COUNT];
     DWRITE_MEASURING_MODE mMeasuringMode;
 #endif
     mozilla::RefPtr<IDXGIAdapter1> mAdapter;
     nsRefPtr<mozilla::layers::DeviceManagerD3D9> mDeviceManager;
     mozilla::RefPtr<ID3D10Device1> mD3D10Device;
     mozilla::RefPtr<ID3D11Device> mD3D11Device;
     mozilla::RefPtr<ID3D11Device> mD3D11ContentDevice;
     mozilla::RefPtr<ID3D11Device> mD3D11ImageBridgeDevice;
-    bool mD3D11DeviceInitialized;
     mozilla::RefPtr<mozilla::layers::ReadbackManagerD3D11> mD3D11ReadbackManager;
     bool mIsWARP;
     bool mHasDeviceReset;
     bool mDoesD3D11TextureSharingWork;
     DeviceResetReason mDeviceResetReason;
 
+    // These should not be accessed directly. Use the Get[Feature]Status
+    // accessors instead.
+    mozilla::gfx::FeatureStatus mAcceleration;
     mozilla::gfx::FeatureStatus mD3D11Status;
     mozilla::gfx::FeatureStatus mD2DStatus;
+    mozilla::gfx::FeatureStatus mD2D1Status;
 
-    virtual void GetPlatformCMSOutputProfile(void* &mem, size_t &size);
+    nsTArray<D3D_FEATURE_LEVEL> mFeatureLevels;
 };
 
 #endif /* GFX_WINDOWS_PLATFORM_H */
--- a/gfx/thebes/gfxWindowsSurface.cpp
+++ b/gfx/thebes/gfxWindowsSurface.cpp
@@ -295,14 +295,8 @@ gfxWindowsSurface::GetSize() const
         return mozilla::gfx::IntSize(-1, -1);
     }
 
     NS_ASSERTION(mSurface != nullptr, "CairoSurface() shouldn't be nullptr when mSurfaceValid is TRUE!");
 
     return mozilla::gfx::IntSize(cairo_win32_surface_get_width(mSurface),
                       cairo_win32_surface_get_height(mSurface));
 }
-
-gfxMemoryLocation
-gfxWindowsSurface::GetMemoryLocation() const
-{
-    return gfxMemoryLocation::IN_PROCESS_NONHEAP;
-}
--- a/gfx/thebes/gfxWindowsSurface.h
+++ b/gfx/thebes/gfxWindowsSurface.h
@@ -60,20 +60,16 @@ public:
     nsresult BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName);
     nsresult EndPrinting();
     nsresult AbortPrinting();
     nsresult BeginPage();
     nsresult EndPage();
 
     const mozilla::gfx::IntSize GetSize() const;
 
-    // The memory used by this surface lives in this process's address space,
-    // but not in the heap.
-    virtual gfxMemoryLocation GetMemoryLocation() const;
-
 private:
     void MakeInvalid(mozilla::gfx::IntSize& size);
 
     bool mOwnsDC;
     bool mForPrinting;
 
     HDC mDC;
     HWND mWnd;
--- a/gfx/thebes/gfxXlibSurface.cpp
+++ b/gfx/thebes/gfxXlibSurface.cpp
@@ -601,14 +601,8 @@ gfxXlibSurface::GetGLXPixmap()
         NS_ASSERTION(CairoStatus() != CAIRO_STATUS_SURFACE_FINISHED,
             "GetGLXPixmap called after surface finished");
 #endif
         mGLXPixmap = gl::sGLXLibrary.CreatePixmap(this);
     }
     return mGLXPixmap;
 }
 #endif
-
-gfxMemoryLocation
-gfxXlibSurface::GetMemoryLocation() const
-{
-    return gfxMemoryLocation::OUT_OF_PROCESS;
-}
--- a/gfx/thebes/gfxXlibSurface.h
+++ b/gfx/thebes/gfxXlibSurface.h
@@ -80,20 +80,16 @@ public:
     // Release ownership of this surface's Pixmap.  This is only valid
     // on gfxXlibSurfaces for which the user called TakePixmap(), or
     // on those created by a Create() factory method.
     Drawable ReleasePixmap();
 
     // Find a visual and colormap pair suitable for rendering to this surface.
     bool GetColormapAndVisual(Colormap* colormap, Visual **visual);
 
-    // This surface is a wrapper around X pixmaps, which are stored in the X
-    // server, not the main application.
-    virtual gfxMemoryLocation GetMemoryLocation() const override;
-
 #if defined(GL_PROVIDER_GLX)
     GLXPixmap GetGLXPixmap();
 #endif
 
     // Return true if cairo will take its slow path when this surface is used
     // in a pattern with EXTEND_PAD.  As a workaround for XRender's RepeatPad
     // not being implemented correctly on old X servers, cairo avoids XRender
     // and instead reads back to perform EXTEND_PAD with pixman.  Cairo does
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -329,22 +329,19 @@ DoCollectSizeOfCompositingSurfaces(const
   SurfaceKey key = RasterSurfaceKey(aSurface->GetImageSize(),
                                     imgIContainer::DECODE_FLAGS_DEFAULT,
                                     /* aFrameNum = */ 0);
 
   // Create a counter for this surface.
   SurfaceMemoryCounter counter(key, /* aIsLocked = */ true, aType);
 
   // Extract the surface's memory usage information.
-  size_t heap = aSurface
-    ->SizeOfExcludingThis(gfxMemoryLocation::IN_PROCESS_HEAP, aMallocSizeOf);
+  size_t heap = 0, nonHeap = 0;
+  aSurface->AddSizeOfExcludingThis(aMallocSizeOf, heap, nonHeap);
   counter.Values().SetDecodedHeap(heap);
-
-  size_t nonHeap = aSurface
-    ->SizeOfExcludingThis(gfxMemoryLocation::IN_PROCESS_NONHEAP, nullptr);
   counter.Values().SetDecodedNonHeap(nonHeap);
 
   // Record it.
   aCounters.AppendElement(counter);
 }
 
 void
 FrameAnimator::CollectSizeOfCompositingSurfaces(
--- a/image/Image.h
+++ b/image/Image.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_image_Image_h
 #define mozilla_image_Image_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
-#include "gfx2DGlue.h"                // for gfxMemoryLocation
+#include "gfx2DGlue.h"
 #include "imgIContainer.h"
 #include "ImageURL.h"
 #include "nsStringFwd.h"
 #include "ProgressTracker.h"
 #include "SurfaceCache.h"
 
 class nsIRequest;
 class nsIInputStream;
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -311,20 +311,16 @@ private:
   DrawableFrameRef LookupFrame(uint32_t aFrameNum,
                                const nsIntSize& aSize,
                                uint32_t aFlags);
   uint32_t GetCurrentFrameIndex() const;
   uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
 
   nsIntRect GetFirstFrameRect();
 
-  size_t
-    SizeOfDecodedWithComputedFallbackIfHeap(gfxMemoryLocation aLocation,
-                                            MallocSizeOf aMallocSizeOf) const;
-
   Pair<DrawResult, nsRefPtr<layers::Image>>
     GetCurrentImage(layers::ImageContainer* aContainer, uint32_t aFlags);
 
   void UpdateImageContainer();
 
   // We would like to just check if we have a zero lock count, but we can't do
   // that for animated images because in EnsureAnimExists we lock the image and
   // never unlock so that animated images always have their lock count >= 1. In
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -202,23 +202,20 @@ public:
       MOZ_ASSERT(aCachedSurface, "Should have a CachedSurface");
 
       SurfaceMemoryCounter counter(aCachedSurface->GetSurfaceKey(),
                                    aCachedSurface->IsLocked());
 
       if (aCachedSurface->mSurface) {
         counter.SubframeSize() = Some(aCachedSurface->mSurface->GetSize());
 
-        size_t heap = aCachedSurface->mSurface
-          ->SizeOfExcludingThis(gfxMemoryLocation::IN_PROCESS_HEAP,
-                                mMallocSizeOf);
+        size_t heap = 0, nonHeap = 0;
+        aCachedSurface->mSurface->AddSizeOfExcludingThis(mMallocSizeOf,
+                                                         heap, nonHeap);
         counter.Values().SetDecodedHeap(heap);
-
-        size_t nonHeap = aCachedSurface->mSurface
-          ->SizeOfExcludingThis(gfxMemoryLocation::IN_PROCESS_NONHEAP, nullptr);
         counter.Values().SetDecodedNonHeap(nonHeap);
       }
 
       mCounters.AppendElement(counter);
     }
 
   private:
     nsTArray<SurfaceMemoryCounter>& mCounters;
--- a/image/SurfaceCache.h
+++ b/image/SurfaceCache.h
@@ -9,17 +9,17 @@
  */
 
 #ifndef mozilla_image_SurfaceCache_h
 #define mozilla_image_SurfaceCache_h
 
 #include "mozilla/Maybe.h"           // for Maybe
 #include "mozilla/MemoryReporting.h" // for MallocSizeOf
 #include "mozilla/HashFunctions.h"   // for HashGeneric and AddToHash
-#include "gfx2DGlue.h"               // for gfxMemoryLocation
+#include "gfx2DGlue.h"
 #include "gfxPoint.h"                // for gfxSize
 #include "nsCOMPtr.h"                // for already_AddRefed
 #include "mozilla/gfx/Point.h"       // for mozilla::gfx::IntSize
 #include "mozilla/gfx/2D.h"          // for SourceSurface
 #include "SVGImageContext.h"         // for SVGImageContext
 
 namespace mozilla {
 namespace image {
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -1110,48 +1110,34 @@ bool imgFrame::GetCompositingFailed() co
 
 void
 imgFrame::SetCompositingFailed(bool val)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mCompositingFailed = val;
 }
 
-size_t
-imgFrame::SizeOfExcludingThis(gfxMemoryLocation aLocation,
-                              MallocSizeOf aMallocSizeOf) const
+void
+imgFrame::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
+                                 size_t& aHeapSizeOut,
+                                 size_t& aNonHeapSizeOut) const
 {
   MonitorAutoLock lock(mMonitor);
 
-  // aMallocSizeOf is only used if aLocation is
-  // gfxMemoryLocation::IN_PROCESS_HEAP.  It
-  // should be nullptr otherwise.
-  MOZ_ASSERT(
-    (aLocation == gfxMemoryLocation::IN_PROCESS_HEAP &&  aMallocSizeOf) ||
-    (aLocation != gfxMemoryLocation::IN_PROCESS_HEAP && !aMallocSizeOf),
-    "mismatch between aLocation and aMallocSizeOf");
-
-  size_t n = 0;
-
-  if (mPalettedImageData && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mPalettedImageData);
+  if (mPalettedImageData) {
+    aHeapSizeOut += aMallocSizeOf(mPalettedImageData);
   }
-  if (mImageSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mImageSurface);
+  if (mImageSurface) {
+    aHeapSizeOut += aMallocSizeOf(mImageSurface);
   }
-  if (mOptSurface && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mOptSurface);
+  if (mOptSurface) {
+    aHeapSizeOut += aMallocSizeOf(mOptSurface);
   }
 
-  if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_HEAP) {
-    n += aMallocSizeOf(mVBuf);
-    n += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
+  if (mVBuf) {
+    aHeapSizeOut += aMallocSizeOf(mVBuf);
+    aHeapSizeOut += mVBuf->HeapSizeOfExcludingThis(aMallocSizeOf);
+    aNonHeapSizeOut += mVBuf->NonHeapSizeOfExcludingThis();
   }
-
-  if (mVBuf && aLocation == gfxMemoryLocation::IN_PROCESS_NONHEAP) {
-    n += mVBuf->NonHeapSizeOfExcludingThis();
-  }
-
-  return n;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -259,18 +259,18 @@ public:
   void SetOptimizable();
 
   Color SinglePixelColor() const;
   bool IsSinglePixel() const;
 
   already_AddRefed<SourceSurface> GetSurface();
   already_AddRefed<DrawTarget> GetDrawTarget();
 
-  size_t SizeOfExcludingThis(gfxMemoryLocation aLocation,
-                             MallocSizeOf aMallocSizeOf) const;
+  void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, size_t& aHeapSizeOut,
+                              size_t& aNonHeapSizeOut) const;
 
 private: // methods
 
   ~imgFrame();
 
   nsresult LockImageData();
   nsresult UnlockImageData();
   nsresult Optimize();
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -848,16 +848,17 @@ MessageChannel::Send(Message* aMsg, Mess
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
     if (mCurrentTransaction == 0)
         mListener->OnBeginSyncTransaction();
 
 #ifdef OS_WIN
     SyncStackFrame frame(this, false);
+    NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
     CxxStackFrame f(*this, OUT_MESSAGE, msg);
 
     MonitorAutoLock lock(*mMonitor);
 
     if (mTimedOutMessageSeqno) {
         // Don't bother sending another sync message if a previous one timed out
@@ -989,16 +990,17 @@ MessageChannel::Send(Message* aMsg, Mess
 bool
 MessageChannel::Call(Message* aMsg, Message* aReply)
 {
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
 #ifdef OS_WIN
     SyncStackFrame frame(this, true);
+    NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
     // This must come before MonitorAutoLock, as its destructor acquires the
     // monitor lock.
     CxxStackFrame cxxframe(*this, OUT_MESSAGE, aMsg);
 
     MonitorAutoLock lock(*mMonitor);
     if (!Connected()) {
@@ -1027,16 +1029,22 @@ MessageChannel::Call(Message* aMsg, Mess
         // might have already processed the OnError event. if so,
         // trying another loop iteration will be futile because
         // channel state will have been cleared
         if (!Connected()) {
             ReportConnectionError("MessageChannel::Call");
             return false;
         }
 
+#ifdef OS_WIN
+        /* We should pump messages at this point to ensure that the IPC peer
+           does not become deadlocked on a pending inter-thread SendMessage() */
+        neuteredRgn.PumpOnce();
+#endif
+
         // Now might be the time to process a message deferred because of race
         // resolution.
         MaybeUndeferIncall();
 
         // Wait for an event to occur.
         while (!InterruptEventOccurred()) {
             bool maybeTimedOut = !WaitForInterruptNotify();
 
@@ -1143,16 +1151,17 @@ MessageChannel::Call(Message* aMsg, Mess
     return true;
 }
 
 bool
 MessageChannel::WaitForIncomingMessage()
 {
 #ifdef OS_WIN
     SyncStackFrame frame(this, true);
+    NeuteredWindowRegion neuteredRgn(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 #endif
 
     { // Scope for lock
         MonitorAutoLock lock(*mMonitor);
         AutoEnterWaitForIncoming waitingForIncoming(*this);
         if (mChannelState != ChannelConnected) {
             return false;
         }
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -10,16 +10,19 @@
 
 #include "base/basictypes.h"
 #include "base/message_loop.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Vector.h"
 #include "mozilla/WeakPtr.h"
+#if defined(OS_WIN)
+#include "mozilla/ipc/Neutering.h"
+#endif // defined(OS_WIN)
 #include "mozilla/ipc/Transport.h"
 #include "MessageLink.h"
 #include "nsAutoPtr.h"
 
 #include <deque>
 #include <stack>
 #include <math.h>
 
new file mode 100644
--- /dev/null
+++ b/ipc/glue/Neutering.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_Neutering_h
+#define mozilla_ipc_Neutering_h
+
+#include "mozilla/GuardObjects.h"
+
+/**
+ * This header declares RAII wrappers for Window neutering. See
+ * WindowsMessageLoop.cpp for more details.
+ */
+
+namespace mozilla {
+namespace ipc {
+
+/**
+ * This class is a RAII wrapper around Window neutering. As long as a
+ * NeuteredWindowRegion object is instantiated, Win32 windows belonging to the
+ * current thread will be neutered. It is safe to nest multiple instances of
+ * this class.
+ */
+class MOZ_STACK_CLASS NeuteredWindowRegion
+{
+public:
+  explicit NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+  ~NeuteredWindowRegion();
+
+  /**
+   * This function clears any backlog of nonqueued messages that are pending for
+   * the current thread.
+   */
+  void PumpOnce();
+
+private:
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  bool mNeuteredByThis;
+};
+
+/**
+ * This class is analagous to MutexAutoUnlock for Mutex; it is an RAII class
+ * that is to be instantiated within a NeuteredWindowRegion, thus temporarily
+ * disabling neutering for the remainder of its enclosing block.
+ * @see NeuteredWindowRegion
+ */
+class MOZ_STACK_CLASS DeneuteredWindowRegion
+{
+public:
+  DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
+  ~DeneuteredWindowRegion();
+
+private:
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+  bool mReneuter;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_Neutering_h
+
--- a/ipc/glue/WindowsMessageLoop.cpp
+++ b/ipc/glue/WindowsMessageLoop.cpp
@@ -3,16 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 
 #include "WindowsMessageLoop.h"
+#include "Neutering.h"
 #include "MessageChannel.h"
 
 #include "nsAutoPtr.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsIXULAppInfo.h"
 #include "WinUtils.h"
 
@@ -857,16 +858,91 @@ MessageChannel::SpinInternalEventLoop()
 
 static inline bool
 IsTimeoutExpired(PRIntervalTime aStart, PRIntervalTime aTimeout)
 {
   return (aTimeout != PR_INTERVAL_NO_TIMEOUT) &&
     (aTimeout <= (PR_IntervalNow() - aStart));
 }
 
+static HHOOK gWindowHook;
+
+static inline void
+StartNeutering()
+{
+  MOZ_ASSERT(gUIThreadId);
+  MOZ_ASSERT(!gWindowHook);
+  NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
+               "Shouldn't be pumping already!");
+  MessageChannel::SetIsPumpingMessages(true);
+  gWindowHook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
+                                   nullptr, gUIThreadId);
+  NS_ASSERTION(gWindowHook, "Failed to set hook!");
+}
+
+static void
+StopNeutering()
+{
+  MOZ_ASSERT(MessageChannel::IsPumpingMessages());
+  ::UnhookWindowsHookEx(gWindowHook);
+  gWindowHook = NULL;
+  ::UnhookNeuteredWindows();
+  // Before returning we need to set a hook to run any deferred messages that
+  // we received during the IPC call. The hook will unset itself as soon as
+  // someone else calls GetMessage, PeekMessage, or runs code that generates
+  // a "nonqueued" message.
+  ::ScheduleDeferredMessageRun();
+  MessageChannel::SetIsPumpingMessages(false);
+}
+
+NeuteredWindowRegion::NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
+  : mNeuteredByThis(!gWindowHook)
+{
+  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  if (aDoNeuter && mNeuteredByThis) {
+    StartNeutering();
+  }
+}
+
+NeuteredWindowRegion::~NeuteredWindowRegion()
+{
+  if (gWindowHook && mNeuteredByThis) {
+    StopNeutering();
+  }
+}
+
+void
+NeuteredWindowRegion::PumpOnce()
+{
+  MSG msg = {0};
+  // Pump any COM messages so that we don't hang due to STA marshaling.
+  if (gCOMWindow && ::PeekMessageW(&msg, gCOMWindow, 0, 0, PM_REMOVE)) {
+      ::TranslateMessage(&msg);
+      ::DispatchMessageW(&msg);
+  }
+  // Expunge any nonqueued messages on the current thread.
+  ::PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
+}
+
+DeneuteredWindowRegion::DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
+  : mReneuter(gWindowHook != NULL)
+{
+  MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  if (mReneuter) {
+    StopNeutering();
+  }
+}
+
+DeneuteredWindowRegion::~DeneuteredWindowRegion()
+{
+  if (mReneuter) {
+    StartNeutering();
+  }
+}
+
 bool
 MessageChannel::WaitForSyncNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
   MOZ_ASSERT(gUIThreadId, "InitUIThread was not called!");
 
   // Use a blocking wait if this channel does not require
@@ -911,25 +987,16 @@ MessageChannel::WaitForSyncNotify()
     InitTimeoutData(&timeoutData, mTimeoutMs);
 
     // We only do this to ensure that we won't get stuck in
     // MsgWaitForMultipleObjects below.
     timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
     NS_ASSERTION(timerId, "SetTimer failed!");
   }
 
-  // Setup deferred processing of native events while we wait for a response.
-  NS_ASSERTION(!MessageChannel::IsPumpingMessages(),
-               "Shouldn't be pumping already!");
-
-  MessageChannel::SetIsPumpingMessages(true);
-  HHOOK windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
-                                      nullptr, gUIThreadId);
-  NS_ASSERTION(windowHook, "Failed to set hook!");
-
   {
     while (1) {
       MSG msg = { 0 };
       // Don't get wrapped up in here if the child connection dies.
       {
         MonitorAutoLock lock(*mMonitor);
         if (!Connected()) {
           break;
@@ -993,35 +1060,21 @@ MessageChannel::WaitForSyncNotify()
       if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
           !haveSentMessagesPending) {
         // Message was for child, we should wait a bit.
         SwitchToThread();
       }
     }
   }
 
-  // Unhook the neutered window procedure hook.
-  UnhookWindowsHookEx(windowHook);
-
-  // Unhook any neutered windows procedures so messages can be delivered
-  // normally.
-  UnhookNeuteredWindows();
-
-  // Before returning we need to set a hook to run any deferred messages that
-  // we received during the IPC call. The hook will unset itself as soon as
-  // someone else calls GetMessage, PeekMessage, or runs code that generates
-  // a "nonqueued" message.
-  ScheduleDeferredMessageRun();
-
   if (timerId) {
     KillTimer(nullptr, timerId);
+    timerId = 0;
   }
 
-  MessageChannel::SetIsPumpingMessages(false);
-
   return WaitResponse(timedout);
 }
 
 bool
 MessageChannel::WaitForInterruptNotify()
 {
   mMonitor->AssertCurrentThreadOwns();
 
@@ -1045,66 +1098,38 @@ MessageChannel::WaitForInterruptNotify()
 
   MonitorAutoUnlock unlock(*mMonitor);
 
   bool timedout = false;
 
   UINT_PTR timerId = 0;
   TimeoutData timeoutData = { 0 };
 
-  // windowHook is used as a flag variable for the loop below: if it is set
+  // gWindowHook is used as a flag variable for the loop below: if it is set
   // and we start to spin a nested event loop, we need to clear the hook and
   // process deferred/pending messages.
-  // If windowHook is nullptr, MessageChannel::IsPumpingMessages should be false.
-  HHOOK windowHook = nullptr;
-
   while (1) {
-    NS_ASSERTION((!!windowHook) == MessageChannel::IsPumpingMessages(),
-                 "windowHook out of sync with reality");
+    NS_ASSERTION((!!gWindowHook) == MessageChannel::IsPumpingMessages(),
+                 "gWindowHook out of sync with reality");
 
     if (mTopFrame->mSpinNestedEvents) {
-      if (windowHook) {
-        UnhookWindowsHookEx(windowHook);
-        windowHook = nullptr;
-
-        if (timerId) {
-          KillTimer(nullptr, timerId);
-          timerId = 0;
-        }
-
-        // Used by widget to assert on incoming native events
-        MessageChannel::SetIsPumpingMessages(false);
-
-        // Unhook any neutered windows procedures so messages can be delievered
-        // normally.
-        UnhookNeuteredWindows();
-
-        // Send all deferred "nonqueued" message to the intended receiver.
-        // We're dropping into SpinInternalEventLoop so we should be fairly
-        // certain these will get delivered soohn.
-        ScheduleDeferredMessageRun();
+      if (gWindowHook && timerId) {
+        KillTimer(nullptr, timerId);
+        timerId = 0;
       }
+      DeneuteredWindowRegion deneuteredRgn;
       SpinInternalEventLoop();
       ResetEvent(mEvent);
       return true;
     }
 
-    if (!windowHook) {
-      MessageChannel::SetIsPumpingMessages(true);
-      windowHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWindowProcedureHook,
-                                    nullptr, gUIThreadId);
-      NS_ASSERTION(windowHook, "Failed to set hook!");
-
-      NS_ASSERTION(!timerId, "Timer already initialized?");
-
-      if (mTimeoutMs != kNoTimeout) {
-        InitTimeoutData(&timeoutData, mTimeoutMs);
-        timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
-        NS_ASSERTION(timerId, "SetTimer failed!");
-      }
+    if (mTimeoutMs != kNoTimeout && !timerId) {
+      InitTimeoutData(&timeoutData, mTimeoutMs);
+      timerId = SetTimer(nullptr, 0, mTimeoutMs, nullptr);
+      NS_ASSERTION(timerId, "SetTimer failed!");
     }
 
     MSG msg = { 0 };
 
     // Don't get wrapped up in here if the child connection dies.
     {
       MonitorAutoLock lock(*mMonitor);
       if (!Connected()) {
@@ -1146,37 +1171,21 @@ MessageChannel::WaitForInterruptNotify()
     // MsgWaitForMultipleObjects every time.
     if (!PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE) &&
         !haveSentMessagesPending) {
       // Message was for child, we should wait a bit.
       SwitchToThread();
     }
   }
 
-  if (windowHook) {
-    // Unhook the neutered window procedure hook.
-    UnhookWindowsHookEx(windowHook);
-
-    // Unhook any neutered windows procedures so messages can be delivered
-    // normally.
-    UnhookNeuteredWindows();
-
-    // Before returning we need to set a hook to run any deferred messages that
-    // we received during the IPC call. The hook will unset itself as soon as
-    // someone else calls GetMessage, PeekMessage, or runs code that generates
-    // a "nonqueued" message.
-    ScheduleDeferredMessageRun();
-
-    if (timerId) {
-      KillTimer(nullptr, timerId);
-    }
+  if (timerId) {
+    KillTimer(nullptr, timerId);
+    timerId = 0;
   }
 
-  MessageChannel::SetIsPumpingMessages(false);
-
   return WaitResponse(timedout);
 }
 
 void
 MessageChannel::NotifyWorkerThread()
 {
   mMonitor->AssertCurrentThreadOwns();
 
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -20,16 +20,17 @@ EXPORTS.mozilla.ipc += [
     'FileDescriptorSetChild.h',
     'FileDescriptorSetParent.h',
     'FileDescriptorUtils.h',
     'GeckoChildProcessHost.h',
     'InputStreamUtils.h',
     'IOThreadChild.h',
     'MessageChannel.h',
     'MessageLink.h',
+    'Neutering.h',
     'ProcessChild.h',
     'ProtocolUtils.h',
     'ScopedXREEmbed.h',
     'SharedMemory.h',
     'SharedMemoryBasic.h',
     'SharedMemorySysV.h',
     'Shmem.h',
     'Transport.h',
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -158,63 +158,52 @@ class JS_FRIEND_API(GCCellPtr)
   public:
     // Construction from a void* and trace kind.
     GCCellPtr(void* gcthing, JS::TraceKind traceKind) : ptr(checkedCast(gcthing, traceKind)) {}
 
     // Automatically construct a null GCCellPtr from nullptr.
     MOZ_IMPLICIT GCCellPtr(decltype(nullptr)) : ptr(checkedCast(nullptr, JS::TraceKind::Null)) {}
 
     // Construction from an explicit type.
-    explicit GCCellPtr(JSObject* obj) : ptr(checkedCast(obj, JS::TraceKind::Object)) { }
-    explicit GCCellPtr(JSFunction* fun) : ptr(checkedCast(fun, JS::TraceKind::Object)) { }
-    explicit GCCellPtr(JSString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
+    template <typename T>
+    explicit GCCellPtr(T* p) : ptr(checkedCast(p, JS::MapTypeToTraceKind<T>::kind)) { }
+    explicit GCCellPtr(JSFunction* p) : ptr(checkedCast(p, JS::TraceKind::Object)) { }
     explicit GCCellPtr(JSFlatString* str) : ptr(checkedCast(str, JS::TraceKind::String)) { }
-    explicit GCCellPtr(JS::Symbol* sym) : ptr(checkedCast(sym, JS::TraceKind::Symbol)) { }
-    explicit GCCellPtr(JSScript* script) : ptr(checkedCast(script, JS::TraceKind::Script)) { }
     explicit GCCellPtr(const Value& v);
 
     JS::TraceKind kind() const {
         JS::TraceKind traceKind = JS::TraceKind(ptr & OutOfLineTraceKindMask);
         if (uintptr_t(traceKind) != OutOfLineTraceKindMask)
             return traceKind;
         return outOfLineKind();
     }
 
     // Allow GCCellPtr to be used in a boolean context.
     explicit operator bool() const {
         MOZ_ASSERT(bool(asCell()) == (kind() != JS::TraceKind::Null));
         return asCell();
     }
 
     // Simplify checks to the kind.
-    bool isObject() const { return kind() == JS::TraceKind::Object; }
-    bool isScript() const { return kind() == JS::TraceKind::Script; }
-    bool isString() const { return kind() == JS::TraceKind::String; }
-    bool isSymbol() const { return kind() == JS::TraceKind::Symbol; }
-    bool isShape() const { return kind() == JS::TraceKind::Shape; }
-    bool isObjectGroup() const { return kind() == JS::TraceKind::ObjectGroup; }
+    template <typename T>
+    bool is() const { return kind() == JS::MapTypeToTraceKind<T>::kind; }
 
     // Conversions to more specific types must match the kind. Access to
     // further refined types is not allowed directly from a GCCellPtr.
-    JSObject* toObject() const {
-        MOZ_ASSERT(kind() == JS::TraceKind::Object);
-        return reinterpret_cast<JSObject*>(asCell());
-    }
-    JSString* toString() const {
-        MOZ_ASSERT(kind() == JS::TraceKind::String);
-        return reinterpret_cast<JSString*>(asCell());
+    template <typename T>
+    T& as() const {
+        MOZ_ASSERT(kind() == JS::MapTypeToTraceKind<T>::kind);
+        // We can't use static_cast here, because the fact that JSObject
+        // inherits from js::gc::Cell is not part of the public API.
+        return *reinterpret_cast<T*>(asCell());
     }
-    JSScript* toScript() const {
-        MOZ_ASSERT(kind() == JS::TraceKind::Script);
-        return reinterpret_cast<JSScript*>(asCell());
-    }
-    Symbol* toSymbol() const {
-        MOZ_ASSERT(kind() == JS::TraceKind::Symbol);
-        return reinterpret_cast<Symbol*>(asCell());
-    }
+
+    // Return a pointer to the cell this |GCCellPtr| refers to, or |nullptr|.
+    // (It would be more symmetrical with |to| for this to return a |Cell&|, but
+    // the result can be |nullptr|, and null references are undefined behavior.)
     js::gc::Cell* asCell() const {
         return reinterpret_cast<js::gc::Cell*>(ptr & ~OutOfLineTraceKindMask);
     }
 
     // The CC's trace logger needs an identity that is XPIDL serializable.
     uint64_t unsafeAsInteger() const {
         return static_cast<uint64_t>(unsafeAsUIntPtr());
     }
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -604,16 +604,20 @@ class DispatchWrapper
 #if JS_BITS_PER_WORD == 32
     uint32_t padding; // Ensure the storage fields have CellSize alignment.
 #endif
     T storage;
 
   public:
     // Mimic a pointer type, so that we can drop into Rooted.
     MOZ_IMPLICIT DispatchWrapper(const T& initial) : tracer(&T::trace), storage(initial) {}
+    MOZ_IMPLICIT DispatchWrapper(T&& initial)
+      : tracer(&T::trace),
+        storage(mozilla::Forward<T>(initial))
+    { }
     T* operator &() { return &storage; }
     const T* operator &() const { return &storage; }
     operator T&() { return storage; }
     operator const T&() const { return storage; }
 
     // Trace the contained storage (of unknown type) using the trace function
     // we set aside when we did know the type.
     static void TraceWrapped(JSTracer* trc, JS::StaticTraceable* thingp, const char* name) {
--- a/js/public/TraceKind.h
+++ b/js/public/TraceKind.h
@@ -2,16 +2,28 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 js_TraceKind_h
 #define js_TraceKind_h
 
+#include "js/TypeDecls.h"
+
+// Forward declarations of all the types a TraceKind can denote.
+namespace js {
+class BaseShape;
+class LazyScript;
+class ObjectGroup;
+namespace jit {
+class JitCode;
+} // namespace jit
+} // namespace js
+
 namespace JS {
 
 // When tracing a thing, the GC needs to know about the layout of the object it
 // is looking at. There are a fixed number of different layouts that the GC
 // knows about. The "trace kind" is a static map which tells which layout a GC
 // thing has.
 //
 // Although this map is public, the details are completely hidden. Not all of
@@ -42,11 +54,87 @@ enum class TraceKind
     JitCode = 0x1F,
     LazyScript = 0x2F
 };
 const static uintptr_t OutOfLineTraceKindMask = 0x07;
 static_assert(uintptr_t(JS::TraceKind::BaseShape) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::JitCode) & OutOfLineTraceKindMask, "mask bits are set");
 static_assert(uintptr_t(JS::TraceKind::LazyScript) & OutOfLineTraceKindMask, "mask bits are set");
 
+#define JS_FOR_EACH_TRACEKIND(D) \
+ /* PrettyName       TypeName           AddToCCKind */ \
+    D(BaseShape,     js::BaseShape,     true) \
+    D(JitCode,       js::jit::JitCode,  true) \
+    D(LazyScript,    js::LazyScript,    true) \
+    D(Object,        JSObject,          true) \
+    D(ObjectGroup,   js::ObjectGroup,   true) \
+    D(Script,        JSScript,          true) \
+    D(Shape,         js::Shape,         true) \
+    D(String,        JSString,          false) \
+    D(Symbol,        JS::Symbol,        false)
+
+// Map from base trace type to the trace kind.
+template <typename T> struct MapTypeToTraceKind {};
+#define JS_EXPAND_DEF(name, type, _) \
+    template <> struct MapTypeToTraceKind<type> { \
+        static const JS::TraceKind kind = JS::TraceKind::name; \
+    };
+JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+
+// Fortunately, few places in the system need to deal with fully abstract
+// cells. In those places that do, we generally want to move to a layout
+// templated function as soon as possible. This template wraps the upcast
+// for that dispatch.
+//
+// Given a call:
+//
+//    DispatchTraceKindTyped(f, thing, traceKind, ... args)
+//
+// Downcast the |void *thing| to the specific type designated by |traceKind|,
+// and pass it to the functor |f| along with |... args|, forwarded. Pass the
+// type designated by |traceKind| as the functor's template argument. The
+// |thing| parameter is optional; without it, we simply pass through |... args|.
+
+// GCC and Clang require an explicit template declaration in front of the
+// specialization of operator() because it is a dependent template. MSVC, on
+// the other hand, gets very confused if we have a |template| token there.
+#ifdef _MSC_VER
+# define JS_DEPENDENT_TEMPLATE_HINT
+#else
+# define JS_DEPENDENT_TEMPLATE_HINT template
+#endif
+template <typename F, typename... Args>
+auto
+DispatchTraceKindTyped(F f, JS::TraceKind traceKind, Args&&... args)
+  -> decltype(f. JS_DEPENDENT_TEMPLATE_HINT operator()<JSObject>(mozilla::Forward<Args>(args)...))
+{
+    switch (traceKind) {
+#define JS_EXPAND_DEF(name, type, _) \
+      case JS::TraceKind::name: \
+        return f. JS_DEPENDENT_TEMPLATE_HINT operator()<type>(mozilla::Forward<Args>(args)...);
+      JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+      default:
+          MOZ_CRASH("Invalid trace kind in DispatchTraceKindTyped.");
+    }
+}
+#undef JS_DEPENDENT_TEMPLATE_HINT
+
+template <typename F, typename... Args>
+auto
+DispatchTraceKindTyped(F f, void* thing, JS::TraceKind traceKind, Args&&... args)
+  -> decltype(f(reinterpret_cast<JSObject*>(0), mozilla::Forward<Args>(args)...))
+{
+    switch (traceKind) {
+#define JS_EXPAND_DEF(name, type, _) \
+      case JS::TraceKind::name: \
+          return f(static_cast<type*>(thing), mozilla::Forward<Args>(args)...);
+      JS_FOR_EACH_TRACEKIND(JS_EXPAND_DEF);
+#undef JS_EXPAND_DEF
+      default:
+          MOZ_CRASH("Invalid trace kind in DispatchTraceKindTyped.");
+    }
+}
+
 } // namespace JS
 
 #endif // js_TraceKind_h
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -21,25 +21,16 @@ template <typename T> class Heap;
 template <typename T> class TenuredHeap;
 
 // Returns a static string equivalent of |kind|.
 JS_FRIEND_API(const char*)
 GCTraceKindToAscii(JS::TraceKind kind);
 
 } // namespace JS
 
-namespace js {
-class BaseShape;
-class LazyScript;
-class ObjectGroup;
-namespace jit {
-class JitCode;
-} // namespace jit
-} // namespace js
-
 enum WeakMapTraceKind {
     DoNotTraceWeakMaps = 0,
     TraceWeakMapValues = 1,
     TraceWeakMapKeysValues = 2
 };
 
 class JS_PUBLIC_API(JSTracer)
 {
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -30,16 +30,18 @@
 
 #include "gc/Nursery-inl.h"
 #include "vm/String-inl.h"
 #include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
+using JS::MapTypeToTraceKind;
+
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::IsBaseOf;
 using mozilla::IsSame;
 using mozilla::MakeRange;
 using mozilla::PodCopy;
 
 // Tracing Overview
@@ -250,17 +252,17 @@ template <>
 void
 CheckTracedThing<jsid>(JSTracer* trc, jsid id)
 {
     DispatchIdTyped(CheckTracedFunctor<jsid>(), id, trc);
 }
 
 #define IMPL_CHECK_TRACED_THING(_, type, __) \
     template void CheckTracedThing<type*>(JSTracer*, type*);
-FOR_EACH_GC_LAYOUT(IMPL_CHECK_TRACED_THING);
+JS_FOR_EACH_TRACEKIND(IMPL_CHECK_TRACED_THING);
 #undef IMPL_CHECK_TRACED_THING
 } // namespace js
 
 static bool
 ShouldMarkCrossCompartment(JSTracer* trc, JSObject* src, Cell* cell)
 {
     if (!trc->isMarkingTracer())
         return true;
@@ -399,17 +401,17 @@ template <typename T,
                         : IsBaseOf<Shape, T>::value        ? JS::TraceKind::Shape
                         : IsBaseOf<BaseShape, T>::value    ? JS::TraceKind::BaseShape
                         : IsBaseOf<jit::JitCode, T>::value ? JS::TraceKind::JitCode
                         : IsBaseOf<LazyScript, T>::value   ? JS::TraceKind::LazyScript
                         :                                    JS::TraceKind::ObjectGroup>
 struct BaseGCType;
 #define IMPL_BASE_GC_TYPE(name, type_, _) \
     template <typename T> struct BaseGCType<T, JS::TraceKind:: name> { typedef type_ type; };
-FOR_EACH_GC_LAYOUT(IMPL_BASE_GC_TYPE);
+JS_FOR_EACH_TRACEKIND(IMPL_BASE_GC_TYPE);
 #undef IMPL_BASE_GC_TYPE
 
 // Our barrier templates are parameterized on the pointer types so that we can
 // share the definitions with Value and jsid. Thus, we need to strip the
 // pointer before sending the type to BaseGCType and re-add it on the other
 // side. As such:
 template <typename T> struct PtrBaseGCType {};
 template <> struct PtrBaseGCType<Value> { typedef Value type; };
@@ -549,17 +551,17 @@ struct TraceRootFunctor {
 
 void
 js::TraceGenericPointerRoot(JSTracer* trc, Cell** thingp, const char* name)
 {
     MOZ_ASSERT(thingp);
     if (!*thingp)
         return;
     TraceRootFunctor f;
-    CallTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
+    DispatchTraceKindTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
 }
 
 // A typed functor adaptor for TraceManuallyBarrieredEdge.
 struct TraceManuallyBarrieredEdgeFunctor {
     template <typename T>
     void operator()(JSTracer* trc, Cell** thingp, const char* name) {
         TraceManuallyBarrieredEdge(trc, reinterpret_cast<T**>(thingp), name);
     }
@@ -567,29 +569,29 @@ struct TraceManuallyBarrieredEdgeFunctor
 
 void
 js::TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, Cell** thingp, const char* name)
 {
     MOZ_ASSERT(thingp);
     if (!*thingp)
         return;
     TraceManuallyBarrieredEdgeFunctor f;
-    CallTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
+    DispatchTraceKindTyped(f, (*thingp)->getTraceKind(), trc, thingp, name);
 }
 
 // This method is responsible for dynamic dispatch to the real tracer
 // implementation. Consider replacing this choke point with virtual dispatch:
 // a sufficiently smart C++ compiler may be able to devirtualize some paths.
 template <typename T>
 void
 DispatchToTracer(JSTracer* trc, T* thingp, const char* name)
 {
 #define IS_SAME_TYPE_OR(name, type, _) mozilla::IsSame<type*, T>::value ||
     static_assert(
-            FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
+            JS_FOR_EACH_TRACEKIND(IS_SAME_TYPE_OR)
             mozilla::IsSame<T, JS::Value>::value ||
             mozilla::IsSame<T, jsid>::value,
             "Only the base cell layout types are allowed into marking/tracing internals");
 #undef IS_SAME_TYPE_OR
     if (trc->isMarkingTracer())
         return DoMarking(static_cast<GCMarker*>(trc), *thingp);
     if (trc->isTenuringTracer())
         return static_cast<TenuringTracer*>(trc)->traverse(thingp);
@@ -1726,17 +1728,17 @@ struct PushArenaFunctor {
     template <typename T> void operator()(GCMarker* gcmarker, ArenaHeader* aheader) {
         PushArenaTyped<T>(gcmarker, aheader);
     }
 };
 
 void
 gc::PushArena(GCMarker* gcmarker, ArenaHeader* aheader)
 {
-    CallTyped(PushArenaFunctor(), MapAllocToTraceKind(aheader->getAllocKind()), gcmarker, aheader);
+    DispatchTraceKindTyped(PushArenaFunctor(), MapAllocToTraceKind(aheader->getAllocKind()), gcmarker, aheader);
 }
 
 #ifdef DEBUG
 void
 GCMarker::checkZone(void* p)
 {
     MOZ_ASSERT(started);
     DebugOnly<Cell*> cell = static_cast<Cell*>(p);
@@ -2111,17 +2113,17 @@ js::TenuringTracer::moveElementsToTenure
 /*** IsMarked / IsAboutToBeFinalized **************************************************************/
 
 template <typename T>
 static inline void
 CheckIsMarkedThing(T* thingp)
 {
 #define IS_SAME_TYPE_OR(name, type, _) mozilla::IsSame<type*, T>::value ||
     static_assert(
-            FOR_EACH_GC_LAYOUT(IS_SAME_TYPE_OR)
+            JS_FOR_EACH_TRACEKIND(IS_SAME_TYPE_OR)
             false, "Only the base cell layout types are allowed into marking/tracing internals");
 #undef IS_SAME_TYPE_OR
 
 #ifdef DEBUG
     MOZ_ASSERT(thingp);
     MOZ_ASSERT(*thingp);
     JSRuntime* rt = (*thingp)->runtimeFromAnyThread();
     MOZ_ASSERT_IF(!ThingIsPermanentAtomOrWellKnownSymbol(*thingp),
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -521,17 +521,17 @@ BufferGrayRootsTracer::onChild(const JS:
     gc::TenuredCell* tenured = gc::TenuredCell::fromPointer(thing.asCell());
 
     Zone* zone = tenured->zone();
     if (zone->isCollecting()) {
         // See the comment on SetMaybeAliveFlag to see why we only do this for
         // objects and scripts. We rely on gray root buffering for this to work,
         // but we only need to worry about uncollected dead compartments during
         // incremental GCs (when we do gray root buffering).
-        CallTyped(SetMaybeAliveFunctor(), tenured, thing.kind());
+        DispatchTraceKindTyped(SetMaybeAliveFunctor(), tenured, thing.kind());
 
         if (!zone->gcGrayRoots.append(tenured))
             bufferingGrayRootsFailed = true;
     }
 }
 
 void
 GCRuntime::markBufferedGrayRoots(JS::Zone* zone)
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -15,42 +15,46 @@ class JSLinearString;
 namespace js {
 
 class PropertyName;
 class NativeObject;
 class ArrayObject;
 class GlobalObject;
 class PlainObject;
 class ScriptSourceObject;
+class SavedFrame;
 class Shape;
 class ObjectGroup;
 
 // These are internal counterparts to the public types such as HandleObject.
 
 typedef JS::Handle<NativeObject*>      HandleNativeObject;
 typedef JS::Handle<Shape*>             HandleShape;
 typedef JS::Handle<ObjectGroup*>       HandleObjectGroup;
 typedef JS::Handle<JSAtom*>            HandleAtom;
 typedef JS::Handle<JSLinearString*>    HandleLinearString;
 typedef JS::Handle<PropertyName*>      HandlePropertyName;
 typedef JS::Handle<ArrayObject*>       HandleArrayObject;
 typedef JS::Handle<PlainObject*>       HandlePlainObject;
+typedef JS::Handle<SavedFrame*>        HandleSavedFrame;
 typedef JS::Handle<ScriptSourceObject*> HandleScriptSource;
 
 typedef JS::MutableHandle<Shape*>      MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>     MutableHandleAtom;
 typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject;
 typedef JS::MutableHandle<PlainObject*> MutableHandlePlainObject;
+typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
 
 typedef JS::Rooted<NativeObject*>      RootedNativeObject;
 typedef JS::Rooted<Shape*>             RootedShape;
 typedef JS::Rooted<ObjectGroup*>       RootedObjectGroup;
 typedef JS::Rooted<JSAtom*>            RootedAtom;
 typedef JS::Rooted<JSLinearString*>    RootedLinearString;
 typedef JS::Rooted<PropertyName*>      RootedPropertyName;
 typedef JS::Rooted<ArrayObject*>       RootedArrayObject;
 typedef JS::Rooted<GlobalObject*>      RootedGlobalObject;
 typedef JS::Rooted<PlainObject*>       RootedPlainObject;
+typedef JS::Rooted<SavedFrame*>        RootedSavedFrame;
 typedef JS::Rooted<ScriptSourceObject*> RootedScriptSource;
 
 } /* namespace js */
 
 #endif /* gc_Rooting_h */
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -48,17 +48,17 @@ DoCallback(JS::CallbackTracer* trc, T* t
 {
     CheckTracedThing(trc, *thingp);
     JS::AutoTracingName ctx(trc, name);
     trc->dispatchToOnEdge(thingp);
     return *thingp;
 }
 #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(name, type, _) \
     template type* DoCallback<type*>(JS::CallbackTracer*, type**, const char*);
-FOR_EACH_GC_LAYOUT(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS);
+JS_FOR_EACH_TRACEKIND(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS);
 #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
 
 template <typename S>
 struct DoCallbackFunctor : public IdentityDefaultAdaptor<S> {
     template <typename T> S operator()(T* t, JS::CallbackTracer* trc, const char* name) {
         return js::gc::RewrapValueOrId<S, T*>::wrap(DoCallback(trc, &t, name));
     }
 };
@@ -188,17 +188,17 @@ struct TraceChildrenFunctor {
     }
 };
 
 void
 js::TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind)
 {
     MOZ_ASSERT(thing);
     TraceChildrenFunctor f;
-    CallTyped(f, kind, trc, thing);
+    DispatchTraceKindTyped(f, kind, trc, thing);
 }
 
 JS_PUBLIC_API(void)
 JS_TraceRuntime(JSTracer* trc)
 {
     AssertHeapIsIdle(trc->runtime());
     TraceRuntime(trc);
 }
@@ -326,33 +326,33 @@ struct ObjectGroupCycleCollectorTracer :
 
     JS::CallbackTracer* innerTracer;
     Vector<ObjectGroup*, 4, SystemAllocPolicy> seen, worklist;
 };
 
 void
 ObjectGroupCycleCollectorTracer::onChild(const JS::GCCellPtr& thing)
 {
-    if (thing.isObject() || thing.isScript()) {
+    if (thing.is<JSObject>() || thing.is<JSScript>()) {
         // Invoke the inner cycle collector callback on this child. It will not
         // recurse back into TraceChildren.
         innerTracer->onChild(thing);
         return;
     }
 
-    if (thing.isObjectGroup()) {
+    if (thing.is<ObjectGroup>()) {
         // If this group is required to be in an ObjectGroup chain, trace it
         // via the provided worklist rather than continuing to recurse.
-        ObjectGroup* group = static_cast<ObjectGroup*>(thing.asCell());
-        if (group->maybeUnboxedLayout()) {
+        ObjectGroup& group = thing.as<ObjectGroup>();
+        if (group.maybeUnboxedLayout()) {
             for (size_t i = 0; i < seen.length(); i++) {
-                if (seen[i] == group)
+                if (seen[i] == &group)
                     return;
             }
-            if (seen.append(group) && worklist.append(group)) {
+            if (seen.append(&group) && worklist.append(&group)) {
                 return;
             } else {
                 // If append fails, keep tracing normally. The worst that will
                 // happen is we end up overrecursing.
             }
         }
     }
 
--- a/js/src/gdb/lib-for-tests/catcher.py
+++ b/js/src/gdb/lib-for-tests/catcher.py
@@ -10,13 +10,13 @@
 # simple as possible!
 
 import os
 import sys
 import traceback
 try:
     # testlibdir is set on the GDB command line, via:
     # --eval-command python testlibdir=...
-    execfile(os.path.join(testlibdir, 'prologue.py'))
+    exec(open(os.path.join(testlibdir, 'prologue.py')).read())
 except Exception as err:
     sys.stderr.write('Error running GDB prologue:\n')
     traceback.print_exc()
     sys.exit(1)
--- a/js/src/gdb/lib-for-tests/prologue.py
+++ b/js/src/gdb/lib-for-tests/prologue.py
@@ -18,19 +18,19 @@ def run_fragment(fragment, function='bre
         assert bp.hit_count == 1
     finally:
         bp.delete()
     gdb.execute('frame 1')
 
 # Assert that |actual| is equal to |expected|; if not, complain in a helpful way.
 def assert_eq(actual, expected):
     if actual != expected:
-        raise AssertionError, """Unexpected result:
+        raise AssertionError("""Unexpected result:
 expected: %r
-actual:   %r""" % (expected, actual)
+actual:   %r""" % (expected, actual))
 
 # Assert that |value|'s pretty-printed form is |form|. If |value| is a
 # string, then evaluate it with gdb.parse_and_eval to produce a value.
 def assert_pretty(value, form):
     if isinstance(value, str):
         value = gdb.parse_and_eval(value)
     assert_eq(str(value), form)
 
@@ -39,37 +39,37 @@ def assert_pretty(value, form):
 def assert_subprinter_registered(printer, subprinter):
     # Match a line containing |printer| followed by a colon, and then a
     # series of more-indented lines containing |subprinter|.
 
     names = { 'printer': re.escape(printer), 'subprinter': re.escape(subprinter) }
     pat = r'^( +)%(printer)s *\n(\1 +.*\n)*\1 +%(subprinter)s *\n' % names
     output = gdb.execute('info pretty-printer', to_string=True)
     if not re.search(pat, output, re.MULTILINE):
-        raise AssertionError, ("assert_subprinter_registered failed to find pretty-printer:\n"
-                               "  %s:%s\n"
-                               "'info pretty-printer' says:\n"
-                               "%s" % (printer, subprinter, output))
+        raise AssertionError("assert_subprinter_registered failed to find pretty-printer:\n"
+                             "  %s:%s\n"
+                             "'info pretty-printer' says:\n"
+                             "%s" % (printer, subprinter, output))
 
 # Request full stack traces for Python errors.
 gdb.execute('set python print-stack full')
 
 # Tell GDB not to ask the user about the things we tell it to do.
 gdb.execute('set confirm off', False)
 
 # Some print settings that make testing easier.
 gdb.execute('set print static-members off')
 gdb.execute('set print address off')
 gdb.execute('set print pretty off')
 gdb.execute('set width 0')
 
 try:
     # testscript is set on the GDB command line, via:
     # --eval-command python testscript=...
-    execfile(testscript)
+    exec(open(testscript).read())
 except AssertionError as err:
     sys.stderr.write('\nAssertion traceback:\n')
     (t, v, tb) = sys.exc_info()
     traceback.print_tb(tb)
     sys.stderr.write('\nTest assertion failed:\n')
     sys.stderr.write(str(err))
     sys.exit(1)
 
--- a/js/src/gdb/moz.build
+++ b/js/src/gdb/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 GeckoProgram('gdb-tests', linkage=None)
 
 UNIFIED_SOURCES += [
     'gdb-tests.cpp',
     'tests/test-asmjs.cpp',
+    'tests/test-GCCellPtr.cpp',
     'tests/test-Interpreter.cpp',
     'tests/test-jsid.cpp',
     'tests/test-JSObject.cpp',
     'tests/test-JSString.cpp',
     'tests/test-JSSymbol.cpp',
     'tests/test-jsval.cpp',
     'tests/test-prettyprinters.cpp',
     'tests/test-Root.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/gdb/mozilla/GCCellPtr.py
@@ -0,0 +1,49 @@
+# Pretty-printers for GCCellPtr values.
+
+import gdb
+import mozilla.prettyprinters
+
+from mozilla.prettyprinters import pretty_printer
+
+# Forget any printers from previous loads of this module.
+mozilla.prettyprinters.clear_module_printers(__name__)
+
+# Cache information about the JS::TraceKind type for this objfile.
+class GCCellPtrTypeCache(object):
+    def __init__(self, cache):
+        self.TraceKind_t = gdb.lookup_type('JS::TraceKind')
+
+        # Build a mapping from TraceKind enum values to the types they denote.
+        e = gdb.types.make_enum_dict(self.TraceKind_t)
+        kind_to_type = {}
+        def kind(k, t):
+            kind_to_type[e['JS::TraceKind::' + k]] = gdb.lookup_type(t)
+        kind('Object',      'JSObject')
+        kind('String',      'JSString')
+        kind('Symbol',      'JS::Symbol')
+        kind('Script',      'JSScript')
+        kind('Shape',       'js::Shape')
+        kind('ObjectGroup', 'js::ObjectGroup')
+        kind('BaseShape',   'js::BaseShape')
+        kind('JitCode',     'js::jit::JitCode')
+        kind('LazyScript',  'js::LazyScript')
+        self.kind_to_type = kind_to_type
+
+        self.Null = e['JS::TraceKind::Null']
+        self.mask = gdb.parse_and_eval('JS::OutOfLineTraceKindMask')
+
+@pretty_printer('JS::GCCellPtr')
+class GCCellPtr(object):
+    def __init__(self, value, cache):
+        self.value = value
+        if not cache.mod_GCCellPtr:
+            cache.mod_GCCellPtr = GCCellPtrTypeCache(cache)
+        self.cache = cache
+
+    def to_string(self):
+        ptr = self.value['ptr']
+        kind = ptr & self.cache.mod_GCCellPtr.mask
+        if kind == self.cache.mod_GCCellPtr.Null:
+            return "JS::GCCellPtr(nullptr)"
+        tipe = self.cache.mod_GCCellPtr.kind_to_type[int(kind)]
+        return "JS::GCCellPtr(({}*) {})".format(tipe, ptr.cast(self.cache.void_ptr_t))
--- a/js/src/gdb/mozilla/JSSymbol.py
+++ b/js/src/gdb/mozilla/JSSymbol.py
@@ -13,17 +13,17 @@ UniqueSymbol = 0xffffffff
 
 @ptr_pretty_printer("JS::Symbol")
 class JSSymbolPtr(mozilla.prettyprinters.Pointer):
     def __init__(self, value, cache):
         super(JSSymbolPtr, self).__init__(value, cache)
         self.value = value
 
     def to_string(self):
-        code = int(self.value['code_'])
+        code = int(self.value['code_']) & 0xffffffff
         desc = str(self.value['description_'])
         if code == InSymbolRegistry:
             return "Symbol.for({})".format(desc)
         elif code == UniqueSymbol:
             return "Symbol({})".format(desc)
         else:
             # Well-known symbol. Strip off the quotes added by the JSString *
             # pretty-printer.
--- a/js/src/gdb/mozilla/Root.py
+++ b/js/src/gdb/mozilla/Root.py
@@ -13,16 +13,20 @@ mozilla.prettyprinters.clear_module_prin
 class Common(object):
     # The name of the template member holding the referent.
     member = 'ptr'
 
     # If True, this is a handle type, and should be dereferenced. If False,
     # the template member holds the referent directly.
     handle = False
 
+    # If True, we should strip typedefs from our referent type. (Rooted<T>
+    # uses template magic that gives the referent a noisy type.)
+    strip_typedefs = False
+
     # Initialize a pretty-printer for |value|, using |cache|.
     #
     # If given, |content_printer| is a pretty-printer constructor to use for
     # this handle/root/etc.'s referent. Usually, we can just omit this argument
     # and let GDB choose a pretty-printer for the referent given its type, but
     # when the referent is a typedef of an integral type (say, |jsid| in a
     # non-|DEBUG| build), the GNU toolchain (at least) loses the typedef name,
     # and all we know about the referent is its fundamental integer type ---
@@ -37,28 +41,30 @@ class Common(object):
     def __init__(self, value, cache, content_printer=None):
         self.value = value
         self.cache = cache
         self.content_printer = content_printer
     def to_string(self):
         ptr = self.value[self.member]
         if self.handle:
             ptr = ptr.dereference()
+        if self.strip_typedefs:
+            ptr = ptr.cast(ptr.type.strip_typedefs())
         if self.content_printer:
             return self.content_printer(ptr, self.cache).to_string()
         else:
             # As of 2012-11, GDB suppresses printing pointers in replacement
             # values; see http://sourceware.org/ml/gdb/2012-11/msg00055.html
             # That means that simply returning the 'ptr' member won't work.
             # Instead, just invoke GDB's formatter ourselves.
             return str(ptr)
 
 @template_pretty_printer("JS::Rooted")
 class Rooted(Common):
-    pass
+    strip_typedefs = True
 
 @template_pretty_printer("JS::Handle")
 class Handle(Common):
     handle = True
 
 @template_pretty_printer("JS::MutableHandle")
 class MutableHandle(Common):
     handle = True
--- a/js/src/gdb/mozilla/asmjs.py
+++ b/js/src/gdb/mozilla/asmjs.py
@@ -20,17 +20,17 @@ def on_stop(event):
             sigaction_buffers[process] = buf
 
         # See if AsmJSFaultHandler is installed as the SIGSEGV signal action.
         sigaction_fn = gdb.parse_and_eval('__sigaction')
         sigaction_fn(SIGSEGV, 0, buf)
         AsmJSFaultHandler = gdb.parse_and_eval("AsmJSFaultHandler")
         if buf['__sigaction_handler']['sa_handler'] == AsmJSFaultHandler:
             # Advise the user that magic is happening.
-            print "js/src/gdb/mozilla/asmjs.py: Allowing AsmJSFaultHandler to run."
+            print("js/src/gdb/mozilla/asmjs.py: Allowing AsmJSFaultHandler to run.")
 
             # If AsmJSFaultHandler doesn't handle this segfault, it will unhook
             # itself and re-raise.
             gdb.execute("continue")
 
 def on_exited(event):
     if event.inferior in sigaction_buffers:
         del sigaction_buffers[event.inferior]
--- a/js/src/gdb/mozilla/autoload.py
+++ b/js/src/gdb/mozilla/autoload.py
@@ -3,23 +3,24 @@
 print("Loading JavaScript value pretty-printers; see js/src/gdb/README.")
 print("If they cause trouble, type: disable pretty-printer .* SpiderMonkey")
 
 import gdb.printing
 import mozilla.prettyprinters
 
 # Import the pretty-printer modules. As a side effect, loading these
 # modules registers their printers with mozilla.prettyprinters.
+import mozilla.GCCellPtr
 import mozilla.Interpreter
-import mozilla.jsid
 import mozilla.JSObject
 import mozilla.JSString
 import mozilla.JSSymbol
+import mozilla.Root
+import mozilla.jsid
 import mozilla.jsval
-import mozilla.Root
 
 # The user may have personal pretty-printers. Get those, too, if they exist.
 try:
     import my_mozilla_printers
 except ImportError:
     pass
 
 # Register our pretty-printers with |objfile|.
--- a/js/src/gdb/mozilla/prettyprinters.py
+++ b/js/src/gdb/mozilla/prettyprinters.py
@@ -174,20 +174,21 @@ class TypeCache(object):
         self.void_ptr_t = self.void_t.pointer()
         try:
             self.JSString_ptr_t = gdb.lookup_type('JSString').pointer()
             self.JSSymbol_ptr_t = gdb.lookup_type('JS::Symbol').pointer()
             self.JSObject_ptr_t = gdb.lookup_type('JSObject').pointer()
         except gdb.error:
             raise NotSpiderMonkeyObjfileError
 
-        self.mod_JSString = None
+        self.mod_GCCellPtr = None
+        self.mod_Interpreter = None
         self.mod_JSObject = None
+        self.mod_JSString = None
         self.mod_jsval = None
-        self.mod_Interpreter = None
 
 # Yield a series of all the types that |t| implements, by following typedefs
 # and iterating over base classes. Specifically:
 # - |t| itself is the first value yielded.
 # - If we yield a typedef, we later yield its definition.
 # - If we yield a type with base classes, we later yield those base classes.
 # - If we yield a type with some base classes that are typedefs,
 #   we yield all the type's base classes before following the typedefs.
--- a/js/src/gdb/run-tests.py
+++ b/js/src/gdb/run-tests.py
@@ -156,17 +156,17 @@ class Test(TaskPool.Task):
         return [OPTIONS.gdb_executable,
                 '-nw',          # Don't create a window (unnecessary?)
                 '-nx',          # Don't read .gdbinit.
                 '--ex', 'add-auto-load-safe-path %s' % (OPTIONS.builddir,),
                 '--ex', 'set env LD_LIBRARY_PATH %s' % os.path.join(OPTIONS.objdir, 'js', 'src'),
                 '--ex', 'file %s' % (os.path.join(OPTIONS.builddir, 'gdb-tests'),),
                 '--eval-command', 'python testlibdir=%r' % (testlibdir,),
                 '--eval-command', 'python testscript=%r' % (self.test_path,),
-                '--eval-command', 'python execfile(%r)' % os.path.join(testlibdir, 'catcher.py')]
+                '--eval-command', 'python exec(open(%r).read())' % os.path.join(testlibdir, 'catcher.py')]
 
     def start(self, pipe, deadline):
         super(Test, self).start(pipe, deadline)
         if OPTIONS.show_cmd:
             self.summary.interleave_output(lambda: self.show_cmd(sys.stdout))
 
     def onStdout(self, text):
         self.stdout += text
new file mode 100644
--- /dev/null
+++ b/js/src/gdb/tests/test-GCCellPtr.cpp
@@ -0,0 +1,23 @@
+#include "gdb-tests.h"
+#include "jsapi.h"
+
+#include "js/HeapAPI.h"
+
+FRAGMENT(GCCellPtr, simple) {
+  JS::GCCellPtr nulll(nullptr);
+
+  JS::Rooted<JSObject*> glob(cx, JS::CurrentGlobalOrNull(cx));
+  JS::Rooted<JSString*> empty(cx, JS_NewStringCopyN(cx, nullptr, 0));
+  JS::Rooted<Symbol*> unique(cx, NewSymbol(cx, nullptr));
+
+  JS::GCCellPtr object(glob.get());
+  JS::GCCellPtr string(empty.get());
+  JS::GCCellPtr symbol(unique.get());
+
+  breakpoint();
+
+  (void) nulll;
+  (void) object;
+  (void) string;
+  (void) symbol;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/gdb/tests/test-GCCellPtr.py
@@ -0,0 +1,10 @@
+# Tests for GCCellPtr pretty-printing
+
+assert_subprinter_registered('SpiderMonkey', 'JS::GCCellPtr')
+
+run_fragment('GCCellPtr.simple')
+
+assert_pretty('nulll', 'JS::GCCellPtr(nullptr)')
+assert_pretty('object', 'JS::GCCellPtr((JSObject*) )')
+assert_pretty('string', 'JS::GCCellPtr((JSString*) )')
+assert_pretty('symbol', 'JS::GCCellPtr((JS::Symbol*) )')
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug-1136806.js
@@ -0,0 +1,7 @@
+// |jit-test| allow-unhandlable-oom; allow-oom
+
+if (typeof oomAfterAllocations == "function") {
+  Debugger()
+  oomAfterAllocations(6)
+  Debugger()
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1186271.js
@@ -0,0 +1,18 @@
+function f(x) {
+    return Math.imul(1, x >>> 0) / 9 | 0;
+}
+function g(x) {
+    return 1 * (x >>> 0) / 9 | 0;
+}
+function h(x) {
+    return (x >>> 0) / 9 | 0;
+}
+
+assertEq(0, f(4294967296));
+assertEq(-238609294, f(2147483648));
+
+assertEq(0, g(4294967296));
+assertEq(238609294, g(2147483648));
+
+assertEq(0, h(4294967296));
+assertEq(238609294, h(2147483648));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/caching-and-ccws.js
@@ -0,0 +1,35 @@
+// Test that the SavedFrame caching doesn't get messed up in the presence of
+// cross-compartment calls.
+
+const funcSource = "function call(f) { return f(); }";
+
+const g1 = newGlobal();
+const g2 = newGlobal();
+
+g1.eval(funcSource);
+g2.eval(funcSource);
+eval(funcSource);
+
+function doSaveStack() {
+  return saveStack();
+}
+
+const captureStacksAcrossCompartmens = () =>
+  [this, g1, g2].map(g => g.call(doSaveStack));
+
+(function f0() {
+  const stacks = [];
+
+  for (var i = 0; i < 2; i++)
+    stacks.push(...captureStacksAcrossCompartmens());
+
+  const [s1, s2, s3, s4, s5, s6] = stacks;
+
+  assertEq(s1 != s2, true);
+  assertEq(s2 != s3, true);
+  assertEq(s3 != s1, true);
+
+  assertEq(s1, s4);
+  assertEq(s2, s5);
+  assertEq(s3, s6);
+}());
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/caching-and-frame-count.js
@@ -0,0 +1,38 @@
+// Test that the SavedFrame caching doesn't mess up counts. Specifically, that
+// if we capture only the first n frames of a stack, we don't cache that stack
+// and return it for when someone else captures another stack and asks for more
+// frames than that last time.
+
+function stackLength(stack) {
+  return stack === null
+    ? 0
+    : 1 + stackLength(stack.parent);
+}
+
+(function f0() {
+  (function f1() {
+    (function f2() {
+      (function f3() {
+        (function f4() {
+          (function f5() {
+            (function f6() {
+              (function f7() {
+                (function f8() {
+                  (function f9() {
+                    const s1 = saveStack(3);
+                    const s2 = saveStack(5);
+                    const s3 = saveStack(0 /* no limit */);
+
+                    assertEq(stackLength(s1), 3);
+                    assertEq(stackLength(s2), 5);
+                    assertEq(stackLength(s3), 11);
+                  }());
+                }());
+              }());
+            }());
+          }());
+        }());
+      }());
+    }());
+  }());
+}());
--- a/js/src/jit-test/tests/saved-stacks/gc-frame-cache.js
+++ b/js/src/jit-test/tests/saved-stacks/gc-frame-cache.js
@@ -1,76 +1,87 @@
 // Test that SavedFrame instances get removed from the SavedStacks frames cache
 // after a GC.
 
 const FUZZ_FACTOR = 3;
 
-function assertAboutEq(actual, expected) {
-  if (Math.abs(actual - expected) > FUZZ_FACTOR)
-    throw new Error("Assertion failed: expected about " + expected + ", got " + actual +
-                    ". FUZZ_FACTOR = " + FUZZ_FACTOR);
+function isAboutEq(actual, expected) {
+  return Math.abs(actual - expected) <= FUZZ_FACTOR;
 }
 
 var stacks = [];
 
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
-stacks.push(saveStack());
+(function () {
+  // Use an IIFE here so that we don't keep these saved stacks alive in the
+  // frame cache when we test that they all go away at the end of the test.
+
+  var startCount = getSavedFrameCount();
+  print("startCount = " + startCount);
 
-assertAboutEq(getSavedFrameCount(), 50);
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+  stacks.push(saveStack());
+
+  gc();
+
+  var endCount = getSavedFrameCount();
+  print("endCount = " + endCount);
+
+  assertEq(isAboutEq(endCount - startCount, 50), true);
+}());
 
 while (stacks.length) {
   stacks.pop();
 }
 gc();
 
 stacks = null;
 gc();
 
-assertAboutEq(getSavedFrameCount(), 0);
+assertEq(isAboutEq(getSavedFrameCount(), 0), true);
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1696,16 +1696,19 @@ CopyFromRematerializedFrame(JSContext* c
     frame->thisValue() = rematFrame->thisValue();
 
     for (unsigned i = 0; i < frame->numActualArgs(); i++)
         frame->argv()[i] = rematFrame->argv()[i];
 
     for (size_t i = 0; i < frame->script()->nfixed(); i++)
         *frame->valueSlot(i) = rematFrame->locals()[i];
 
+    if (rematFrame->hasCachedSavedFrame())
+        frame->setHasCachedSavedFrame();
+
     JitSpew(JitSpew_BaselineBailouts,
             "  Copied from rematerialized frame at (%p,%u)",
             fp, inlineDepth);
 
     // Propagate the debuggee frame flag. For the case where the Debugger did
     // not rematerialize an Ion frame, the baseline frame has its debuggee
     // flag set iff its script is considered a debuggee. See the debuggee case
     // in InitFromBailout.
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -683,18 +683,20 @@ RecompileBaselineScriptForDebugMode(JSCo
 #define PATCHABLE_ICSTUB_KIND_LIST(_)           \
     _(Call_Scripted)                            \
     _(Call_AnyScripted)                         \
     _(Call_Native)                              \
     _(Call_ClassHook)                           \
     _(Call_ScriptedApplyArray)                  \
     _(Call_ScriptedApplyArguments)              \
     _(Call_ScriptedFunCall)                     \
-    _(GetElem_NativePrototypeCallNative)        \
-    _(GetElem_NativePrototypeCallScripted)      \
+    _(GetElem_NativePrototypeCallNativeName)    \
+    _(GetElem_NativePrototypeCallNativeSymbol)  \
+    _(GetElem_NativePrototypeCallScriptedName)  \
+    _(GetElem_NativePrototypeCallScriptedSymbol) \
     _(GetProp_CallScripted)                     \
     _(GetProp_CallNative)                       \
     _(GetProp_CallDOMProxyNative)               \
     _(GetProp_CallDOMProxyWithGenerationNative) \
     _(GetProp_DOMProxyShadowed)                 \
     _(GetProp_Generic)                          \
     _(SetProp_CallScripted)                     \
     _(SetProp_CallNative)
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -29,17 +29,17 @@ struct BaselineDebugModeOSRInfo;
 // Like js::InterpreterFrame, every BaselineFrame is either a global frame
 // or a function frame. Both global and function frames can optionally
 // be "eval frames". The callee token for eval function frames is the
 // enclosing function. BaselineFrame::evalScript_ stores the eval script
 // itself.
 class BaselineFrame
 {
   public:
-    enum Flags {
+    enum Flags : uint32_t {
         // The frame has a valid return value. See also InterpreterFrame::HAS_RVAL.
         HAS_RVAL         = 1 << 0,
 
         // A call object has been pushed on the scope chain.
         HAS_CALL_OBJ     = 1 << 2,
 
         // Frame has an arguments object, argsObj_.
         HAS_ARGS_OBJ     = 1 << 4,
@@ -72,17 +72,22 @@ class BaselineFrame
         // current pc.
         //
         // This flag should never be set when we're executing JIT code.
         HAS_OVERRIDE_PC = 1 << 11,
 
         // If set, we're handling an exception for this frame. This is set for
         // debug mode OSR sanity checking when it handles corner cases which
         // only arise during exception handling.
-        HANDLING_EXCEPTION = 1 << 12
+        HANDLING_EXCEPTION = 1 << 12,
+
+        // If set, this frame has been on the stack when
+        // |js::SavedStacks::saveCurrentStack| was called, and so there is a
+        // |js::SavedFrame| object cached for this frame.
+        HAS_CACHED_SAVED_FRAME = 1 << 13
     };
 
   protected: // Silence Clang warning about unused private fields.
     // We need to split the Value into 2 fields of 32 bits, otherwise the C++
     // compiler may add some padding between the fields.
 
     union {
         struct {
@@ -318,16 +323,23 @@ class BaselineFrame
     }
     void setIsHandlingException() {
         flags_ |= HANDLING_EXCEPTION;
     }
     void unsetIsHandlingException() {
         flags_ &= ~HANDLING_EXCEPTION;
     }
 
+    bool hasCachedSavedFrame() const {
+        return flags_ & HAS_CACHED_SAVED_FRAME;
+    }
+    void setHasCachedSavedFrame() {
+        flags_ |= HAS_CACHED_SAVED_FRAME;
+    }
+
     JSScript* evalScript() const {
         MOZ_ASSERT(isEvalFrame());
         return evalScript_;
     }
 
     bool overRecursed() const {
         return flags_ & OVER_RECURSED;
     }
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -2648,17 +2648,17 @@ CheckDOMProxyExpandoDoesNotShadow(JSCont
     masm.bind(&domProxyOk);
     masm.popValue(tempVal);
 }
 
 // Look up a property's shape on an object, being careful never to do any effectful
 // operations.  This procedure not yielding a shape should not be taken as a lack of
 // existence of the property on the object.
 static bool
-EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandlePropertyName name,
+EffectlesslyLookupProperty(JSContext* cx, HandleObject obj, HandleId id,
                            MutableHandleObject holder, MutableHandleShape shape,
                            bool* checkDOMProxy=nullptr,
                            DOMProxyShadowsResult* shadowsResult=nullptr,
                            bool* domProxyHasGeneration=nullptr)
 {
     shape.set(nullptr);
     holder.set(nullptr);
 
@@ -2672,34 +2672,33 @@ EffectlesslyLookupProperty(JSContext* cx
     if (checkDOMProxy && IsCacheableDOMProxy(obj)) {
         MOZ_ASSERT(domProxyHasGeneration);
         MOZ_ASSERT(shadowsResult);
 
         *checkDOMProxy = true;
         if (obj->hasUncacheableProto())
             return true;
 
-        RootedId id(cx, NameToId(name));
         *shadowsResult = GetDOMProxyShadowsCheck()(cx, obj, id);
         if (*shadowsResult == ShadowCheckFailed)
             return false;
 
         if (DOMProxyIsShadowing(*shadowsResult)) {
             holder.set(obj);
             return true;
         }
 
         *domProxyHasGeneration = (*shadowsResult == DoesntShadowUnique);
 
         checkObj = GetDOMProxyProto(obj);
         if (!checkObj)
             return true;
     }
 
-    if (LookupPropertyPure(cx, checkObj, NameToId(name), holder.address(), shape.address()))
+    if (LookupPropertyPure(cx, checkObj, id, holder.address(), shape.address()))
         return true;
 
     holder.set(nullptr);
     shape.set(nullptr);
     return true;
 }
 
 static bool
@@ -2965,129 +2964,163 @@ LookupNoSuchMethodHandler(JSContext* cx,
     return OnUnknownMethod(cx, obj, id, result);
 }
 
 typedef bool (*LookupNoSuchMethodHandlerFn)(JSContext*, HandleObject, HandleValue,
                                             MutableHandleValue);
 static const VMFunction LookupNoSuchMethodHandlerInfo =
     FunctionInfo<LookupNoSuchMethodHandlerFn>(LookupNoSuchMethodHandler);
 
+
+template <class T>
 static bool
 GetElemNativeStubExists(ICGetElem_Fallback* stub, HandleObject obj, HandleObject holder,
-                        HandlePropertyName propName, bool needsAtomize)
+                        Handle<T> key, bool needsAtomize)
 {
     bool indirect = (obj.get() != holder.get());
     MOZ_ASSERT_IF(indirect, holder->isNative());
 
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
-        if (iter->kind() != ICStub::GetElem_NativeSlot &&
-            iter->kind() != ICStub::GetElem_NativePrototypeSlot &&
-            iter->kind() != ICStub::GetElem_NativePrototypeCallNative &&
-            iter->kind() != ICStub::GetElem_NativePrototypeCallScripted)
+        if (iter->kind() != ICStub::GetElem_NativeSlotName &&
+            iter->kind() != ICStub::GetElem_NativeSlotSymbol &&
+            iter->kind() != ICStub::GetElem_NativePrototypeSlotName &&
+            iter->kind() != ICStub::GetElem_NativePrototypeSlotSymbol &&
+            iter->kind() != ICStub::GetElem_NativePrototypeCallNativeName &&
+            iter->kind() != ICStub::GetElem_NativePrototypeCallNativeSymbol &&
+            iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedName &&
+            iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedSymbol)
         {
             continue;
         }
 
-        if (indirect && (iter->kind() != ICStub::GetElem_NativePrototypeSlot &&
-                         iter->kind() != ICStub::GetElem_NativePrototypeCallNative &&
-                         iter->kind() != ICStub::GetElem_NativePrototypeCallScripted))
+        if (indirect && (iter->kind() != ICStub::GetElem_NativePrototypeSlotName &&
+                         iter->kind() != ICStub::GetElem_NativePrototypeSlotSymbol &&
+                         iter->kind() != ICStub::GetElem_NativePrototypeCallNativeName &&
+                         iter->kind() != ICStub::GetElem_NativePrototypeCallNativeSymbol &&
+                         iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedName &&
+                         iter->kind() != ICStub::GetElem_NativePrototypeCallScriptedSymbol))
         {
             continue;
         }
 
-        ICGetElemNativeStub* getElemNativeStub = reinterpret_cast<ICGetElemNativeStub*>(*iter);
-        if (propName != getElemNativeStub->name())
+        if(mozilla::IsSame<T, JS::Symbol*>::value !=
+           static_cast<ICGetElemNativeStub*>(*iter)->isSymbol())
+        {
+            continue;
+        }
+
+        ICGetElemNativeStubImpl<T>* getElemNativeStub =
+            reinterpret_cast<ICGetElemNativeStubImpl<T>*>(*iter);
+        if (key != getElemNativeStub->key())
             continue;
 
         if (ReceiverGuard(obj) != getElemNativeStub->receiverGuard())
             continue;
 
         // If the new stub needs atomization, and the old stub doesn't atomize, then
         // an appropriate stub doesn't exist.
         if (needsAtomize && !getElemNativeStub->needsAtomize())
             continue;
 
         // For prototype gets, check the holder and holder shape.
         if (indirect) {
-            if (iter->isGetElem_NativePrototypeSlot()) {
-                ICGetElem_NativePrototypeSlot* protoStub = iter->toGetElem_NativePrototypeSlot();
+            if (iter->isGetElem_NativePrototypeSlotName() ||
+                iter->isGetElem_NativePrototypeSlotSymbol()) {
+                ICGetElem_NativePrototypeSlot<T>* protoStub =
+                    reinterpret_cast<ICGetElem_NativePrototypeSlot<T>*>(*iter);
 
                 if (holder != protoStub->holder())
                     continue;
 
                 if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape())
                     continue;
             } else {
-                MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNative() ||
-                           iter->isGetElem_NativePrototypeCallScripted());
-
-                ICGetElemNativePrototypeCallStub* protoStub =
-                    reinterpret_cast<ICGetElemNativePrototypeCallStub*>(*iter);
+                MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNativeName() ||
+                           iter->isGetElem_NativePrototypeCallNativeSymbol() ||
+                           iter->isGetElem_NativePrototypeCallScriptedName() ||
+                           iter->isGetElem_NativePrototypeCallScriptedSymbol());
+
+                ICGetElemNativePrototypeCallStub<T>* protoStub =
+                    reinterpret_cast<ICGetElemNativePrototypeCallStub<T>*>(*iter);
 
                 if (holder != protoStub->holder())
                     continue;
 
                 if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape())
                     continue;
             }
         }
 
         return true;
     }
     return false;
 }
 
+template <class T>
 static void
 RemoveExistingGetElemNativeStubs(JSContext* cx, ICGetElem_Fallback* stub, HandleObject obj,
-                                 HandleObject holder, HandlePropertyName propName,
-                                 bool needsAtomize)
+                                 HandleObject holder, Handle<T> key, bool needsAtomize)
 {
     bool indirect = (obj.get() != holder.get());
     MOZ_ASSERT_IF(indirect, holder->isNative());
 
     for (ICStubIterator iter = stub->beginChain(); !iter.atEnd(); iter++) {
         switch (iter->kind()) {
-          case ICStub::GetElem_NativeSlot:
+          case ICStub::GetElem_NativeSlotName:
+          case ICStub::GetElem_NativeSlotSymbol:
             if (indirect)
                 continue;
-          case ICStub::GetElem_NativePrototypeSlot:
-          case ICStub::GetElem_NativePrototypeCallNative:
-          case ICStub::GetElem_NativePrototypeCallScripted:
+          case ICStub::GetElem_NativePrototypeSlotName:
+          case ICStub::GetElem_NativePrototypeSlotSymbol:
+          case ICStub::GetElem_NativePrototypeCallNativeName:
+          case ICStub::GetElem_NativePrototypeCallNativeSymbol:
+          case ICStub::GetElem_NativePrototypeCallScriptedName:
+          case ICStub::GetElem_NativePrototypeCallScriptedSymbol:
             break;
           default:
             continue;
         }
 
-        ICGetElemNativeStub* getElemNativeStub = reinterpret_cast<ICGetElemNativeStub*>(*iter);
-        if (propName != getElemNativeStub->name())
+        if(mozilla::IsSame<T, JS::Symbol*>::value !=
+           static_cast<ICGetElemNativeStub*>(*iter)->isSymbol())
+        {
+            continue;
+        }
+
+        ICGetElemNativeStubImpl<T>* getElemNativeStub =
+            reinterpret_cast<ICGetElemNativeStubImpl<T>*>(*iter);
+        if (key != getElemNativeStub->key())
             continue;
 
         if (ReceiverGuard(obj) != getElemNativeStub->receiverGuard())
             continue;
 
         // For prototype gets, check the holder and holder shape.
         if (indirect) {
-            if (iter->isGetElem_NativePrototypeSlot()) {
-                ICGetElem_NativePrototypeSlot* protoStub = iter->toGetElem_NativePrototypeSlot();
+            if (iter->isGetElem_NativePrototypeSlotName() ||
+                iter->isGetElem_NativePrototypeSlotSymbol()) {
+                ICGetElem_NativePrototypeSlot<T>* protoStub =
+                    reinterpret_cast<ICGetElem_NativePrototypeSlot<T>*>(*iter);
 
                 if (holder != protoStub->holder())
                     continue;
 
                 // If the holder matches, but the holder's lastProperty doesn't match, then
                 // this stub is invalid anyway.  Unlink it.
                 if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape()) {
                     iter.unlink(cx);
                     continue;
                 }
             } else {
-                MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNative() ||
-                           iter->isGetElem_NativePrototypeCallScripted());
-
-                ICGetElemNativePrototypeCallStub* protoStub =
-                    reinterpret_cast<ICGetElemNativePrototypeCallStub*>(*iter);
+                MOZ_ASSERT(iter->isGetElem_NativePrototypeCallNativeName() ||
+                           iter->isGetElem_NativePrototypeCallNativeSymbol() ||
+                           iter->isGetElem_NativePrototypeCallScriptedName() ||
+                           iter->isGetElem_NativePrototypeCallScriptedSymbol());
+                ICGetElemNativePrototypeCallStub<T>* protoStub =
+                    reinterpret_cast<ICGetElemNativePrototypeCallStub<T>*>(*iter);
 
                 if (holder != protoStub->holder())
                     continue;
 
                 // If the holder matches, but the holder's lastProperty doesn't match, then
                 // this stub is invalid anyway.  Unlink it.
                 if (holder->as<NativeObject>().lastProperty() != protoStub->holderShape()) {
                     iter.unlink(cx);
@@ -3128,16 +3161,41 @@ ArgumentsGetElemStubExists(ICGetElem_Fal
         if (!iter->isGetElem_Arguments())
             continue;
         if (iter->toGetElem_Arguments()->which() == which)
             return true;
     }
     return false;
 }
 
+template <class T>
+static T
+getKey(jsid id)
+{
+    MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
+    return false;
+}
+
+template <>
+JS::Symbol* getKey<JS::Symbol*>(jsid id)
+{
+    if (!JSID_IS_SYMBOL(id))
+        return nullptr;
+    return JSID_TO_SYMBOL(id);
+}
+
+template <>
+PropertyName* getKey<PropertyName*>(jsid id)
+{
+    uint32_t dummy;
+    if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
+        return nullptr;
+    return JSID_TO_ATOM(id)->asPropertyName();
+}
+
 static bool
 IsOptimizableElementPropertyName(JSContext* cx, HandleValue key, MutableHandleId idp)
 {
     if (!key.isString())
         return false;
 
     // Convert to interned property name.
     if (!ValueToId<CanGC>(cx, key, idp))
@@ -3145,80 +3203,112 @@ IsOptimizableElementPropertyName(JSConte
 
     uint32_t dummy;
     if (!JSID_IS_ATOM(idp) || JSID_TO_ATOM(idp)->isIndex(&dummy))
         return false;
 
     return true;
 }
 
+template <class T>
+static bool
+checkAtomize(HandleValue key)
+{
+    MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
+    return false;
+}
+
+template <>
+bool checkAtomize<JS::Symbol*>(HandleValue key)
+{
+    return false;
+}
+
+template <>
+bool checkAtomize<PropertyName*>(HandleValue key)
+{
+    return !key.toString()->isAtom();
+}
+
+template <class T>
 static bool
 TryAttachNativeOrUnboxedGetValueElemStub(JSContext* cx, HandleScript script, jsbytecode* pc,
                                          ICGetElem_Fallback* stub, HandleObject obj,
-                                         HandleValue key, bool* attached)
-{
+                                         HandleValue keyVal, bool* attached)
+{
+    MOZ_ASSERT(keyVal.isString() || keyVal.isSymbol());
+
+    // Convert to id.
     RootedId id(cx);
-    if (!IsOptimizableElementPropertyName(cx, key, &id))
-        return true;
-
-    RootedPropertyName propName(cx, JSID_TO_ATOM(id)->asPropertyName());
-    bool needsAtomize = !key.toString()->isAtom();
+    if (!ValueToId<CanGC>(cx, keyVal, &id))
+        return false;
+
+    Rooted<T> key(cx, getKey<T>(id));
+    if (!key)
+        return true;
+    bool needsAtomize = checkAtomize<T>(keyVal);
     bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM);
 
     RootedShape shape(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, propName, &holder, &shape))
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
         return false;
     if (!holder || (holder != obj && !holder->isNative()))
         return true;
 
     // If a suitable stub already exists, nothing else to do.
-    if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize))
+    if (GetElemNativeStubExists<T>(stub, obj, holder, key, needsAtomize))
         return true;
 
     // Remove any existing stubs that may interfere with the new stub being added.
-    RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize);
+    RemoveExistingGetElemNativeStubs<T>(cx, stub, obj, holder, key, needsAtomize);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
 
     if (obj->is<UnboxedPlainObject>() && holder == obj) {
-        const UnboxedLayout::Property* property =
-            obj->as<UnboxedPlainObject>().layout().lookup(propName);
+        const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
+
+        // Once unboxed objects support symbol-keys, we need to change the following accordingly
+        MOZ_ASSERT_IF(!keyVal.isString(), !property);
+
         if (property) {
             if (!cx->runtime()->jitSupportsFloatingPoint)
                 return true;
 
-            ICGetElemNativeCompiler compiler(cx, ICStub::GetElem_UnboxedProperty, isCallElem,
-                                             monitorStub, obj, holder, propName,
-                                             ICGetElemNativeStub::UnboxedProperty, needsAtomize,
-                                             property->offset + UnboxedPlainObject::offsetOfData(),
-                                             property->type);
+            RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
+            ICGetElemNativeCompiler<PropertyName*> compiler(cx, ICStub::GetElem_UnboxedPropertyName,
+                                                            isCallElem, monitorStub, obj, holder,
+                                                            name,
+                                                            ICGetElemNativeStub::UnboxedProperty,
+                                                            needsAtomize, property->offset +
+                                                            UnboxedPlainObject::offsetOfData(),
+                                                            property->type);
             ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
             if (!newStub)
                 return false;
 
             stub->addNewStub(newStub);
             *attached = true;
             return true;
         }
 
-        Shape* shape = obj->as<UnboxedPlainObject>().maybeExpando()->lookup(cx, propName);
+        Shape* shape = obj->as<UnboxedPlainObject>().maybeExpando()->lookup(cx, id);
         if (!shape->hasDefaultGetter() || !shape->hasSlot())
             return true;
 
         bool isFixedSlot;
         uint32_t offset;
         GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
         ICGetElemNativeStub::AccessType acctype =
             isFixedSlot ? ICGetElemNativeStub::FixedSlot
                         : ICGetElemNativeStub::DynamicSlot;
-        ICGetElemNativeCompiler compiler(cx, ICStub::GetElem_NativeSlot, isCallElem,
-                                         monitorStub, obj, holder, propName,
-                                         acctype, needsAtomize, offset);
+        ICGetElemNativeCompiler<T> compiler(cx, getGetElemStubKind<T>(ICStub::GetElem_NativeSlotName),
+                                            isCallElem, monitorStub, obj, holder, key,
+                                            acctype, needsAtomize, offset);
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
@@ -3226,59 +3316,65 @@ TryAttachNativeOrUnboxedGetValueElemStub
     if (!holder->isNative())
         return true;
 
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
         GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
 
-        ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlot
-                                            : ICStub::GetElem_NativePrototypeSlot;
+        ICStub::Kind kind = (obj == holder) ? ICStub::GetElem_NativeSlotName
+                                            : ICStub::GetElem_NativePrototypeSlotName;
+        kind = getGetElemStubKind<T>(kind);
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetElem(Native %s%s slot) stub "
                                     "(obj=%p, holder=%p, holderShape=%p)",
                     (obj == holder) ? "direct" : "prototype",
                     needsAtomize ? " atomizing" : "",
                 obj.get(), holder.get(), holder->as<NativeObject>().lastProperty());
 
-        ICGetElemNativeStub::AccessType acctype = isFixedSlot ? ICGetElemNativeStub::FixedSlot
-                                                              : ICGetElemNativeStub::DynamicSlot;
-        ICGetElemNativeCompiler compiler(cx, kind, isCallElem, monitorStub, obj, holder, propName,
-                                         acctype, needsAtomize, offset);
+        AccType acctype = isFixedSlot ? ICGetElemNativeStub::FixedSlot
+                                      : ICGetElemNativeStub::DynamicSlot;
+        ICGetElemNativeCompiler<T> compiler(cx, kind, isCallElem, monitorStub, obj, holder, key,
+                                            acctype, needsAtomize, offset);
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     return true;
 }
 
+template <class T>
 static bool
 TryAttachNativeGetAccessorElemStub(JSContext* cx, HandleScript script, jsbytecode* pc,
                                    ICGetElem_Fallback* stub, HandleNativeObject obj,
-                                   HandleValue key, bool* attached, bool* isTemporarilyUnoptimizable)
+                                   HandleValue keyVal, bool* attached,
+                                   bool* isTemporarilyUnoptimizable)
 {
     MOZ_ASSERT(!*attached);
+    MOZ_ASSERT(keyVal.isString() || keyVal.isSymbol());
 
     RootedId id(cx);
-    if (!IsOptimizableElementPropertyName(cx, key, &id))
-        return true;
-
-    RootedPropertyName propName(cx, JSID_TO_ATOM(id)->asPropertyName());
-    bool needsAtomize = !key.toString()->isAtom();
+    if (!ValueToId<CanGC>(cx, keyVal, &id))
+        return false;
+
+    Rooted<T> key(cx, getKey<T>(id));
+    if (!key)
+        return true;
+    bool needsAtomize = checkAtomize<T>(keyVal);
     bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM);
 
     RootedShape shape(cx);
     RootedObject baseHolder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, propName, &baseHolder, &shape))
+    if (!EffectlesslyLookupProperty(cx, obj, id, &baseHolder, &shape))
         return false;
     if (!baseHolder || baseHolder->isNative())
         return true;
 
     HandleNativeObject holder = baseHolder.as<NativeObject>();
 
     bool getterIsScripted = false;
     if (IsCacheableGetPropCall(cx, obj, baseHolder, shape, &getterIsScripted,
@@ -3293,25 +3389,26 @@ TryAttachNativeGetAccessorElemStub(JSCon
             return true;
 #endif
 
         // For now, we do not handle own property getters
         if (obj == holder)
             return true;
 
         // If a suitable stub already exists, nothing else to do.
-        if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize))
+        if (GetElemNativeStubExists<T>(stub, obj, holder, key, needsAtomize))
             return true;
 
         // Remove any existing stubs that may interfere with the new stub being added.
-        RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize);
+        RemoveExistingGetElemNativeStubs<T>(cx, stub, obj, holder, key, needsAtomize);
 
         ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-        ICStub::Kind kind = getterIsScripted ? ICStub::GetElem_NativePrototypeCallScripted
-                                             : ICStub::GetElem_NativePrototypeCallNative;
+        ICStub::Kind kind = getterIsScripted ? ICStub::GetElem_NativePrototypeCallScriptedName
+                                             : ICStub::GetElem_NativePrototypeCallNativeName;
+        kind = getGetElemStubKind<T>(kind);
 
         if (getterIsScripted) {
             JitSpew(JitSpew_BaselineIC,
                     "  Generating GetElem(Native %s%s call scripted %s:%" PRIuSIZE ") stub "
                     "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
                         (obj == holder) ? "direct" : "prototype",
                         needsAtomize ? " atomizing" : "",
                         getter->nonLazyScript()->filename(), getter->nonLazyScript()->lineno(),
@@ -3320,20 +3417,19 @@ TryAttachNativeGetAccessorElemStub(JSCon
             JitSpew(JitSpew_BaselineIC,
                     "  Generating GetElem(Native %s%s call native) stub "
                     "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
                         (obj == holder) ? "direct" : "prototype",
                         needsAtomize ? " atomizing" : "",
                         obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty());
         }
 
-        ICGetElemNativeStub::AccessType acctype = getterIsScripted
-                                                           ? ICGetElemNativeStub::ScriptedGetter
-                                                           : ICGetElemNativeStub::NativeGetter;
-        ICGetElemNativeCompiler compiler(cx, kind, monitorStub, obj, holder, propName, acctype,
+        AccType acctype = getterIsScripted ? ICGetElemNativeStub::ScriptedGetter
+                                           : ICGetElemNativeStub::NativeGetter;
+        ICGetElemNativeCompiler<T> compiler(cx, kind, monitorStub, obj, holder, key, acctype,
                                          needsAtomize, getter, script->pcToOffset(pc), isCallElem);
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
@@ -3471,22 +3567,30 @@ TryAttachGetElemStub(JSContext* cx, JSSc
             return false;
 
         stub->addNewStub(denseStub);
         *attached = true;
         return true;
     }
 
     // Check for NativeObject[id] and UnboxedPlainObject[id] shape-optimizable accesses.
-    if ((obj->isNative() || obj->is<UnboxedPlainObject>()) && rhs.isString()) {
+    if (obj->isNative() || obj->is<UnboxedPlainObject>()) {
         RootedScript rootedScript(cx, script);
-        if (!TryAttachNativeOrUnboxedGetValueElemStub(cx, rootedScript, pc, stub,
-                                                      obj, rhs, attached))
-        {
-            return false;
+        if (rhs.isString()) {
+            if (!TryAttachNativeOrUnboxedGetValueElemStub<PropertyName*>(cx, rootedScript, pc, stub,
+                                                                         obj, rhs, attached))
+            {
+                return false;
+            }
+        } else if (rhs.isSymbol()) {
+            if (!TryAttachNativeOrUnboxedGetValueElemStub<JS::Symbol*>(cx, rootedScript, pc, stub,
+                                                                       obj, rhs, attached))
+            {
+                return false;
+            }
         }
         if (*attached)
             return true;
         script = rootedScript;
     }
 
     // Check for UnboxedArray[int] accesses.
     if (obj->is<UnboxedArrayObject>() && rhs.isInt32() && rhs.toInt32() >= 0) {
@@ -3582,25 +3686,38 @@ DoGetElemFallback(JSContext* cx, Baselin
         // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
         // But for now we just bail.
         stub->noteUnoptimizableAccess();
         attached = true;
     }
 
     // Try to attach an optimized getter stub.
     bool isTemporarilyUnoptimizable = false;
-    if (!attached && lhs.isObject() && lhs.toObject().isNative() && rhs.isString()){
-        RootedScript rootedScript(cx, frame->script());
-        RootedNativeObject obj(cx, &lhs.toObject().as<NativeObject>());
-        if (!TryAttachNativeGetAccessorElemStub(cx, rootedScript, pc, stub, obj, rhs, &attached,
-                                                &isTemporarilyUnoptimizable))
-        {
-            return false;
-        }
-        script = rootedScript;
+    if (!attached && lhs.isObject() && lhs.toObject().isNative()){
+        if (rhs.isString()) {
+            RootedScript rootedScript(cx, frame->script());
+            RootedNativeObject obj(cx, &lhs.toObject().as<NativeObject>());
+            if (!TryAttachNativeGetAccessorElemStub<PropertyName*>(cx, rootedScript, pc, stub,
+                                                                   obj, rhs, &attached,
+                                                                   &isTemporarilyUnoptimizable))
+            {
+                return false;
+            }
+            script = rootedScript;
+        } else if (rhs.isSymbol()) {
+            RootedScript rootedScript(cx, frame->script());
+            RootedNativeObject obj(cx, &lhs.toObject().as<NativeObject>());
+            if (!TryAttachNativeGetAccessorElemStub<JS::Symbol*>(cx, rootedScript, pc, stub,
+                                                                 obj, rhs, &attached,
+                                                                 &isTemporarilyUnoptimizable))
+            {
+                return false;
+            }
+            script = rootedScript;
+        }
     }
 
     if (!isOptimizedArgs) {
         if (!GetElementOperation(cx, op, &lhsCopy, rhs, res))
             return false;
         TypeScript::Monitor(cx, frame->script(), pc, res);
     }
 
@@ -3675,45 +3792,47 @@ DoAtomizeString(JSContext* cx, HandleStr
 
     result.set(StringValue(JSID_TO_ATOM(id)));
     return true;
 }
 
 typedef bool (*DoAtomizeStringFn)(JSContext*, HandleString, MutableHandleValue);
 static const VMFunction DoAtomizeStringInfo = FunctionInfo<DoAtomizeStringFn>(DoAtomizeString);
 
-bool
-ICGetElemNativeCompiler::emitCallNative(MacroAssembler& masm, Register objReg)
+template <class T>
+bool
+ICGetElemNativeCompiler<T>::emitCallNative(MacroAssembler& masm, Register objReg)
 {
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
     regs.takeUnchecked(objReg);
     regs.takeUnchecked(ICTailCallReg);
 
     enterStubFrame(masm, regs.getAny());
 
     // Push object.
     masm.push(objReg);
 
     // Push native callee.
-    masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()), objReg);
+    masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()), objReg);
     masm.push(objReg);
 
     regs.add(objReg);
 
     // Call helper.
     if (!callVM(DoCallNativeGetterInfo, masm))
         return false;
 
     leaveStubFrame(masm);
 
     return true;
 }
 
-bool
-ICGetElemNativeCompiler::emitCallScripted(MacroAssembler& masm, Register objReg)
+template <class T>
+bool
+ICGetElemNativeCompiler<T>::emitCallScripted(MacroAssembler& masm, Register objReg)
 {
     AllocatableGeneralRegisterSet regs(availableGeneralRegs(0));
     regs.takeUnchecked(objReg);
     regs.takeUnchecked(ICTailCallReg);
 
     // Enter stub frame.
     enterStubFrame(masm, regs.getAny());
 
@@ -3727,17 +3846,17 @@ ICGetElemNativeCompiler::emitCallScripte
         masm.tagValue(JSVAL_TYPE_OBJECT, objReg, val);
         masm.Push(val);
         regs.add(val);
     }
 
     regs.add(objReg);
 
     Register callee = regs.takeAny();
-    masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()), callee);
+    masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()), callee);
 
     // Push argc, callee, and descriptor.
     {
         Register callScratch = regs.takeAny();
         EmitCreateStubFrameDescriptor(masm, callScratch);
         masm.Push(Imm32(0));  // ActualArgc is 0
         masm.Push(callee);
         masm.Push(callScratch);
@@ -3769,41 +3888,44 @@ ICGetElemNativeCompiler::emitCallScripte
     masm.bind(&noUnderflow);
     masm.callJit(code);
 
     leaveStubFrame(masm, true);
 
     return true;
 }
 
-bool
-ICGetElemNativeCompiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure;
-    Label failurePopR1;
-    bool popR1 = false;
-
-    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+template <class T>
+bool
+ICGetElemNativeCompiler<T>::emitCheckKey(MacroAssembler& masm, Label& failure)
+{
+    MOZ_ASSERT_UNREACHABLE("Key has to be PropertyName or Symbol");
+    return false;
+}
+
+template <>
+bool
+ICGetElemNativeCompiler<JS::Symbol*>::emitCheckKey(MacroAssembler& masm, Label& failure)
+{
+    MOZ_ASSERT(!needsAtomize_);
+    masm.branchTestSymbol(Assembler::NotEqual, R1, &failure);
+    Address symbolAddr(ICStubReg, ICGetElemNativeStubImpl<JS::Symbol*>::offsetOfKey());
+    Register symExtract = masm.extractObject(R1, ExtractTemp1);
+    masm.branchPtr(Assembler::NotEqual, symbolAddr, symExtract, &failure);
+    return true;
+}
+
+template <>
+bool
+ICGetElemNativeCompiler<PropertyName*>::emitCheckKey(MacroAssembler& masm, Label& failure)
+{
     masm.branchTestString(Assembler::NotEqual, R1, &failure);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
-    Register scratchReg = regs.takeAny();
-
-    // Unbox object.
-    Register objReg = masm.extractObject(R0, ExtractTemp0);
-
-    // Check object shape/group.
-    GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratchReg,
-                        ICGetElemNativeStub::offsetOfReceiverGuard(), &failure);
-
     // Check key identity.  Don't automatically fail if this fails, since the incoming
     // key maybe a non-interned string.  Switch to a slowpath vm-call based check.
-    Address nameAddr(ICStubReg, ICGetElemNativeStub::offsetOfName());
+    Address nameAddr(ICStubReg, ICGetElemNativeStubImpl<PropertyName*>::offsetOfKey());
     Register strExtract = masm.extractString(R1, ExtractTemp1);
 
     // If needsAtomize_ is true, and the string is not already an atom, then atomize the
     // string before proceeding.
     if (needsAtomize_) {
         Label skipAtomize;
 
         // If string is already an atom, skip the atomize.
@@ -3833,23 +3955,50 @@ ICGetElemNativeCompiler::generateStubCod
 
         // Extract string from R1 again.
         DebugOnly<Register> strExtract2 = masm.extractString(R1, ExtractTemp1);
         MOZ_ASSERT(Register(strExtract2) == strExtract);
 
         masm.bind(&skipAtomize);
     }
 
+    // Key has been atomized if necessary.  Do identity check on string pointer.
+    masm.branchPtr(Assembler::NotEqual, nameAddr, strExtract, &failure);
+    return true;
+}
+
+template <class T>
+bool
+ICGetElemNativeCompiler<T>::generateStubCode(MacroAssembler& masm)
+{
+    MOZ_ASSERT(engine_ == Engine::Baseline);
+
+    Label failure;
+    Label failurePopR1;
+    bool popR1 = false;
+
+    masm.branchTestObject(Assembler::NotEqual, R0, &failure);
+
+    AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
+    Register scratchReg = regs.takeAny();
+
+    // Unbox object.
+    Register objReg = masm.extractObject(R0, ExtractTemp0);
+
+    // Check object shape/group.
+    GuardReceiverObject(masm, ReceiverGuard(obj_), objReg, scratchReg,
+                        ICGetElemNativeStub::offsetOfReceiverGuard(), &failure);
+
     // Since this stub sometimes enters a stub frame, we manually set this to true (lie).
 #ifdef DEBUG
     entersStubFrame_ = true;
 #endif
 
-    // Key has been atomized if necessary.  Do identity check on string pointer.
-    masm.branchPtr(Assembler::NotEqual, nameAddr, strExtract, &failure);
+    if (!emitCheckKey(masm, failure))
+        return false;
 
     Register holderReg;
     if (obj_ == holder_) {
         holderReg = objReg;
 
         if (obj_->is<UnboxedPlainObject>() && acctype_ != ICGetElemNativeStub::UnboxedProperty) {
             // The property will be loaded off the unboxed expando.
             masm.push(R1.scratchReg());
@@ -3862,41 +4011,43 @@ ICGetElemNativeCompiler::generateStubCod
         if (regs.empty()) {
             masm.push(R1.scratchReg());
             popR1 = true;
             holderReg = R1.scratchReg();
         } else {
             holderReg = regs.takeAny();
         }
 
-        if (kind == ICStub::GetElem_NativePrototypeCallNative ||
-            kind == ICStub::GetElem_NativePrototypeCallScripted)
+        if (kind == ICStub::GetElem_NativePrototypeCallNativeName ||
+            kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
+            kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
+            kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol)
         {
             masm.loadPtr(Address(ICStubReg,
-                                 ICGetElemNativePrototypeCallStub::offsetOfHolder()),
+                                 ICGetElemNativePrototypeCallStub<T>::offsetOfHolder()),
                          holderReg);
             masm.loadPtr(Address(ICStubReg,
-                                 ICGetElemNativePrototypeCallStub::offsetOfHolderShape()),
+                                 ICGetElemNativePrototypeCallStub<T>::offsetOfHolderShape()),
                          scratchReg);
         } else {
             masm.loadPtr(Address(ICStubReg,
-                                 ICGetElem_NativePrototypeSlot::offsetOfHolder()),
+                                 ICGetElem_NativePrototypeSlot<T>::offsetOfHolder()),
                          holderReg);
             masm.loadPtr(Address(ICStubReg,
-                                 ICGetElem_NativePrototypeSlot::offsetOfHolderShape()),
+                                 ICGetElem_NativePrototypeSlot<T>::offsetOfHolderShape()),
                          scratchReg);
         }
         masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratchReg,
                                 popR1 ? &failurePopR1 : &failure);
     }
 
     if (acctype_ == ICGetElemNativeStub::DynamicSlot ||
         acctype_ == ICGetElemNativeStub::FixedSlot)
     {
-        masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub::offsetOfOffset()),
+        masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()),
                     scratchReg);
 
         // Load from object.
         if (acctype_ == ICGetElemNativeStub::DynamicSlot)
             masm.addPtr(Address(holderReg, NativeObject::offsetOfSlots()), scratchReg);
         else
             masm.addPtr(holderReg, scratchReg);
 
@@ -3951,42 +4102,44 @@ ICGetElemNativeCompiler::generateStubCod
         }
 #else
         masm.loadValue(valAddr, R0);
         if (popR1)
             masm.addToStackPtr(ImmWord(sizeof(size_t)));
 #endif
 
     } else if (acctype_ == ICGetElemNativeStub::UnboxedProperty) {
-        masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub::offsetOfOffset()),
+        masm.load32(Address(ICStubReg, ICGetElemNativeSlotStub<T>::offsetOfOffset()),
                     scratchReg);
         masm.loadUnboxedProperty(BaseIndex(objReg, scratchReg, TimesOne), unboxedType_,
                                  TypedOrValueRegister(R0));
         if (popR1)
             masm.addToStackPtr(ImmWord(sizeof(size_t)));
     } else {
         MOZ_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter ||
                    acctype_ == ICGetElemNativeStub::ScriptedGetter);
-        MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNative ||
-                   kind == ICStub::GetElem_NativePrototypeCallScripted);
+        MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNativeName ||
+                   kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
+                   kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
+                   kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol);
 
         if (acctype_ == ICGetElemNativeStub::NativeGetter) {
             // If calling a native getter, there is no chance of failure now.
 
             // GetElem key (R1) is no longer needed.
             if (popR1)
                 masm.addToStackPtr(ImmWord(sizeof(size_t)));
 
             emitCallNative(masm, objReg);
 
         } else {
             MOZ_ASSERT(acctype_ == ICGetElemNativeStub::ScriptedGetter);
 
             // Load function in scratchReg and ensure that it has a jit script.
-            masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub::offsetOfGetter()),
+            masm.loadPtr(Address(ICStubReg, ICGetElemNativeGetterStub<T>::offsetOfGetter()),
                          scratchReg);
             masm.branchIfFunctionHasNoScript(scratchReg, popR1 ? &failurePopR1 : &failure);
             masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
             masm.loadBaselineOrIonRaw(scratchReg, scratchReg, popR1 ? &failurePopR1 : &failure);
 
             // At this point, we are guaranteed to successfully complete.
             if (popR1)
                 masm.addToStackPtr(Imm32(sizeof(size_t)));
@@ -5480,17 +5633,17 @@ TryAttachNativeInStub(JSContext* cx, Han
 
     RootedId id(cx);
     if (!IsOptimizableElementPropertyName(cx, key, &id))
         return true;
 
     RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
     RootedShape shape(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
         return false;
 
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         ICStub::Kind kind = (obj == holder) ? ICStub::In_Native
                                             : ICStub::In_NativePrototype;
         JitSpew(JitSpew_BaselineIC, "  Generating In(Native %s) stub",
                     (obj == holder) ? "direct" : "prototype");
         ICInNativeCompiler compiler(cx, kind, obj, holder, name);
@@ -6600,17 +6753,18 @@ TryAttachNativeGetValuePropStub(JSContex
 
     if (obj->isNative() && oldShape != obj->as<NativeObject>().lastProperty()) {
         // No point attaching anything, since we know the shape guard will fail
         return true;
     }
 
     RootedShape shape(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
+    RootedId id(cx, NameToId(name));
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
         return false;
 
     bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
@@ -6658,17 +6812,18 @@ TryAttachNativeGetAccessorPropStub(JSCon
 
     RootedObject obj(cx, &val.toObject());
 
     bool isDOMProxy;
     bool domProxyHasGeneration;
     DOMProxyShadowsResult domProxyShadowsResult;
     RootedShape shape(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isDOMProxy,
+    RootedId id(cx, NameToId(name));
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy,
                                     &domProxyShadowsResult, &domProxyHasGeneration))
     {
         return false;
     }
 
     bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
 
     ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
@@ -6735,17 +6890,17 @@ TryAttachNativeGetAccessorPropStub(JSCon
         outerClass = obj->getClass();
         DebugOnly<JSObject*> outer = obj.get();
         obj = GetInnerObject(obj);
         MOZ_ASSERT(script->global().isNative());
         if (obj != &script->global())
             return true;
         // ICGetProp_CallNative*::Compiler::generateStubCode depends on this.
         MOZ_ASSERT(&((GetProxyDataLayout(outer)->values->privateSlot).toObject()) == obj);
-        if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isDOMProxy,
+        if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape, &isDOMProxy,
                                         &domProxyShadowsResult, &domProxyHasGeneration))
         {
             return false;
         }
         cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted,
                                                isTemporarilyUnoptimizable, isDOMProxy);
     }
 
@@ -8219,17 +8374,17 @@ TryAttachSetValuePropStub(JSContext* cx,
 {
     MOZ_ASSERT(!*attached);
 
     if (obj->watched())
         return true;
 
     RootedShape shape(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
         return false;
     if (obj != holder)
         return true;
 
     if (!obj->isNative()) {
         if (obj->is<UnboxedPlainObject>()) {
             UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
             if (expando) {
@@ -8328,17 +8483,17 @@ TryAttachSetAccessorPropStub(JSContext* 
     MOZ_ASSERT(!*attached);
     MOZ_ASSERT(!*isTemporarilyUnoptimizable);
 
     if (obj->watched())
         return true;
 
     RootedShape shape(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
+    if (!EffectlesslyLookupProperty(cx, obj, id, &holder, &shape))
         return false;
 
     bool isScripted = false;
     bool cacheableCall = IsCacheableSetPropCall(cx, obj, holder, shape,
                                                 &isScripted, isTemporarilyUnoptimizable);
 
     // Try handling scripted setters.
     if (cacheableCall && isScripted) {
@@ -12005,86 +12160,93 @@ ICTypeUpdate_SingleObject::ICTypeUpdate_
 
 ICTypeUpdate_ObjectGroup::ICTypeUpdate_ObjectGroup(JitCode* stubCode, ObjectGroup* group)
   : ICStub(TypeUpdate_ObjectGroup, stubCode),
     group_(group)
 { }
 
 ICGetElemNativeStub::ICGetElemNativeStub(ICStub::Kind kind, JitCode* stubCode,
                                          ICStub* firstMonitorStub,
-                                         ReceiverGuard guard, PropertyName* name,
-                                         AccessType acctype, bool needsAtomize)
+                                         ReceiverGuard guard, AccessType acctype,
+                                         bool needsAtomize, bool isSymbol)
   : ICMonitoredStub(kind, stubCode, firstMonitorStub),
-    receiverGuard_(guard),
-    name_(name)
+    receiverGuard_(guard)
 {
     extra_ = (static_cast<uint16_t>(acctype) << ACCESSTYPE_SHIFT) |
-             (static_cast<uint16_t>(needsAtomize) << NEEDS_ATOMIZE_SHIFT);
+             (static_cast<uint16_t>(needsAtomize) << NEEDS_ATOMIZE_SHIFT) |
+             (static_cast<uint16_t>(isSymbol) << ISSYMBOL_SHIFT);
 }
 
 ICGetElemNativeStub::~ICGetElemNativeStub()
 { }
 
-ICGetElemNativeGetterStub::ICGetElemNativeGetterStub(
-                        ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                        ReceiverGuard guard, PropertyName* name, AccessType acctype,
-                        bool needsAtomize, JSFunction* getter, uint32_t pcOffset)
-  : ICGetElemNativeStub(kind, stubCode, firstMonitorStub, guard, name, acctype, needsAtomize),
+template <class T>
+ICGetElemNativeGetterStub<T>::ICGetElemNativeGetterStub(
+                           ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
+                           ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize,
+                           JSFunction* getter, uint32_t pcOffset)
+  : ICGetElemNativeStubImpl<T>(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize),
     getter_(getter),
     pcOffset_(pcOffset)
 {
-    MOZ_ASSERT(kind == GetElem_NativePrototypeCallNative ||
-               kind == GetElem_NativePrototypeCallScripted);
-    MOZ_ASSERT(acctype == NativeGetter || acctype == ScriptedGetter);
-}
-
-ICGetElem_NativePrototypeSlot::ICGetElem_NativePrototypeSlot(
-                            JitCode* stubCode, ICStub* firstMonitorStub,
-                            ReceiverGuard guard, PropertyName* name,
-                            AccessType acctype, bool needsAtomize, uint32_t offset,
-                            JSObject* holder, Shape* holderShape)
-  : ICGetElemNativeSlotStub(ICStub::GetElem_NativePrototypeSlot, stubCode, firstMonitorStub, guard,
-                            name, acctype, needsAtomize, offset),
+    MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNativeName ||
+               kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
+               kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
+               kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol);
+    MOZ_ASSERT(acctype == ICGetElemNativeStub::NativeGetter ||
+               acctype == ICGetElemNativeStub::ScriptedGetter);
+}
+
+template <class T>
+ICGetElem_NativePrototypeSlot<T>::ICGetElem_NativePrototypeSlot(
+                               JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
+                               const T* key, AccType acctype, bool needsAtomize, uint32_t offset,
+                               JSObject* holder, Shape* holderShape)
+  : ICGetElemNativeSlotStub<T>(getGetElemStubKind<T>(ICStub::GetElem_NativePrototypeSlotName),
+                               stubCode, firstMonitorStub, guard, key, acctype, needsAtomize, offset),
     holder_(holder),
     holderShape_(holderShape)
 { }
 
-ICGetElemNativePrototypeCallStub::ICGetElemNativePrototypeCallStub(
-                                ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                                ReceiverGuard guard, PropertyName* name,
-                                AccessType acctype, bool needsAtomize, JSFunction* getter,
-                                uint32_t pcOffset, JSObject* holder, Shape* holderShape)
-  : ICGetElemNativeGetterStub(kind, stubCode, firstMonitorStub, guard, name, acctype, needsAtomize,
-                              getter, pcOffset),
+template <class T>
+ICGetElemNativePrototypeCallStub<T>::ICGetElemNativePrototypeCallStub(
+                                  ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
+                                  ReceiverGuard guard, const T* key, AccType acctype,
+                                  bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
+                                  JSObject* holder, Shape* holderShape)
+  : ICGetElemNativeGetterStub<T>(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize,
+                                 getter, pcOffset),
     holder_(holder),
     holderShape_(holderShape)
 {}
 
-/* static */ ICGetElem_NativePrototypeCallNative*
-ICGetElem_NativePrototypeCallNative::Clone(JSContext* cx,
-                                           ICStubSpace* space,
-                                           ICStub* firstMonitorStub,
-                                           ICGetElem_NativePrototypeCallNative& other)
-{
-    return New<ICGetElem_NativePrototypeCallNative>(cx, space, other.jitCode(), firstMonitorStub,
-                                                    other.receiverGuard(), other.name(), other.accessType(),
-                                                    other.needsAtomize(), other.getter(), other.pcOffset_,
-                                                    other.holder(), other.holderShape());
-}
-
-/* static */ ICGetElem_NativePrototypeCallScripted*
-ICGetElem_NativePrototypeCallScripted::Clone(JSContext* cx,
-                                             ICStubSpace* space,
-                                             ICStub* firstMonitorStub,
-                                             ICGetElem_NativePrototypeCallScripted& other)
-{
-    return New<ICGetElem_NativePrototypeCallScripted>(cx, space, other.jitCode(), firstMonitorStub,
-                                                      other.receiverGuard(), other.name(),
-                                                      other.accessType(), other.needsAtomize(), other.getter(),
-                                                      other.pcOffset_, other.holder(), other.holderShape());
+template <class T>
+/* static */ ICGetElem_NativePrototypeCallNative<T>*
+ICGetElem_NativePrototypeCallNative<T>::Clone(JSContext* cx,
+                                              ICStubSpace* space,
+                                              ICStub* firstMonitorStub,
+                                              ICGetElem_NativePrototypeCallNative<T>& other)
+{
+    return ICStub::New<ICGetElem_NativePrototypeCallNative<T>>(cx, space, other.jitCode(),
+                firstMonitorStub, other.receiverGuard(), other.key().unsafeGet(), other.accessType(),
+                other.needsAtomize(), other.getter(), other.pcOffset_, other.holder(),
+                other.holderShape());
+}
+
+template <class T>
+/* static */ ICGetElem_NativePrototypeCallScripted<T>*
+ICGetElem_NativePrototypeCallScripted<T>::Clone(JSContext* cx,
+                                                ICStubSpace* space,
+                                                ICStub* firstMonitorStub,
+                                                ICGetElem_NativePrototypeCallScripted<T>& other)
+{
+    return ICStub::New<ICGetElem_NativePrototypeCallScripted<T>>(cx, space, other.jitCode(),
+                firstMonitorStub, other.receiverGuard(), other.key().unsafeGet(), other.accessType(),
+                other.needsAtomize(), other.getter(), other.pcOffset_, other.holder(),
+                other.holderShape());
 }
 
 ICGetElem_Dense::ICGetElem_Dense(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape)
     : ICMonitoredStub(GetElem_Dense, stubCode, firstMonitorStub),
       shape_(shape)
 { }
 
 /* static */ ICGetElem_Dense*
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1527,134 +1527,187 @@ class ICGetElem_Fallback : public ICMoni
 
 class ICGetElemNativeStub : public ICMonitoredStub
 {
   public:
     enum AccessType { FixedSlot = 0, DynamicSlot, UnboxedProperty, NativeGetter, ScriptedGetter };
 
   protected:
     HeapReceiverGuard receiverGuard_;
-    HeapPtrPropertyName name_;
 
     static const unsigned NEEDS_ATOMIZE_SHIFT = 0;
     static const uint16_t NEEDS_ATOMIZE_MASK = 0x1;
 
     static const unsigned ACCESSTYPE_SHIFT = 1;
     static const uint16_t ACCESSTYPE_MASK = 0x3;
 
+    static const unsigned ISSYMBOL_SHIFT = 3;
+    static const uint16_t ISSYMBOL_MASK = 0x1;
+
     ICGetElemNativeStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                        ReceiverGuard guard, PropertyName* name, AccessType acctype,
-                        bool needsAtomize);
+                        ReceiverGuard guard, AccessType acctype, bool needsAtomize, bool isSymbol);
 
     ~ICGetElemNativeStub();
 
   public:
     HeapReceiverGuard& receiverGuard() {
         return receiverGuard_;
     }
     static size_t offsetOfReceiverGuard() {
         return offsetof(ICGetElemNativeStub, receiverGuard_);
     }
 
-    HeapPtrPropertyName& name() {
-        return name_;
-    }
-    static size_t offsetOfName() {
-        return offsetof(ICGetElemNativeStub, name_);
-    }
-
     AccessType accessType() const {
         return static_cast<AccessType>((extra_ >> ACCESSTYPE_SHIFT) & ACCESSTYPE_MASK);
     }
 
     bool needsAtomize() const {
         return (extra_ >> NEEDS_ATOMIZE_SHIFT) & NEEDS_ATOMIZE_MASK;
     }
+
+    bool isSymbol() const {
+        return (extra_ >> ISSYMBOL_SHIFT) & ISSYMBOL_MASK;
+    }
 };
 
-class ICGetElemNativeSlotStub : public ICGetElemNativeStub
+template <class T>
+class ICGetElemNativeStubImpl : public ICGetElemNativeStub
+{
+  protected:
+    HeapPtr<T> key_;
+
+    ICGetElemNativeStubImpl(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
+                            ReceiverGuard guard, const T* key, AccessType acctype, bool needsAtomize)
+      : ICGetElemNativeStub(kind, stubCode, firstMonitorStub, guard, acctype, needsAtomize,
+                            mozilla::IsSame<T, JS::Symbol*>::value),
+        key_(*key)
+    {}
+
+  public:
+    HeapPtr<T>& key() {
+        return key_;
+    }
+    static size_t offsetOfKey() {
+        return offsetof(ICGetElemNativeStubImpl, key_);
+    }
+};
+
+typedef ICGetElemNativeStub::AccessType AccType;
+
+template <class T>
+class ICGetElemNativeSlotStub : public ICGetElemNativeStubImpl<T>
 {
   protected:
     uint32_t offset_;
 
     ICGetElemNativeSlotStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                            ReceiverGuard guard, PropertyName* name,
-                            AccessType acctype, bool needsAtomize, uint32_t offset)
-      : ICGetElemNativeStub(kind, stubCode, firstMonitorStub, guard, name, acctype, needsAtomize),
+                            ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize,
+                            uint32_t offset)
+      : ICGetElemNativeStubImpl<T>(kind, stubCode, firstMonitorStub, guard, key, acctype, needsAtomize),
         offset_(offset)
     {
-        MOZ_ASSERT(kind == GetElem_NativeSlot || kind == GetElem_NativePrototypeSlot ||
-                   kind == GetElem_UnboxedProperty);
-        MOZ_ASSERT(acctype == FixedSlot || acctype == DynamicSlot || acctype == UnboxedProperty);
+        MOZ_ASSERT(kind == ICStub::GetElem_NativeSlotName ||
+                   kind == ICStub::GetElem_NativeSlotSymbol ||
+                   kind == ICStub::GetElem_NativePrototypeSlotName ||
+                   kind == ICStub::GetElem_NativePrototypeSlotSymbol ||
+                   kind == ICStub::GetElem_UnboxedPropertyName);
+        MOZ_ASSERT(acctype == ICGetElemNativeStub::FixedSlot ||
+                   acctype == ICGetElemNativeStub::DynamicSlot ||
+                   acctype == ICGetElemNativeStub::UnboxedProperty);
     }
 
   public:
     uint32_t offset() const {
         return offset_;
     }
 
     static size_t offsetOfOffset() {
         return offsetof(ICGetElemNativeSlotStub, offset_);
     }
 };
 
-class ICGetElemNativeGetterStub : public ICGetElemNativeStub
+template <class T>
+class ICGetElemNativeGetterStub : public ICGetElemNativeStubImpl<T>
 {
   protected:
     HeapPtrFunction getter_;
     uint32_t pcOffset_;
 
     ICGetElemNativeGetterStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                              ReceiverGuard guard, PropertyName* name, AccessType acctype,
-                              bool needsAtomize, JSFunction* getter, uint32_t pcOffset);
+                              ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize,
+                              JSFunction* getter, uint32_t pcOffset);
 
   public:
     HeapPtrFunction& getter() {
         return getter_;
     }
     static size_t offsetOfGetter() {
         return offsetof(ICGetElemNativeGetterStub, getter_);
     }
 
     static size_t offsetOfPCOffset() {
         return offsetof(ICGetElemNativeGetterStub, pcOffset_);
     }
 };
 
-class ICGetElem_NativeSlot : public ICGetElemNativeSlotStub
+template <class T>
+ICStub::Kind
+getGetElemStubKind(ICStub::Kind kind)
+{
+    MOZ_ASSERT(kind == ICStub::GetElem_NativeSlotName ||
+               kind == ICStub::GetElem_NativePrototypeSlotName ||
+               kind == ICStub::GetElem_NativePrototypeCallNativeName ||
+               kind == ICStub::GetElem_NativePrototypeCallScriptedName);
+    return static_cast<ICStub::Kind>(kind + mozilla::IsSame<T, JS::Symbol*>::value);
+}
+
+template <class T>
+class ICGetElem_NativeSlot : public ICGetElemNativeSlotStub<T>
 {
     friend class ICStubSpace;
-    ICGetElem_NativeSlot(JitCode* stubCode, ICStub* firstMonitorStub,
-                         ReceiverGuard guard, PropertyName* name,
-                         AccessType acctype, bool needsAtomize, uint32_t offset)
-      : ICGetElemNativeSlotStub(ICStub::GetElem_NativeSlot, stubCode, firstMonitorStub, guard,
-                                name, acctype, needsAtomize, offset)
+    ICGetElem_NativeSlot(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
+                         const T* key, AccType acctype, bool needsAtomize, uint32_t offset)
+      : ICGetElemNativeSlotStub<T>(getGetElemStubKind<T>(ICStub::GetElem_NativeSlotName),
+                                   stubCode, firstMonitorStub, guard,
+                                   key, acctype, needsAtomize, offset)
     {}
 };
 
-class ICGetElem_UnboxedProperty : public ICGetElemNativeSlotStub
+class ICGetElem_NativeSlotName :
+      public ICGetElem_NativeSlot<PropertyName*>
+{};
+class ICGetElem_NativeSlotSymbol :
+      public ICGetElem_NativeSlot<JS::Symbol*>
+{};
+
+template <class T>
+class ICGetElem_UnboxedProperty : public ICGetElemNativeSlotStub<T>
 {
     friend class ICStubSpace;
     ICGetElem_UnboxedProperty(JitCode* stubCode, ICStub* firstMonitorStub,
-                              ReceiverGuard guard, PropertyName* name,
-                              AccessType acctype, bool needsAtomize, uint32_t offset)
-      : ICGetElemNativeSlotStub(ICStub::GetElem_UnboxedProperty, stubCode, firstMonitorStub, guard,
-                                name, acctype, needsAtomize, offset)
-    {}
+                              ReceiverGuard guard, const T* key, AccType acctype,
+                              bool needsAtomize, uint32_t offset)
+      : ICGetElemNativeSlotStub<T>(ICStub::GetElem_UnboxedPropertyName, stubCode, firstMonitorStub,
+                                   guard, key, acctype, needsAtomize, offset)
+     {}
 };
 
-class ICGetElem_NativePrototypeSlot : public ICGetElemNativeSlotStub
+class ICGetElem_UnboxedPropertyName :
+      public ICGetElem_UnboxedProperty<PropertyName*>
+{};
+
+template <class T>
+class ICGetElem_NativePrototypeSlot : public ICGetElemNativeSlotStub<T>
 {
     friend class ICStubSpace;
     HeapPtrObject holder_;
     HeapPtrShape holderShape_;
 
-    ICGetElem_NativePrototypeSlot(JitCode* stubCode, ICStub* firstMonitorStub,
-                                  ReceiverGuard guard, PropertyName* name,
-                                  AccessType acctype, bool needsAtomize, uint32_t offset,
+    ICGetElem_NativePrototypeSlot(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
+                                  const T* key, AccType acctype, bool needsAtomize, uint32_t offset,
                                   JSObject* holder, Shape* holderShape);
 
   public:
     HeapPtrObject& holder() {
         return holder_;
     }
     static size_t offsetOfHolder() {
         return offsetof(ICGetElem_NativePrototypeSlot, holder_);
@@ -1663,28 +1716,35 @@ class ICGetElem_NativePrototypeSlot : pu
     HeapPtrShape& holderShape() {
         return holderShape_;
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICGetElem_NativePrototypeSlot, holderShape_);
     }
 };
 
-class ICGetElemNativePrototypeCallStub : public ICGetElemNativeGetterStub
+class ICGetElem_NativePrototypeSlotName :
+      public ICGetElem_NativePrototypeSlot<PropertyName*>
+{};
+class ICGetElem_NativePrototypeSlotSymbol :
+      public ICGetElem_NativePrototypeSlot<JS::Symbol*>
+{};
+
+template <class T>
+class ICGetElemNativePrototypeCallStub : public ICGetElemNativeGetterStub<T>
 {
     friend class ICStubSpace;
     HeapPtrObject holder_;
     HeapPtrShape holderShape_;
 
   protected:
     ICGetElemNativePrototypeCallStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
-                                     ReceiverGuard guard, PropertyName* name,
-                                     AccessType acctype, bool needsAtomize, JSFunction* getter,
-                                     uint32_t pcOffset, JSObject* holder,
-                                     Shape* holderShape);
+                                     ReceiverGuard guard, const T* key, AccType acctype,
+                                     bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
+                                     JSObject* holder, Shape* holderShape);
 
   public:
     HeapPtrObject& holder() {
         return holder_;
     }
     static size_t offsetOfHolder() {
         return offsetof(ICGetElemNativePrototypeCallStub, holder_);
     }
@@ -1692,166 +1752,188 @@ class ICGetElemNativePrototypeCallStub :
     HeapPtrShape& holderShape() {
         return holderShape_;
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICGetElemNativePrototypeCallStub, holderShape_);
     }
 };
 
-class ICGetElem_NativePrototypeCallNative : public ICGetElemNativePrototypeCallStub
+template <class T>
+class ICGetElem_NativePrototypeCallNative : public ICGetElemNativePrototypeCallStub<T>
 {
     friend class ICStubSpace;
 
     ICGetElem_NativePrototypeCallNative(JitCode* stubCode, ICStub* firstMonitorStub,
-                                        ReceiverGuard guard, PropertyName* name,
-                                        AccessType acctype, bool needsAtomize,
-                                        JSFunction* getter, uint32_t pcOffset,
+                                        ReceiverGuard guard, const T* key, AccType acctype,
+                                        bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
                                         JSObject* holder, Shape* holderShape)
-      : ICGetElemNativePrototypeCallStub(GetElem_NativePrototypeCallNative,
-                                         stubCode, firstMonitorStub, guard, name,
-                                         acctype, needsAtomize, getter, pcOffset, holder,
-                                         holderShape)
+      : ICGetElemNativePrototypeCallStub<T>(getGetElemStubKind<T>(
+                                            ICStub::GetElem_NativePrototypeCallNativeName),
+                                            stubCode, firstMonitorStub, guard, key,
+                                            acctype, needsAtomize, getter, pcOffset, holder,
+                                            holderShape)
     {}
 
   public:
-    static ICGetElem_NativePrototypeCallNative* Clone(JSContext* cx, ICStubSpace* space,
-                                                      ICStub* firstMonitorStub,
-                                                      ICGetElem_NativePrototypeCallNative& other);
+    static ICGetElem_NativePrototypeCallNative<T>* Clone(JSContext* cx, ICStubSpace* space,
+                                                         ICStub* firstMonitorStub,
+                                                         ICGetElem_NativePrototypeCallNative<T>& other);
 };
 
-class ICGetElem_NativePrototypeCallScripted : public ICGetElemNativePrototypeCallStub
+class ICGetElem_NativePrototypeCallNativeName :
+      public ICGetElem_NativePrototypeCallNative<PropertyName*>
+{};
+class ICGetElem_NativePrototypeCallNativeSymbol :
+      public ICGetElem_NativePrototypeCallNative<JS::Symbol*>
+{};
+
+template <class T>
+class ICGetElem_NativePrototypeCallScripted : public ICGetElemNativePrototypeCallStub<T>
 {
     friend class ICStubSpace;
 
     ICGetElem_NativePrototypeCallScripted(JitCode* stubCode, ICStub* firstMonitorStub,
-                                          ReceiverGuard guard, PropertyName* name,
-                                          AccessType acctype, bool needsAtomize,
-                                          JSFunction* getter, uint32_t pcOffset,
+                                          ReceiverGuard guard, const T* key, AccType acctype,
+                                          bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
                                           JSObject* holder, Shape* holderShape)
-      : ICGetElemNativePrototypeCallStub(GetElem_NativePrototypeCallScripted,
-                                         stubCode, firstMonitorStub, guard, name,
-                                         acctype, needsAtomize, getter, pcOffset, holder,
-                                         holderShape)
+      : ICGetElemNativePrototypeCallStub<T>(getGetElemStubKind<T>(
+                                            ICStub::GetElem_NativePrototypeCallScriptedName),
+                                            stubCode, firstMonitorStub, guard, key, acctype,
+                                            needsAtomize, getter, pcOffset, holder, holderShape)
     {}
 
   public:
-    static ICGetElem_NativePrototypeCallScripted*
+    static ICGetElem_NativePrototypeCallScripted<T>*
     Clone(JSContext* cx, ICStubSpace* space,
           ICStub* firstMonitorStub,
-          ICGetElem_NativePrototypeCallScripted& other);
+          ICGetElem_NativePrototypeCallScripted<T>& other);
 };
 
+class ICGetElem_NativePrototypeCallScriptedName :
+      public ICGetElem_NativePrototypeCallScripted<PropertyName*>
+{};
+class ICGetElem_NativePrototypeCallScriptedSymbol :
+      public ICGetElem_NativePrototypeCallScripted<JS::Symbol*>
+{};
+
 // Compiler for GetElem_NativeSlot and GetElem_NativePrototypeSlot stubs.
+template <class T>
 class ICGetElemNativeCompiler : public ICStubCompiler
 {
     bool isCallElem_;
     ICStub* firstMonitorStub_;
     HandleObject obj_;
     HandleObject holder_;
-    HandlePropertyName name_;
-    ICGetElemNativeStub::AccessType acctype_;
+    Handle<T> key_;
+    AccType acctype_;
     bool needsAtomize_;
     uint32_t offset_;
     JSValueType unboxedType_;
     HandleFunction getter_;
     uint32_t pcOffset_;
 
+    bool emitCheckKey(MacroAssembler& masm, Label& failure);
     bool emitCallNative(MacroAssembler& masm, Register objReg);
     bool emitCallScripted(MacroAssembler& masm, Register objReg);
     bool generateStubCode(MacroAssembler& masm);
 
   protected:
     virtual int32_t getKey() const {
         MOZ_ASSERT(static_cast<int32_t>(acctype_) <= 7);
         MOZ_ASSERT(static_cast<int32_t>(unboxedType_) <= 8);
         return static_cast<int32_t>(engine_) |
               (static_cast<int32_t>(kind) << 1) |
 #if JS_HAS_NO_SUCH_METHOD
               (static_cast<int32_t>(isCallElem_) << 17) |
 #endif
               (static_cast<int32_t>(needsAtomize_) << 18) |
               (static_cast<int32_t>(acctype_) << 19) |
               (static_cast<int32_t>(unboxedType_) << 22) |
-              (HeapReceiverGuard::keyBits(obj_) << 26);
+              (static_cast<int32_t>(mozilla::IsSame<JS::Symbol*, T>::value) << 26) |
+              (HeapReceiverGuard::keyBits(obj_) << 27);
     }
 
   public:
     ICGetElemNativeCompiler(JSContext* cx, ICStub::Kind kind, bool isCallElem,
                             ICStub* firstMonitorStub, HandleObject obj, HandleObject holder,
-                            HandlePropertyName name, ICGetElemNativeStub::AccessType acctype,
-                            bool needsAtomize, uint32_t offset,
+                            Handle<T> key, AccType acctype, bool needsAtomize, uint32_t offset,
                             JSValueType unboxedType = JSVAL_TYPE_MAGIC)
       : ICStubCompiler(cx, kind, Engine::Baseline),
         isCallElem_(isCallElem),
         firstMonitorStub_(firstMonitorStub),
         obj_(obj),
         holder_(holder),
-        name_(name),
+        key_(key),
         acctype_(acctype),
         needsAtomize_(needsAtomize),
         offset_(offset),
         unboxedType_(unboxedType),
         getter_(nullptr),
         pcOffset_(0)
     {}
 
     ICGetElemNativeCompiler(JSContext* cx, ICStub::Kind kind, ICStub* firstMonitorStub,
-                            HandleObject obj, HandleObject holder, HandlePropertyName name,
-                            ICGetElemNativeStub::AccessType acctype, bool needsAtomize,
-                            HandleFunction getter, uint32_t pcOffset, bool isCallElem)
+                            HandleObject obj, HandleObject holder, Handle<T> key, AccType acctype,
+                            bool needsAtomize, HandleFunction getter, uint32_t pcOffset,
+                            bool isCallElem)
       : ICStubCompiler(cx, kind, Engine::Baseline),
         isCallElem_(false),
         firstMonitorStub_(firstMonitorStub),
         obj_(obj),
         holder_(holder),
-        name_(name),
+        key_(key),
         acctype_(acctype),
         needsAtomize_(needsAtomize),
         offset_(0),
         unboxedType_(JSVAL_TYPE_MAGIC),
         getter_(getter),
         pcOffset_(pcOffset)
     {}
 
     ICStub* getStub(ICStubSpace* space) {
         RootedReceiverGuard guard(cx, ReceiverGuard(obj_));
-        if (kind == ICStub::GetElem_NativeSlot) {
+        if (kind == ICStub::GetElem_NativeSlotName || kind == ICStub::GetElem_NativeSlotSymbol) {
             MOZ_ASSERT(obj_ == holder_);
-            return newStub<ICGetElem_NativeSlot>(
-                    space, getStubCode(), firstMonitorStub_, guard, name_, acctype_, needsAtomize_,
-                    offset_);
+            return newStub<ICGetElem_NativeSlot<T>>(
+                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
+                    needsAtomize_, offset_);
         }
 
-        if (kind == ICStub::GetElem_UnboxedProperty) {
+	if (kind == ICStub::GetElem_UnboxedPropertyName) {
             MOZ_ASSERT(obj_ == holder_);
-            return newStub<ICGetElem_UnboxedProperty>(
-                    space, getStubCode(), firstMonitorStub_, guard, name_, acctype_, needsAtomize_,
-                    offset_);
+            return newStub<ICGetElem_UnboxedProperty<T>>(
+                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
+                    needsAtomize_, offset_);
         }
 
         MOZ_ASSERT(obj_ != holder_);
         RootedShape holderShape(cx, holder_->as<NativeObject>().lastProperty());
-        if (kind == ICStub::GetElem_NativePrototypeSlot) {
-            return newStub<ICGetElem_NativePrototypeSlot>(
-                    space, getStubCode(), firstMonitorStub_, guard, name_, acctype_, needsAtomize_,
-                    offset_, holder_, holderShape);
+        if (kind == ICStub::GetElem_NativePrototypeSlotName ||
+            kind == ICStub::GetElem_NativePrototypeSlotSymbol)
+        {
+            return newStub<ICGetElem_NativePrototypeSlot<T>>(
+                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
+                    needsAtomize_, offset_, holder_, holderShape);
         }
 
-        if (kind == ICStub::GetElem_NativePrototypeCallNative) {
-            return newStub<ICGetElem_NativePrototypeCallNative>(
-                    space, getStubCode(), firstMonitorStub_, guard, name_, acctype_, needsAtomize_,
-                    getter_, pcOffset_, holder_, holderShape);
+        if (kind == ICStub::GetElem_NativePrototypeCallNativeSymbol ||
+            kind == ICStub::GetElem_NativePrototypeCallNativeName) {
+            return newStub<ICGetElem_NativePrototypeCallNative<T>>(
+                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
+                    needsAtomize_, getter_, pcOffset_, holder_, holderShape);
         }
 
-        MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallScripted);
-        if (kind == ICStub::GetElem_NativePrototypeCallScripted) {
-            return newStub<ICGetElem_NativePrototypeCallScripted>(
-                    space, getStubCode(), firstMonitorStub_, guard, name_, acctype_, needsAtomize_,
-                    getter_, pcOffset_, holder_, holderShape);
+        MOZ_ASSERT(kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
+                   kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol);
+        if (kind == ICStub::GetElem_NativePrototypeCallScriptedName ||
+            kind == ICStub::GetElem_NativePrototypeCallScriptedSymbol) {
+            return newStub<ICGetElem_NativePrototypeCallScripted<T>>(
+                    space, getStubCode(), firstMonitorStub_, guard, key_.address(), acctype_,
+                    needsAtomize_, getter_, pcOffset_, holder_, holderShape);
         }
 
         MOZ_CRASH("Invalid kind.");
     }
 };
 
 class ICGetElem_String : public ICStub
 {
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -68,21 +68,25 @@ namespace jit {
     _(Call_ClassHook)                            \
     _(Call_ScriptedApplyArray)                   \
     _(Call_ScriptedApplyArguments)               \
     _(Call_ScriptedFunCall)                      \
     _(Call_StringSplit)                          \
     _(Call_IsSuspendedStarGenerator)             \
                                                  \
     _(GetElem_Fallback)                          \
-    _(GetElem_NativeSlot)                        \
-    _(GetElem_NativePrototypeSlot)               \
-    _(GetElem_NativePrototypeCallNative)         \
-    _(GetElem_NativePrototypeCallScripted)       \
-    _(GetElem_UnboxedProperty)                   \
+    _(GetElem_NativeSlotName)                    \
+    _(GetElem_NativeSlotSymbol)                  \
+    _(GetElem_NativePrototypeSlotName)           \
+    _(GetElem_NativePrototypeSlotSymbol)         \
+    _(GetElem_NativePrototypeCallNativeName)     \
+    _(GetElem_NativePrototypeCallNativeSymbol)   \
+    _(GetElem_NativePrototypeCallScriptedName)   \
+    _(GetElem_NativePrototypeCallScriptedSymbol) \
+    _(GetElem_UnboxedPropertyName)               \
     _(GetElem_String)                            \
     _(GetElem_Dense)                             \
     _(GetElem_UnboxedArray)                      \
     _(GetElem_TypedArray)                        \
     _(GetElem_Arguments)                         \
                                                  \
     _(SetElem_Fallback)                          \
     _(SetElem_DenseOrUnboxedArray)               \
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -739,21 +739,25 @@ BaselineInspector::expectedPropertyAcces
           case ICStub::GetProp_NativePrototype:
           case ICStub::GetProp_Unboxed:
           case ICStub::GetProp_TypedObject:
           case ICStub::GetProp_CallScripted:
           case ICStub::GetProp_CallNative:
           case ICStub::GetProp_CallDOMProxyNative:
           case ICStub::GetProp_CallDOMProxyWithGenerationNative:
           case ICStub::GetProp_DOMProxyShadowed:
-          case ICStub::GetElem_NativeSlot:
-          case ICStub::GetElem_NativePrototypeSlot:
-          case ICStub::GetElem_NativePrototypeCallNative:
-          case ICStub::GetElem_NativePrototypeCallScripted:
-          case ICStub::GetElem_UnboxedProperty:
+          case ICStub::GetElem_NativeSlotName:
+          case ICStub::GetElem_NativeSlotSymbol:
+          case ICStub::GetElem_NativePrototypeSlotName:
+          case ICStub::GetElem_NativePrototypeSlotSymbol:
+          case ICStub::GetElem_NativePrototypeCallNativeName:
+          case ICStub::GetElem_NativePrototypeCallNativeSymbol:
+          case ICStub::GetElem_NativePrototypeCallScriptedName:
+          case ICStub::GetElem_NativePrototypeCallScriptedSymbol:
+          case ICStub::GetElem_UnboxedPropertyName:
           case ICStub::GetElem_String:
           case ICStub::GetElem_Dense:
           case ICStub::GetElem_TypedArray:
           case ICStub::GetElem_UnboxedArray:
             stubType = MIRType_Object;
             break;
 
           case ICStub::GetProp_Primitive:
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10111,16 +10111,37 @@ IonBuilder::maybeUnboxForPropertyAccess(
         return def;
 
     MIRType type = inspector->expectedPropertyAccessInputType(pc);
     if (type == MIRType_Value || !def->mightBeType(type))
         return def;
 
     MUnbox* unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
     current->add(unbox);
+
+    // Fixup type information for a common case where a property call
+    // is converted to the following bytecodes
+    //
+    //      a.foo()
+    // ================= Compiles to ================
+    //      LOAD "a"
+    //      DUP
+    //      CALLPROP "foo"
+    //      SWAP
+    //      CALL 0
+    //
+    // If we have better type information to unbox the first copy going into
+    // the CALLPROP operation, we can replace the duplicated copy on the
+    // stack as well.
+    if (*pc == JSOP_CALLPROP || *pc == JSOP_CALLELEM) {
+        uint32_t idx = current->stackDepth() - 1;
+        MOZ_ASSERT(current->getSlot(idx) == def);
+        current->setSlot(idx, unbox);
+    }
+
     return unbox;
 }
 
 bool
 IonBuilder::jsop_getprop(PropertyName* name)
 {
     bool emitted = false;
     startTrackingOptimizations();
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -176,19 +176,20 @@ class OsiIndex
 //      arg0      /
 //   -3 this    _/
 //   -2 callee
 //   -1 descriptor
 //    0 returnAddress
 //   .. locals ..
 
 // The descriptor is organized into three sections:
-// [ frame size | constructing bit | frame type ]
+// [ frame size | has cached saved frame bit | frame type ]
 // < highest - - - - - - - - - - - - - - lowest >
-static const uintptr_t FRAMESIZE_SHIFT = 4;
+static const uintptr_t FRAMESIZE_SHIFT = 5;
+static const uintptr_t HASCACHEDSAVEDFRAME_BIT = 1 << 4;
 static const uintptr_t FRAMETYPE_BITS = 4;
 
 // Ion frames have a few important numbers associated with them:
 //      Local depth:    The number of bytes required to spill local variables.
 //      Argument depth: The number of bytes required to push arguments and make
 //                      a function call.
 //      Slack:          A frame may temporarily use extra stack to resolve cycles.
 //
@@ -277,17 +278,17 @@ void MarkIonCompilerRoots(JSTracer* trc)
 JSCompartment*
 TopmostIonActivationCompartment(JSRuntime* rt);
 
 void UpdateJitActivationsForMinorGC(JSRuntime* rt, JSTracer* trc);
 
 static inline uint32_t
 MakeFrameDescriptor(uint32_t frameSize, FrameType type)
 {
-    return (frameSize << FRAMESIZE_SHIFT) | type;
+    return 0 | (frameSize << FRAMESIZE_SHIFT) | type;
 }
 
 // Returns the JSScript associated with the topmost JIT frame.
 inline JSScript*
 GetTopJitJSScript(JSContext* cx)
 {
     JitFrameIterator iter(cx);
     MOZ_ASSERT(iter.type() == JitFrame_Exit);
@@ -338,17 +339,23 @@ class CommonFrameLayout
     void changePrevType(FrameType type) {
         descriptor_ &= ~FrameTypeMask;
         descriptor_ |= type;
     }
     size_t prevFrameLocalSize() const {
         return descriptor_ >> FRAMESIZE_SHIFT;
     }
     void setFrameDescriptor(size_t size, FrameType type) {
-        descriptor_ = (size << FRAMESIZE_SHIFT) | type;
+        descriptor_ = 0 | (size << FRAMESIZE_SHIFT) | type;
+    }
+    bool hasCachedSavedFrame() const {
+        return descriptor_ & HASCACHEDSAVEDFRAME_BIT;
+    }
+    void setHasCachedSavedFrame() {
+        descriptor_ |= HASCACHEDSAVEDFRAME_BIT;
     }
     uint8_t* returnAddress() const {
         return returnAddress_;
     }
     void setReturnAddress(uint8_t* addr) {
         returnAddress_ = addr;
     }
 };
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2205,32 +2205,44 @@ NeedNegativeZeroCheck(MDefinition* def)
 MDefinition*
 MBinaryArithInstruction::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ == MIRType_None)
         return this;
 
     MDefinition* lhs = getOperand(0);
     MDefinition* rhs = getOperand(1);
-    if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
+    if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
+        if (isTruncated()) {
+            if (!folded->block())
+                block()->insertBefore(this, folded);
+            return MTruncateToInt32::New(alloc, folded);
+        }
         return folded;
+    }
 
     // 0 + -0 = 0. So we can't remove addition
     if (isAdd() && specialization_ != MIRType_Int32)
         return this;
 
-    if (IsConstant(rhs, getIdentity()))
+    if (IsConstant(rhs, getIdentity())) {
+        if (isTruncated())
+            return MTruncateToInt32::New(alloc, lhs);
         return lhs;
+    }
 
     // subtraction isn't commutative. So we can't remove subtraction when lhs equals 0
     if (isSub())
         return this;
 
-    if (IsConstant(lhs, getIdentity()))
+    if (IsConstant(lhs, getIdentity())) {
+        if (isTruncated())
+            return MTruncateToInt32::New(alloc, rhs);
         return rhs; // x op id => x
+    }
 
     return this;
 }
 
 void
 MBinaryArithInstruction::trySpecializeFloat32(TempAllocator& alloc)
 {
     // Do not use Float32 if we can use int32.
--- a/js/src/jit/RematerializedFrame.cpp
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -31,16 +31,17 @@ struct CopyValueToRematerializedFrame
     }
 };
 
 RematerializedFrame::RematerializedFrame(JSContext* cx, uint8_t* top, unsigned numActualArgs,
                                          InlineFrameIterator& iter, MaybeReadFallback& fallback)
   : prevUpToDate_(false),
     isDebuggee_(iter.script()->isDebuggee()),
     isConstructing_(iter.isConstructing()),
+    hasCachedSavedFrame_(false),
     top_(top),
     pc_(iter.pc()),
     frameNo_(iter.frameNo()),
     numActualArgs_(numActualArgs),
     script_(iter.script())
 {
     if (iter.isFunctionFrame())
         callee_ = iter.callee(fallback);
--- a/js/src/jit/RematerializedFrame.h
+++ b/js/src/jit/RematerializedFrame.h
@@ -30,16 +30,21 @@ class RematerializedFrame
     bool isDebuggee_;
 
     // Has a call object been pushed?
     bool hasCallObj_;
 
     // Is this frame constructing?
     bool isConstructing_;
 
+    // If true, this frame has been on the stack when
+    // |js::SavedStacks::saveCurrentStack| was called, and so there is a
+    // |js::SavedFrame| object cached for this frame.
+    bool hasCachedSavedFrame_;
+
     // The fp of the top frame associated with this possibly inlined frame.
     uint8_t* top_;
 
     // The bytecode at the time of rematerialization.
     jsbytecode* pc_;
 
     size_t frameNo_;
     unsigned numActualArgs_;
@@ -164,16 +169,24 @@ class RematerializedFrame
     Value& thisValue() {
         return thisValue_;
     }
 
     bool isConstructing() const {
         return isConstructing_;
     }
 
+    bool hasCachedSavedFrame() const {
+        return hasCachedSavedFrame_;
+    }
+
+    void setHasCachedSavedFrame() {
+        hasCachedSavedFrame_ = true;
+    }
+
     unsigned numFormalArgs() const {
         return maybeFun() ? fun()->nargs() : 0;
     }
     unsigned numActualArgs() const {
         return numActualArgs_;
     }
 
     Value* argv() {
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -184,41 +184,71 @@ ICStub::trace(JSTracer* trc)
       }
       case ICStub::Call_StringSplit: {
         ICCall_StringSplit* callStub = toCall_StringSplit();
         TraceEdge(trc, &callStub->templateObject(), "baseline-callstringsplit-template");
         TraceEdge(trc, &callStub->expectedArg(), "baseline-callstringsplit-arg");
         TraceEdge(trc, &callStub->expectedThis(), "baseline-callstringsplit-this");
         break;
       }
-      case ICStub::GetElem_NativeSlot:
-      case ICStub::GetElem_UnboxedProperty: {
-        ICGetElemNativeSlotStub* getElemStub =
-            reinterpret_cast<ICGetElemNativeSlotStub*>(this);
+      case ICStub::GetElem_NativeSlotName:
+      case ICStub::GetElem_NativeSlotSymbol:
+      case ICStub::GetElem_UnboxedPropertyName: {
+        ICGetElemNativeStub* getElemStub = static_cast<ICGetElemNativeStub*>(this);
         getElemStub->receiverGuard().trace(trc);
-        TraceEdge(trc, &getElemStub->name(), "baseline-getelem-native-name");
+        if (getElemStub->isSymbol()) {
+            ICGetElem_NativeSlot<JS::Symbol*>* typedGetElemStub = toGetElem_NativeSlotSymbol();
+            TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-native-key");
+        } else {
+            ICGetElemNativeSlotStub<PropertyName*>* typedGetElemStub =
+                reinterpret_cast<ICGetElemNativeSlotStub<PropertyName*>*>(this);
+            TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-native-key");
+        }
         break;
       }
-      case ICStub::GetElem_NativePrototypeSlot: {
-        ICGetElem_NativePrototypeSlot* getElemStub = toGetElem_NativePrototypeSlot();
+      case ICStub::GetElem_NativePrototypeSlotName:
+      case ICStub::GetElem_NativePrototypeSlotSymbol: {
+        ICGetElemNativeStub* getElemStub = static_cast<ICGetElemNativeStub*>(this);
         getElemStub->receiverGuard().trace(trc);
-        TraceEdge(trc, &getElemStub->name(), "baseline-getelem-nativeproto-name");
-        TraceEdge(trc, &getElemStub->holder(), "baseline-getelem-nativeproto-holder");
-        TraceEdge(trc, &getElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape");
+        if (getElemStub->isSymbol()) {
+            ICGetElem_NativePrototypeSlot<JS::Symbol*>* typedGetElemStub
+                = toGetElem_NativePrototypeSlotSymbol();
+            TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-nativeproto-key");
+            TraceEdge(trc, &typedGetElemStub->holder(), "baseline-getelem-nativeproto-holder");
+            TraceEdge(trc, &typedGetElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape");
+        } else {
+            ICGetElem_NativePrototypeSlot<PropertyName*>* typedGetElemStub
+                = toGetElem_NativePrototypeSlotName();
+            TraceEdge(trc, &typedGetElemStub->key(), "baseline-getelem-nativeproto-key");
+            TraceEdge(trc, &typedGetElemStub->holder(), "baseline-getelem-nativeproto-holder");
+            TraceEdge(trc, &typedGetElemStub->holderShape(), "baseline-getelem-nativeproto-holdershape");
+        }
         break;
       }
-      case ICStub::GetElem_NativePrototypeCallNative:
-      case ICStub::GetElem_NativePrototypeCallScripted: {
-        ICGetElemNativePrototypeCallStub* callStub =
-            reinterpret_cast<ICGetElemNativePrototypeCallStub*>(this);
-        callStub->receiverGuard().trace(trc);
-        TraceEdge(trc, &callStub->name(), "baseline-getelem-nativeprotocall-name");
-        TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter");
-        TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder");
-        TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape");
+      case ICStub::GetElem_NativePrototypeCallNativeName:
+      case ICStub::GetElem_NativePrototypeCallNativeSymbol:
+      case ICStub::GetElem_NativePrototypeCallScriptedName:
+      case ICStub::GetElem_NativePrototypeCallScriptedSymbol: {
+        ICGetElemNativeStub* getElemStub = static_cast<ICGetElemNativeStub*>(this);
+        getElemStub->receiverGuard().trace(trc);
+        if (getElemStub->isSymbol()) {
+            ICGetElemNativePrototypeCallStub<JS::Symbol*>* callStub =
+                reinterpret_cast<ICGetElemNativePrototypeCallStub<JS::Symbol*>*>(this);
+            TraceEdge(trc, &callStub->key(), "baseline-getelem-nativeprotocall-key");
+            TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter");
+            TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder");
+            TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape");
+        } else {
+            ICGetElemNativePrototypeCallStub<PropertyName*>* callStub =
+                reinterpret_cast<ICGetElemNativePrototypeCallStub<PropertyName*>*>(this);
+            TraceEdge(trc, &callStub->key(), "baseline-getelem-nativeprotocall-key");
+            TraceEdge(trc, &callStub->getter(), "baseline-getelem-nativeprotocall-getter");
+            TraceEdge(trc, &callStub->holder(), "baseline-getelem-nativeprotocall-holder");
+            TraceEdge(trc, &callStub->holderShape(), "baseline-getelem-nativeprotocall-holdershape");
+        }
         break;
       }
       case ICStub::GetElem_Dense: {
         ICGetElem_Dense* getElemStub = toGetElem_Dense();
         TraceEdge(trc, &getElemStub->shape(), "baseline-getelem-dense-shape");
         break;
       }
       case ICStub::GetElem_UnboxedArray: {
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -685,21 +685,25 @@ class ICStub
           case Call_AnyScripted:
           case Call_Native:
           case Call_ClassHook:
           case Call_ScriptedApplyArray:
           case Call_ScriptedApplyArguments:
           case Call_ScriptedFunCall:
           case Call_StringSplit:
           case WarmUpCounter_Fallback:
-          case GetElem_NativeSlot:
-          case GetElem_NativePrototypeSlot:
-          case GetElem_NativePrototypeCallNative:
-          case GetElem_NativePrototypeCallScripted:
-          case GetElem_UnboxedProperty:
+          case GetElem_NativeSlotName:
+          case GetElem_NativeSlotSymbol:
+          case GetElem_NativePrototypeSlotName:
+          case GetElem_NativePrototypeSlotSymbol:
+          case GetElem_NativePrototypeCallNativeName:
+          case GetElem_NativePrototypeCallNativeSymbol:
+          case GetElem_NativePrototypeCallScriptedName:
+          case GetElem_NativePrototypeCallScriptedSymbol:
+          case GetElem_UnboxedPropertyName:
           case GetProp_CallScripted:
           case GetProp_CallNative:
           case GetProp_CallDOMProxyNative:
           case GetProp_CallDOMProxyWithGenerationNative:
           case GetProp_DOMProxyShadowed:
           case GetProp_Generic:
           case SetProp_CallScripted:
           case SetProp_CallNative:
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -200,25 +200,25 @@ JS_FRIEND_API(bool)
 JS_WrapPropertyDescriptor(JSContext* cx, JS::MutableHandle<js::PropertyDescriptor> desc)
 {
     return cx->compartment()->wrap(cx, desc);
 }
 
 JS_FRIEND_API(void)
 JS_TraceShapeCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr shape)
 {
-    MOZ_ASSERT(shape.isShape());
-    TraceCycleCollectorChildren(trc, static_cast<Shape*>(shape.asCell()));
+    MOZ_ASSERT(shape.is<Shape>());
+    TraceCycleCollectorChildren(trc, &shape.as<Shape>());
 }
 
 JS_FRIEND_API(void)
 JS_TraceObjectGroupCycleCollectorChildren(JS::CallbackTracer* trc, JS::GCCellPtr group)
 {
-    MOZ_ASSERT(group.isObjectGroup());
-    TraceCycleCollectorChildren(trc, static_cast<ObjectGroup*>(group.asCell()));
+    MOZ_ASSERT(group.is<ObjectGroup>());
+    TraceCycleCollectorChildren(trc, &group.as<ObjectGroup>());
 }
 
 static bool
 DefineHelpProperty(JSContext* cx, HandleObject obj, const char* prop, const char* value)
 {
     RootedAtom atom(cx, Atomize(cx, value, strlen(value)));
     if (!atom)
         return false;
@@ -847,18 +847,18 @@ struct DumpHeapTracer : public JS::Callb
     DumpHeapTracer(FILE* fp, JSRuntime* rt)
       : JS::CallbackTracer(rt, DoNotTraceWeakMaps),
         js::WeakMapTracer(rt), prefix(""), output(fp)
     {}
 
   private:
     void trace(JSObject* map, JS::GCCellPtr key, JS::GCCellPtr value) override {
         JSObject* kdelegate = nullptr;
-        if (key.isObject())
-            kdelegate = js::GetWeakmapKeyDelegate(key.toObject());
+        if (key.is<JSObject>())
+            kdelegate = js::GetWeakmapKeyDelegate(&key.as<JSObject>());
 
         fprintf(output, "WeakMapEntry map=%p key=%p keyDelegate=%p value=%p\n",
                 map, key.asCell(), kdelegate, value.asCell());
     }
 
     void onChild(const JS::GCCellPtr& thing) override;
 };
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2604,16 +2604,17 @@ GCRuntime::updatePointersToRelocatedCell
     {
         markRuntime(&trc, MarkRuntime);
 
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&trc);
         Debugger::markIncomingCrossCompartmentEdges(&trc);
 
         for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) {
+            c->trace(&trc);
             WeakMapBase::markAll(c, &trc);
             if (c->watchpointMap)
                 c->watchpointMap->markAll(&trc);
         }
 
         // Mark all gray roots, making sure we call the trace callback to get the
         // current set.
         if (JSTraceDataOp op = grayRootTracer.op)
@@ -3715,17 +3716,17 @@ struct MaybeCompartmentFunctor {
     template <typename T> JSCompartment* operator()(T* t) { return t->maybeCompartment(); }
 };
 
 void
 CompartmentCheckTracer::onChild(const JS::GCCellPtr& thing)
 {
     TenuredCell* tenured = TenuredCell::fromPointer(thing.asCell());
 
-    JSCompartment* comp = CallTyped(MaybeCompartmentFunctor(), tenured, thing.kind());
+    JSCompartment* comp = DispatchTraceKindTyped(MaybeCompartmentFunctor(), tenured, thing.kind());
     if (comp && compartment) {
         MOZ_ASSERT(comp == compartment || runtime()->isAtomsCompartment(comp) ||
                    (srcKind == JS::TraceKind::Object &&
                     InCrossCompartmentMap(static_cast<JSObject*>(src), tenured, thing.kind())));
     } else {
         MOZ_ASSERT(tenured->zone() == zone || tenured->zone()->isAtomsZone());
     }
 }
@@ -3738,17 +3739,18 @@ GCRuntime::checkForCompartmentMismatches
 
     CompartmentCheckTracer trc(rt);
     for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
         trc.zone = zone;
         for (auto thingKind : AllAllocKinds()) {
             for (ZoneCellIterUnderGC i(zone, thingKind); !i.done(); i.next()) {
                 trc.src = i.getCell();
                 trc.srcKind = MapAllocToTraceKind(thingKind);
-                trc.compartment = CallTyped(MaybeCompartmentFunctor(), trc.src, trc.srcKind);
+                trc.compartment = DispatchTraceKindTyped(MaybeCompartmentFunctor(),
+                                                         trc.src, trc.srcKind);
                 JS_TraceChildren(&trc, trc.src, trc.srcKind);
             }
         }
     }
 }
 #endif
 
 static void
@@ -6966,17 +6968,17 @@ JS::AutoAssertGCCallback::AutoAssertGCCa
     MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapCollecting());
 }
 
 JS_FRIEND_API(const char*)
 JS::GCTraceKindToAscii(JS::TraceKind kind)
 {
     switch(kind) {
 #define MAP_NAME(name, _0, _1) case JS::TraceKind::name: return #name;
-FOR_EACH_GC_LAYOUT(MAP_NAME);
+JS_FOR_EACH_TRACEKIND(MAP_NAME);
 #undef MAP_NAME
       default: return "Invalid";
     }
 }
 
 JS::GCCellPtr::GCCellPtr(const Value& v)
   : ptr(0)
 {
@@ -6996,18 +6998,18 @@ JS::GCCellPtr::outOfLineKind() const
     MOZ_ASSERT((ptr & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
     MOZ_ASSERT(asCell()->isTenured());
     return MapAllocToTraceKind(asCell()->asTenured().getAllocKind());
 }
 
 bool
 JS::GCCellPtr::mayBeOwnedByOtherRuntime() const
 {
-    return (isString() && toString()->isPermanentAtom()) ||
-           (isSymbol() && toSymbol()->isWellKnownSymbol());
+    return (is<JSString>() && as<JSString>().isPermanentAtom()) ||
+           (is<Symbol>() && as<Symbol>().isWellKnownSymbol());
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
 js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt)
 {
     /*
      * Check that internal hash tables no longer have any pointers to things
@@ -7190,17 +7192,17 @@ struct IncrementalReferenceBarrierFuncto
 };
 
 JS_PUBLIC_API(void)
 JS::IncrementalReferenceBarrier(GCCellPtr thing)
 {
     if (!thing)
         return;
 
-    CallTyped(IncrementalReferenceBarrierFunctor(), thing.asCell(), thing.kind());
+    DispatchTraceKindTyped(IncrementalReferenceBarrierFunctor(), thing.asCell(), thing.kind());
 }
 
 JS_PUBLIC_API(void)
 JS::IncrementalValueBarrier(const Value& v)
 {
     js::HeapValue::writeBarrierPre(v);
 }
 
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -18,28 +18,16 @@
 #include "jslock.h"
 
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "js/Vector.h"
 
 #include "vm/NativeObject.h"
 
-#define FOR_EACH_GC_LAYOUT(D) \
- /* PrettyName       TypeName           AddToCCKind */ \
-    D(BaseShape,     js::BaseShape,     true) \
-    D(JitCode,       js::jit::JitCode,  true) \
-    D(LazyScript,    js::LazyScript,    true) \
-    D(Object,        JSObject,          true) \
-    D(ObjectGroup,   js::ObjectGroup,   true) \
-    D(Script,        JSScript,          true) \
-    D(Shape,         js::Shape,         true) \
-    D(String,        JSString,          false) \
-    D(Symbol,        JS::Symbol,        false)
-
 namespace js {
 
 unsigned GetCPUCount();
 
 enum ThreadType
 {
     MainThread,
     BackgroundThread
@@ -58,25 +46,16 @@ struct FinalizePhase;
 enum State {
     NO_INCREMENTAL,
     MARK_ROOTS,
     MARK,
     SWEEP,
     COMPACT
 };
 
-// Map from base trace type to the trace kind.
-template <typename T> struct MapTypeToTraceKind {};
-#define EXPAND_DEF(name, type, _) \
-    template <> struct MapTypeToTraceKind<type> { \
-        static const JS::TraceKind kind = JS::TraceKind::name; \
-    };
-FOR_EACH_GC_LAYOUT(EXPAND_DEF);
-#undef EXPAND_DEF
-
 /* Map from C++ type to alloc kind. JSObject does not have a 1:1 mapping, so must use Arena::thingSize. */
 template <typename T> struct MapTypeToFinalizeKind {};
 template <> struct MapTypeToFinalizeKind<JSScript>          { static const AllocKind kind = AllocKind::SCRIPT; };
 template <> struct MapTypeToFinalizeKind<LazyScript>        { static const AllocKind kind = AllocKind::LAZY_SCRIPT; };
 template <> struct MapTypeToFinalizeKind<Shape>             { static const AllocKind kind = AllocKind::SHAPE; };
 template <> struct MapTypeToFinalizeKind<AccessorShape>     { static const AllocKind kind = AllocKind::ACCESSOR_SHAPE; };
 template <> struct MapTypeToFinalizeKind<BaseShape>         { static const AllocKind kind = AllocKind::BASE_SHAPE; };
 template <> struct MapTypeToFinalizeKind<ObjectGroup>       { static const AllocKind kind = AllocKind::OBJECT_GROUP; };
@@ -84,17 +63,17 @@ template <> struct MapTypeToFinalizeKind
 template <> struct MapTypeToFinalizeKind<JSString>          { static const AllocKind kind = AllocKind::STRING; };
 template <> struct MapTypeToFinalizeKind<JSExternalString>  { static const AllocKind kind = AllocKind::EXTERNAL_STRING; };
 template <> struct MapTypeToFinalizeKind<JS::Symbol>        { static const AllocKind kind = AllocKind::SYMBOL; };
 template <> struct MapTypeToFinalizeKind<jit::JitCode>      { static const AllocKind kind = AllocKind::JITCODE; };
 
 template <typename T> struct ParticipatesInCC {};
 #define EXPAND_PARTICIPATES_IN_CC(_, type, addToCCKind) \
     template <> struct ParticipatesInCC<type> { static const bool value = addToCCKind; };
-FOR_EACH_GC_LAYOUT(EXPAND_PARTICIPATES_IN_CC)
+JS_FOR_EACH_TRACEKIND(EXPAND_PARTICIPATES_IN_CC)
 #undef EXPAND_PARTICIPATES_IN_CC
 
 static inline bool
 IsNurseryAllocable(AllocKind kind)
 {
     MOZ_ASSERT(IsValidAllocKind(kind));
     static const bool map[] = {
         true,      /* AllocKind::FUNCTION */
@@ -172,63 +151,16 @@ CanBeFinalizedInBackground(AllocKind kin
      * AllocKind::OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
      * IsBackgroundFinalized is called to prevent recursively incrementing
      * the alloc kind; kind may already be a background finalize kind.
      */
     return (!IsBackgroundFinalized(kind) &&
             (!clasp->finalize || (clasp->flags & JSCLASS_BACKGROUND_FINALIZE)));
 }
 
-// Fortunately, few places in the system need to deal with fully abstract
-// cells. In those places that do, we generally want to move to a layout
-// templated function as soon as possible. This template wraps the upcast
-// for that dispatch.
-//
-// Call the functor |F f| with template parameter of the layout type.
-
-// GCC and Clang require an explicit template declaration in front of the
-// specialization of operator() because it is a dependent template. MSVC, on
-// the other hand, gets very confused if we have a |template| token there.
-#ifdef _MSC_VER
-# define DEPENDENT_TEMPLATE_HINT
-#else
-# define DEPENDENT_TEMPLATE_HINT template
-#endif
-template <typename F, typename... Args>
-auto
-CallTyped(F f, JS::TraceKind traceKind, Args&&... args)
-  -> decltype(f. DEPENDENT_TEMPLATE_HINT operator()<JSObject>(mozilla::Forward<Args>(args)...))
-{
-    switch (traceKind) {
-#define EXPAND_DEF(name, type, _) \
-      case JS::TraceKind::name: \
-        return f. DEPENDENT_TEMPLATE_HINT operator()<type>(mozilla::Forward<Args>(args)...);
-      FOR_EACH_GC_LAYOUT(EXPAND_DEF);
-#undef EXPAND_DEF
-      default:
-          MOZ_CRASH("Invalid trace kind in CallTyped.");
-    }
-}
-#undef DEPENDENT_TEMPLATE_HINT
-
-template <typename F, typename... Args>
-auto
-CallTyped(F f, void* thing, JS::TraceKind traceKind, Args&&... args)
-  -> decltype(f(reinterpret_cast<JSObject*>(0), mozilla::Forward<Args>(args)...))
-{
-    switch (traceKind) {
-#define EXPAND_DEF(name, type, _) \
-      case JS::TraceKind::name: \
-          return f(static_cast<type*>(thing), mozilla::Forward<Args>(args)...);
-      FOR_EACH_GC_LAYOUT(EXPAND_DEF);
-#undef EXPAND_DEF
-      default:
-          MOZ_CRASH("Invalid trace kind in CallTyped.");
-    }
-}
 /* Capacity for slotsToThingKind */
 const size_t SLOTS_TO_THING_KIND_LIMIT = 17;
 
 extern const AllocKind slotsToThingKind[];
 
 /* Get the best kind to use when making an object with the given slot count. */
 static inline AllocKind
 GetGCObjectKind(size_t numSlots)
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -246,18 +246,18 @@ class WeakMap : public HashMap<Key, Valu
 
     /* memberOf can be nullptr, which means that the map is not part of a JSObject. */
     void traceMappings(WeakMapTracer* tracer) {
         for (Range r = Base::all(); !r.empty(); r.popFront()) {
             gc::Cell* key = gc::ToMarkable(r.front().key());
             gc::Cell* value = gc::ToMarkable(r.front().value());
             if (key && value) {
                 tracer->trace(memberOf,
-                              JS::GCCellPtr(r.front().key()),
-                              JS::GCCellPtr(r.front().value()));
+                              JS::GCCellPtr(r.front().key().get()),
+                              JS::GCCellPtr(r.front().value().get()));
             }
         }
     }
 
     /* Rekey an entry when moved, ensuring we do not trigger barriers. */
     void entryMoved(Enum& e, const Key& k) {
         e.rekeyFront(k);
     }
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -74,19 +74,30 @@ class DebuggerWeakMap : private WeakMap<
                     uintptr_t,
                     DefaultHasher<JS::Zone*>,
                     RuntimeAllocPolicy> CountMap;
 
     CountMap zoneCounts;
 
   public:
     typedef WeakMap<Key, Value, DefaultHasher<Key> > Base;
+
     explicit DebuggerWeakMap(JSContext* cx)
         : Base(cx), zoneCounts(cx->runtime()) { }
 
+    ~DebuggerWeakMap() {
+        // If our owning Debugger fails construction after already initializing
+        // this DebuggerWeakMap, we need to make sure that we aren't in the
+        // compartment's weak map list anymore. Normally, when we are destroyed
+        // because the GC finds us unreachable, the GC takes care of removing us
+        // from this list.
+        if (WeakMapBase::isInList())
+            WeakMapBase::removeWeakMapFromList(this);
+    }
+
   public:
     /* Expose those parts of HashMap public interface that are used by Debugger methods. */
 
     typedef typename Base::Entry Entry;
     typedef typename Base::Ptr Ptr;
     typedef typename Base::AddPtr AddPtr;
     typedef typename Base::Range Range;
     typedef typename Base::Enum Enum;
new file mode 100644
--- /dev/null
+++ b/js/src/vm/SavedFrame.h
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 vm_SavedFrame_h
+#define vm_SavedFrame_h
+
+namespace js {
+
+class SavedFrame : public NativeObject {
+    friend class SavedStacks;
+
+  public:
+    static const Class          class_;
+    static const JSPropertySpec protoAccessors[];
+    static const JSFunctionSpec protoFunctions[];
+    static const JSFunctionSpec staticFunctions[];
+
+    // Prototype methods and properties to be exposed to JS.
+    static bool construct(JSContext* cx, unsigned argc, Value* vp);
+    static bool sourceProperty(JSContext* cx, unsigned argc, Value* vp);
+    static bool lineProperty(JSContext* cx, unsigned argc, Value* vp);
+    static bool columnProperty(JSContext* cx, unsigned argc, Value* vp);
+    static bool functionDisplayNameProperty(JSContext* cx, unsigned argc, Value* vp);
+    static bool asyncCauseProperty(JSContext* cx, unsigned argc, Value* vp);
+    static bool asyncParentProperty(JSContext* cx, unsigned argc, Value* vp);
+    static bool parentProperty(JSContext* cx, unsigned argc, Value* vp);
+    static bool toStringMethod(JSContext* cx, unsigned argc, Value* vp);
+
+    static void finalize(FreeOp* fop, JSObject* obj);
+
+    // Convenient getters for SavedFrame's reserved slots for use from C++.
+    JSAtom*      getSource();
+    uint32_t     getLine();
+    uint32_t     getColumn();
+    JSAtom*      getFunctionDisplayName();
+    JSAtom*      getAsyncCause();
+    SavedFrame*  getParent();
+    JSPrincipals* getPrincipals();
+
+    bool         isSelfHosted();
+
+    static bool isSavedFrameAndNotProto(JSObject& obj) {
+        return obj.is<SavedFrame>() &&
+               !obj.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull();
+    }
+
+    struct Lookup;
+    struct HashPolicy;
+
+    typedef HashSet<js::ReadBarriered<SavedFrame*>,
+                    HashPolicy,
+                    SystemAllocPolicy> Set;
+
+    class AutoLookupVector;
+
+    class MOZ_STACK_CLASS HandleLookup {
+        friend class AutoLookupVector;
+
+        Lookup& lookup;
+
+        explicit HandleLookup(Lookup& lookup) : lookup(lookup) { }
+
+      public:
+        inline Lookup& get() { return lookup; }
+        inline Lookup* operator->() { return &lookup; }
+    };
+
+  private:
+    static bool finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject proto);
+    void initFromLookup(HandleLookup lookup);
+
+    enum {
+        // The reserved slots in the SavedFrame class.
+        JSSLOT_SOURCE,
+        JSSLOT_LINE,
+        JSSLOT_COLUMN,
+        JSSLOT_FUNCTIONDISPLAYNAME,
+        JSSLOT_ASYNCCAUSE,
+        JSSLOT_PARENT,
+        JSSLOT_PRINCIPALS,
+        JSSLOT_PRIVATE_PARENT,
+
+        // The total number of reserved slots in the SavedFrame class.
+        JSSLOT_COUNT
+    };
+
+    // Because we hash the parent pointer, we need to rekey a saved frame
+    // whenever its parent was relocated by the GC. However, the GC doesn't
+    // notify us when this occurs. As a work around, we keep a duplicate copy of
+    // the parent pointer as a private value in a reserved slot. Whenever the
+    // private value parent pointer doesn't match the regular parent pointer, we
+    // know that GC moved the parent and we need to update our private value and
+    // rekey the saved frame in its hash set. These two methods are helpers for
+    // this process.
+    bool parentMoved();
+    void updatePrivateParent();
+
+    static bool checkThis(JSContext* cx, CallArgs& args, const char* fnName,
+                          MutableHandleObject frame);
+};
+
+struct SavedFrame::HashPolicy
+{
+    typedef SavedFrame::Lookup              Lookup;
+    typedef PointerHasher<SavedFrame*, 3>   SavedFramePtrHasher;
+    typedef PointerHasher<JSPrincipals*, 3> JSPrincipalsPtrHasher;
+
+    static HashNumber hash(const Lookup& lookup);
+    static bool       match(SavedFrame* existing, const Lookup& lookup);
+
+    typedef ReadBarriered<SavedFrame*> Key;
+    static void rekey(Key& key, const Key& newKey);
+};
+
+// Assert that if the given object is not null, that it must be either a
+// SavedFrame object or wrapper (Xray or CCW) around a SavedFrame object.
+inline void AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack);
+
+} // namespace js
+
+#endif // vm_SavedFrame_h
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -3,23 +3,22 @@
  * 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 "vm/SavedStacks.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
-#include "mozilla/Maybe.h"
+#include "mozilla/Move.h"
 
 #include <algorithm>
 #include <math.h>
 
 #include "jsapi.h"
-#include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jshashutil.h"
 #include "jsmath.h"
 #include "jsnum.h"
 #include "jsscript.h"
 #include "prmjtime.h"
 
@@ -28,64 +27,168 @@
 #include "js/Vector.h"
 #include "vm/Debugger.h"
 #include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
 
 #include "jscntxtinlines.h"
 
 #include "vm/NativeObject-inl.h"
+#include "vm/Stack-inl.h"
 
 using mozilla::AddToHash;
 using mozilla::DebugOnly;
 using mozilla::HashString;
 using mozilla::Maybe;
+using mozilla::Move;
+using mozilla::Nothing;
+using mozilla::Some;
 
 namespace js {
 
 /**
  * Maximum number of saved frames returned for an async stack.
  */
 const unsigned ASYNC_STACK_MAX_FRAME_COUNT = 60;
 
+/* static */ Maybe<LiveSavedFrameCache::FramePtr>
+LiveSavedFrameCache::getFramePtr(FrameIter& iter)
+{
+    if (iter.hasUsableAbstractFramePtr())
+        return Some(FramePtr(iter.abstractFramePtr()));
+
+    if (iter.isPhysicalIonFrame())
+        return Some(FramePtr(iter.physicalIonFrame()));
+
+    return Nothing();
+}
+
+/* static */ void
+LiveSavedFrameCache::trace(LiveSavedFrameCache* cache, JSTracer* trc)
+{
+    if (!cache->initialized())
+        return;
+
+    for (auto* entry = cache->frames->begin(); entry < cache->frames->end(); entry++) {
+        TraceEdge(trc,
+                  &entry->savedFrame,
+                  "LiveSavedFrameCache::frames SavedFrame");
+    }
+}
+
+bool
+LiveSavedFrameCache::insert(JSContext* cx, FramePtr& framePtr, jsbytecode* pc,
+                            HandleSavedFrame savedFrame)
+{
+    MOZ_ASSERT(initialized());
+
+    if (!frames->emplaceBack(framePtr, pc, savedFrame)) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    // Safe to dereference the cache key because the stack frames are still
+    // live. After this point, they should never be dereferenced again.
+    if (framePtr.is<AbstractFramePtr>())
+        framePtr.as<AbstractFramePtr>().setHasCachedSavedFrame();
+    else
+        framePtr.as<jit::CommonFrameLayout*>()->setHasCachedSavedFrame();
+
+    return true;
+}
+
+void
+LiveSavedFrameCache::find(JSContext* cx, FrameIter& frameIter, MutableHandleSavedFrame frame) const
+{
+    MOZ_ASSERT(initialized());
+    MOZ_ASSERT(!frameIter.done());
+    MOZ_ASSERT(frameIter.hasCachedSavedFrame());
+
+    Maybe<FramePtr> maybeFramePtr = getFramePtr(frameIter);
+    MOZ_ASSERT(maybeFramePtr.isSome());
+
+    FramePtr framePtr(*maybeFramePtr);
+    jsbytecode* pc = frameIter.pc();
+    size_t numberStillValid = 0;
+
+    frame.set(nullptr);
+    for (auto* p = frames->begin(); p < frames->end(); p++) {
+        numberStillValid++;
+        if (framePtr == p->framePtr && pc == p->pc) {
+            frame.set(p->savedFrame);
+            break;
+        }
+    }
+
+    if (!frame) {
+        frames->clear();
+        return;
+    }
+
+    MOZ_ASSERT(0 < numberStillValid && numberStillValid <= frames->length());
+
+    if (frame->compartment() != cx->compartment()) {
+        frame.set(nullptr);
+        numberStillValid--;
+    }
+
+    // Everything after the cached SavedFrame are stale younger frames we have
+    // since popped.
+    frames->shrinkBy(frames->length() - numberStillValid);
+}
+
 struct SavedFrame::Lookup {
     Lookup(JSAtom* source, uint32_t line, uint32_t column,
            JSAtom* functionDisplayName, JSAtom* asyncCause, SavedFrame* parent,
-           JSPrincipals* principals)
+           JSPrincipals* principals, Maybe<LiveSavedFrameCache::FramePtr> framePtr, jsbytecode* pc,
+           Activation* activation)
       : source(source),
         line(line),
         column(column),
         functionDisplayName(functionDisplayName),
         asyncCause(asyncCause),
         parent(parent),
-        principals(principals)
+        principals(principals),
+        framePtr(framePtr),
+        pc(pc),
+        activation(activation)
     {
         MOZ_ASSERT(source);
+        MOZ_ASSERT(activation);
     }
 
     explicit Lookup(SavedFrame& savedFrame)
       : source(savedFrame.getSource()),
         line(savedFrame.getLine()),
         column(savedFrame.getColumn()),
         functionDisplayName(savedFrame.getFunctionDisplayName()),
         asyncCause(savedFrame.getAsyncCause()),
         parent(savedFrame.getParent()),
-        principals(savedFrame.getPrincipals())
+        principals(savedFrame.getPrincipals()),
+        framePtr(Nothing()),
+        pc(nullptr),
+        activation(nullptr)
     {
         MOZ_ASSERT(source);
     }
 
-    JSAtom*      source;
-    uint32_t     line;
-    uint32_t     column;
-    JSAtom*      functionDisplayName;
-    JSAtom*      asyncCause;
-    SavedFrame*  parent;
+    JSAtom*       source;
+    uint32_t      line;
+    uint32_t      column;
+    JSAtom*       functionDisplayName;
+    JSAtom*       asyncCause;
+    SavedFrame*   parent;
     JSPrincipals* principals;
 
+    // These are used only by the LiveSavedFrameCache and not used for identity or
+    // hashing.
+    Maybe<LiveSavedFrameCache::FramePtr> framePtr;
+    jsbytecode*                          pc;
+    Activation*                          activation;
+
     void trace(JSTracer* trc) {
         TraceManuallyBarrieredEdge(trc, &source, "SavedFrame::Lookup::source");
         if (functionDisplayName) {
             TraceManuallyBarrieredEdge(trc, &functionDisplayName,
                                        "SavedFrame::Lookup::functionDisplayName");
         }
         if (asyncCause)
             TraceManuallyBarrieredEdge(trc, &asyncCause, "SavedFrame::Lookup::asyncCause");
@@ -394,18 +497,17 @@ GetFirstSubsumedSavedFrame(JSContext* cx
 
 /* static */ bool
 SavedFrame::checkThis(JSContext* cx, CallArgs& args, const char* fnName,
                       MutableHandleObject frame)
 {
     const Value& thisValue = args.thisv();
 
     if (!thisValue.isObject()) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT,
-                             InformalValueTypeName(thisValue));
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, InformalValueTypeName(thisValue));
         return false;
     }
 
     JSObject* thisObject = CheckedUnwrap(&thisValue.toObject());
     if (!thisObject || !thisObject->is<SavedFrame>()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                              SavedFrame::class_.name, fnName,
                              thisObject ? thisObject->getClass()->name : "object");
@@ -809,17 +911,17 @@ SavedStacks::init()
 }
 
 bool
 SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsigned maxFrameCount)
 {
     MOZ_ASSERT(initialized());
     assertSameCompartment(cx, this);
 
-    if (creatingSavedFrame) {
+    if (creatingSavedFrame || cx->isExceptionPending()) {
         frame.set(nullptr);
         return true;
     }
 
     FrameIter iter(cx, FrameIter::ALL_CONTEXTS, FrameIter::GO_THROUGH_SAVED);
     return insertFrames(cx, iter, frame, maxFrameCount);
 }
 
@@ -850,23 +952,22 @@ SavedStacks::sweep(JSRuntime* rt)
     }
 
     sweepPCLocationMap();
 }
 
 void
 SavedStacks::trace(JSTracer* trc)
 {
-    if (!pcLocationMap.initialized())
-        return;
-
-    // Mark each of the source strings in our pc to location cache.
-    for (PCLocationMap::Enum e(pcLocationMap); !e.empty(); e.popFront()) {
-        LocationValue& loc = e.front().value();
-        TraceEdge(trc, &loc.source, "SavedStacks::PCLocationMap's memoized script source name");
+    if (pcLocationMap.initialized()) {
+        // Mark each of the source strings in our pc to location cache.
+        for (PCLocationMap::Enum e(pcLocationMap); !e.empty(); e.popFront()) {
+            LocationValue& loc = e.front().value();
+            TraceEdge(trc, &loc.source, "SavedStacks::PCLocationMap's memoized script source name");
+        }
     }
 }
 
 uint32_t
 SavedStacks::count()
 {
     MOZ_ASSERT(initialized());
     return frames.count();
@@ -901,16 +1002,18 @@ SavedStacks::insertFrames(JSContext* cx,
     // only contain the FrameIter data we need. The `SavedFrame::Lookup`
     // objects are partially initialized with everything except their parent
     // pointers on the first pass, and then we fill in the parent pointers as we
     // return in the second pass.
 
     Activation* asyncActivation = nullptr;
     RootedSavedFrame asyncStack(cx, nullptr);
     RootedString asyncCause(cx, nullptr);
+    bool parentIsInCache = false;
+    RootedSavedFrame cachedFrame(cx, nullptr);
 
     // Accumulate the vector of Lookup objects in |stackChain|.
     SavedFrame::AutoLookupVector stackChain(cx);
     while (!iter.done()) {
         Activation& activation = *iter.activation();
 
         if (asyncActivation && asyncActivation != &activation) {
             // We found an async stack in the previous activation, and we
@@ -938,60 +1041,86 @@ SavedStacks::insertFrames(JSContext* cx,
 
         AutoLocationValueRooter location(cx);
         {
             AutoCompartment ac(cx, iter.compartment());
             if (!cx->compartment()->savedStacks().getLocation(cx, iter, &location))
                 return false;
         }
 
+        // The bit set means that the next older parent (frame, pc) pair *must*
+        // be in the cache.
+        if (maxFrameCount == 0)
+            parentIsInCache = iter.hasCachedSavedFrame();
+
         auto displayAtom = iter.isNonEvalFunctionFrame() ? iter.functionDisplayAtom() : nullptr;
         if (!stackChain->emplaceBack(location->source,
                                      location->line,
                                      location->column,
                                      displayAtom,
                                      nullptr,
                                      nullptr,
-                                     iter.compartment()->principals()))
+                                     iter.compartment()->principals(),
+                                     LiveSavedFrameCache::getFramePtr(iter),
+                                     iter.pc(),
+                                     &activation))
         {
             ReportOutOfMemory(cx);
             return false;
         }
 
         ++iter;
 
-        // If maxFrameCount is zero there's no limit on the number of frames.
-        if (maxFrameCount == 0)
-            continue;
-
         if (maxFrameCount == 1) {
             // The frame we just saved was the last one we were asked to save.
             // If we had an async stack, ensure we don't use any of its frames.
             asyncStack.set(nullptr);
             break;
         }
 
+        if (parentIsInCache &&
+            !iter.done() &&
+            iter.hasCachedSavedFrame())
+        {
+            auto* cache = activation.getLiveSavedFrameCache(cx);
+            if (!cache)
+                return false;
+            cache->find(cx, iter, &cachedFrame);
+            if (cachedFrame)
+                break;
+        }
+
+        // If maxFrameCount is zero there's no limit on the number of frames.
+        if (maxFrameCount == 0)
+            continue;
+
         maxFrameCount--;
     }
 
     // Limit the depth of the async stack, if any, and ensure that the
     // SavedFrame instances we use are stored in the same compartment as the
     // rest of the synchronous stack chain.
-    RootedSavedFrame parentFrame(cx, nullptr);
+    RootedSavedFrame parentFrame(cx, cachedFrame);
     if (asyncStack && !adoptAsyncStack(cx, asyncStack, asyncCause, &parentFrame, maxFrameCount))
         return false;
 
     // Iterate through |stackChain| in reverse order and get or create the
     // actual SavedFrame instances.
     for (size_t i = stackChain->length(); i != 0; i--) {
         SavedFrame::HandleLookup lookup = stackChain[i-1];
         lookup->parent = parentFrame;
         parentFrame.set(getOrCreateSavedFrame(cx, lookup));
         if (!parentFrame)
             return false;
+
+        if (maxFrameCount == 0 && lookup->framePtr && parentFrame != cachedFrame) {
+            auto* cache = lookup->activation->getLiveSavedFrameCache(cx);
+            if (!cache || !cache->insert(cx, *lookup->framePtr, lookup->pc, parentFrame))
+                return false;
+        }
     }
 
     frame.set(parentFrame);
     return true;
 }
 
 bool
 SavedStacks::adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -6,18 +6,21 @@
 
 #ifndef vm_SavedStacks_h
 #define vm_SavedStacks_h
 
 #include "jscntxt.h"
 #include "jsmath.h"
 #include "jswrapper.h"
 #include "js/HashTable.h"
+#include "vm/SavedFrame.h"
 #include "vm/Stack.h"
 
+namespace js {
+
 // # Saved Stacks
 //
 // The `SavedStacks` class provides a compact way to capture and save JS stacks
 // as `SavedFrame` `JSObject` subclasses. A single `SavedFrame` object
 // represents one frame that was on the stack, and has a strong reference to its
 // parent `SavedFrame` (the next youngest frame). This reference is null when
 // the `SavedFrame` object is the oldest frame that was on the stack.
 //
@@ -137,133 +140,16 @@
 // frames.
 //
 // In the case of z, the `SavedFrame` accessors are called with the `SavedFrame`
 // object in the `this` value, and the content compartment as the cx's current
 // compartment. Similar to the case of y, only the B and C frames are exposed
 // because the cx's current compartment's principals do not subsume A's captured
 // principals.
 
-
-namespace js {
-
-class SavedFrame;
-typedef JS::Handle<SavedFrame*> HandleSavedFrame;
-typedef JS::MutableHandle<SavedFrame*> MutableHandleSavedFrame;
-typedef JS::Rooted<SavedFrame*> RootedSavedFrame;
-
-class SavedFrame : public NativeObject {
-    friend class SavedStacks;
-
-  public:
-    static const Class          class_;
-    static void finalize(FreeOp* fop, JSObject* obj);
-    static const JSPropertySpec protoAccessors[];
-    static const JSFunctionSpec protoFunctions[];
-    static const JSFunctionSpec staticFunctions[];
-
-    // Prototype methods and properties to be exposed to JS.
-    static bool construct(JSContext* cx, unsigned argc, Value* vp);
-    static bool sourceProperty(JSContext* cx, unsigned argc, Value* vp);
-    static bool lineProperty(JSContext* cx, unsigned argc, Value* vp);
-    static bool columnProperty(JSContext* cx, unsigned argc, Value* vp);
-    static bool functionDisplayNameProperty(JSContext* cx, unsigned argc, Value* vp);
-    static bool asyncCauseProperty(JSContext* cx, unsigned argc, Value* vp);
-    static bool asyncParentProperty(JSContext* cx, unsigned argc, Value* vp);
-    static bool parentProperty(JSContext* cx, unsigned argc, Value* vp);
-    static bool toStringMethod(JSContext* cx, unsigned argc, Value* vp);
-
-    // Convenient getters for SavedFrame's reserved slots for use from C++.
-    JSAtom*      getSource();
-    uint32_t     getLine();
-    uint32_t     getColumn();
-    JSAtom*      getFunctionDisplayName();
-    JSAtom*      getAsyncCause();
-    SavedFrame*  getParent();
-    JSPrincipals* getPrincipals();
-
-    bool         isSelfHosted();
-
-    static bool isSavedFrameAndNotProto(JSObject& obj) {
-        return obj.is<SavedFrame>() &&
-               !obj.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull();
-    }
-
-    struct Lookup;
-    struct HashPolicy;
-
-    typedef HashSet<js::ReadBarriered<SavedFrame*>,
-                    HashPolicy,
-                    SystemAllocPolicy> Set;
-
-    class AutoLookupVector;
-
-    class MOZ_STACK_CLASS HandleLookup {
-        friend class AutoLookupVector;
-
-        Lookup& lookup;
-
-        explicit HandleLookup(Lookup& lookup) : lookup(lookup) { }
-
-      public:
-        inline Lookup& get() { return lookup; }
-        inline Lookup* operator->() { return &lookup; }
-    };
-
-  private:
-    static bool finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject proto);
-    void initFromLookup(HandleLookup lookup);
-
-    enum {
-        // The reserved slots in the SavedFrame class.
-        JSSLOT_SOURCE,
-        JSSLOT_LINE,
-        JSSLOT_COLUMN,
-        JSSLOT_FUNCTIONDISPLAYNAME,
-        JSSLOT_ASYNCCAUSE,
-        JSSLOT_PARENT,
-        JSSLOT_PRINCIPALS,
-        JSSLOT_PRIVATE_PARENT,
-
-        // The total number of reserved slots in the SavedFrame class.
-        JSSLOT_COUNT
-    };
-
-    // Because we hash the parent pointer, we need to rekey a saved frame
-    // whenever its parent was relocated by the GC. However, the GC doesn't
-    // notify us when this occurs. As a work around, we keep a duplicate copy of
-    // the parent pointer as a private value in a reserved slot. Whenever the
-    // private value parent pointer doesn't match the regular parent pointer, we
-    // know that GC moved the parent and we need to update our private value and
-    // rekey the saved frame in its hash set. These two methods are helpers for
-    // this process.
-    bool parentMoved();
-    void updatePrivateParent();
-
-    static bool checkThis(JSContext* cx, CallArgs& args, const char* fnName,
-                          MutableHandleObject frame);
-};
-
-struct SavedFrame::HashPolicy
-{
-    typedef SavedFrame::Lookup               Lookup;
-    typedef PointerHasher<SavedFrame*, 3>   SavedFramePtrHasher;
-    typedef PointerHasher<JSPrincipals*, 3> JSPrincipalsPtrHasher;
-
-    static HashNumber hash(const Lookup& lookup);
-    static bool       match(SavedFrame* existing, const Lookup& lookup);
-
-    typedef ReadBarriered<SavedFrame*> Key;
-    static void rekey(Key& key, const Key& newKey);
-};
-
-// Assert that if the given object is not null, that it must be either a
-// SavedFrame object or wrapper (Xray or CCW) around a SavedFrame object.
-inline void AssertObjectIsSavedFrameOrWrapper(JSContext* cx, HandleObject stack);
-
 class SavedStacks {
     friend JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target);
 
   public:
     SavedStacks()
       : frames(),
         allocationSamplingProbability(1.0),
         allocationSkipCount(0),
@@ -304,25 +190,25 @@ class SavedStacks {
         }
 
         ~AutoReentrancyGuard()
         {
             stacks.creatingSavedFrame = false;
         }
     };
 
-    bool       insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFrame frame,
-                            unsigned maxFrameCount = 0);
-    bool       adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
-                               HandleString asyncCause,
-                               MutableHandleSavedFrame adoptedStack,
-                               unsigned maxFrameCount);
+    bool        insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFrame frame,
+                             unsigned maxFrameCount = 0);
+    bool        adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
+                                HandleString asyncCause,
+                                MutableHandleSavedFrame adoptedStack,
+                                unsigned maxFrameCount);
     SavedFrame* getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup lookup);
     SavedFrame* createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup);
-    void       chooseSamplingProbability(JSContext* cx);
+    void        chooseSamplingProbability(JSContext* cx);
 
     // Cache for memoizing PCToLineNumber lookups.
 
     struct PCKey {
         PCKey(JSScript* script, jsbytecode* pc) : script(script), pc(pc) { }
 
         PreBarrieredScript script;
         jsbytecode*        pc;
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -602,16 +602,37 @@ AbstractFramePtr::isDebuggerEvalFrame() 
         return asInterpreterFrame()->isDebuggerEvalFrame();
     if (isBaselineFrame())
         return asBaselineFrame()->isDebuggerEvalFrame();
     MOZ_ASSERT(isRematerializedFrame());
     return false;
 }
 
 inline bool
+AbstractFramePtr::hasCachedSavedFrame() const
+{
+    if (isInterpreterFrame())
+        return asInterpreterFrame()->hasCachedSavedFrame();
+    if (isBaselineFrame())
+        return asBaselineFrame()->hasCachedSavedFrame();
+    return asRematerializedFrame()->hasCachedSavedFrame();
+}
+
+inline void
+AbstractFramePtr::setHasCachedSavedFrame()
+{
+    if (isInterpreterFrame())
+        asInterpreterFrame()->setHasCachedSavedFrame();
+    else if (isBaselineFrame())
+        asBaselineFrame()->setHasCachedSavedFrame();
+    else
+        asRematerializedFrame()->setHasCachedSavedFrame();
+}
+
+inline bool
 AbstractFramePtr::isDebuggee() const
 {
     if (isInterpreterFrame())
         return asInterpreterFrame()->isDebuggee();
     if (isBaselineFrame())
         return asBaselineFrame()->isDebuggee();
     return asRematerializedFrame()->isDebuggee();
 }
@@ -861,16 +882,17 @@ AbstractFramePtr::popWith(JSContext* cx)
 
 Activation::Activation(JSContext* cx, Kind kind)
   : cx_(cx),
     compartment_(cx->compartment()),
     prev_(cx->runtime_->activation_),
     prevProfiling_(prev_ ? prev_->mostRecentProfiling() : nullptr),
     savedFrameChain_(0),
     hideScriptedCallerCount_(0),
+    frameCache_(cx),
     asyncStack_(cx, cx->runtime_->asyncStackForNewActivations),
     asyncCause_(cx, cx->runtime_->asyncCauseForNewActivations),
     asyncCallIsExplicit_(cx->runtime_->asyncCallIsExplicit),
     entryMonitor_(cx->runtime_->entryMonitor),
     kind_(kind)
 {
     cx->runtime_->asyncStackForNewActivations = nullptr;
     cx->runtime_->asyncCauseForNewActivations = nullptr;
@@ -907,16 +929,23 @@ Activation::isProfiling() const
 Activation*
 Activation::mostRecentProfiling()
 {
     if (isProfiling())
         return this;
     return prevProfiling_;
 }
 
+inline LiveSavedFrameCache*
+Activation::getLiveSavedFrameCache(JSContext* cx) {
+    if (!frameCache_.get().initialized() && !frameCache_.get().init(cx))
+        return nullptr;
+    return frameCache_.address();
+}
+
 InterpreterActivation::InterpreterActivation(RunState& state, JSContext* cx,
                                              InterpreterFrame* entryFrame)
   : Activation(cx, Interpreter),
     entryFrame_(entryFrame),
     opMask_(0)
 #ifdef DEBUG
   , oldFrameCount_(cx->runtime()->interpreterStack().frameCount_)
 #endif
@@ -984,11 +1013,41 @@ InterpreterActivation::resumeGeneratorFr
 }
 
 inline JSContext*
 AsmJSActivation::cx()
 {
     return cx_->asJSContext();
 }
 
+inline bool
+FrameIter::hasCachedSavedFrame() const
+{
+    if (isAsmJS())
+        return false;
+
+    if (hasUsableAbstractFramePtr())
+        return abstractFramePtr().hasCachedSavedFrame();
+
+    MOZ_ASSERT(data_.jitFrames_.isIonScripted());
+    // SavedFrame caching is done at the physical frame granularity (rather than
+    // for each inlined frame) for ion. Therefore, it is impossible to have a
+    // cached SavedFrame if this frame is not a physical frame.
+    return isPhysicalIonFrame() && data_.jitFrames_.current()->hasCachedSavedFrame();
+}
+
+inline void
+FrameIter::setHasCachedSavedFrame()
+{
+    MOZ_ASSERT(!isAsmJS());
+
+    if (hasUsableAbstractFramePtr()) {
+        abstractFramePtr().setHasCachedSavedFrame();
+        return;
+    }
+
+    MOZ_ASSERT(isPhysicalIonFrame());
+    data_.jitFrames_.current()->setHasCachedSavedFrame();
+}
+
 } /* namespace js */
 
 #endif /* vm_Stack_inl_h */
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -3,52 +3,62 @@
  * 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 vm_Stack_h
 #define vm_Stack_h
 
 #include "mozilla/Atomics.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Variant.h"
 
 #include "jsfun.h"
 #include "jsscript.h"
 #include "jsutil.h"
 
 #include "asmjs/AsmJSFrameIterator.h"
+#include "gc/Rooting.h"
 #include "jit/JitFrameIterator.h"
 #ifdef CHECK_OSIPOINT_REGISTERS
 #include "jit/Registers.h" // for RegisterDump
 #endif
+#include "js/RootingAPI.h"
+#include "vm/SavedFrame.h"
 
 struct JSCompartment;
 
 namespace JS {
 namespace dbg {
 class AutoEntryMonitor;
 } // namespace dbg
 } // namespace JS
 
 namespace js {
 
 class ArgumentsObject;
 class AsmJSModule;
 class InterpreterRegs;
 class CallObject;
+class FrameIter;
 class ScopeObject;
 class ScriptFrameIter;
 class SPSProfiler;
 class InterpreterFrame;
 class StaticBlockObject;
 
 class ScopeCoordinate;
 
 class SavedFrame;
 
+namespace jit {
+class CommonFrameLayout;
+}
+
 // VM stack layout
 //
 // A JSRuntime's stack consists of a linked list of activations. Every activation
 // contains a number of scripted frames that are either running in the interpreter
 // (InterpreterActivation) or JIT code (JitActivation). The frames inside a single
 // activation are contiguous: whenever C++ calls back into JS, a new activation is
 // pushed.
 //
@@ -191,16 +201,18 @@ class AbstractFramePtr
 
     inline JSCompartment* compartment() const;
 
     inline bool hasCallObj() const;
     inline bool isFunctionFrame() const;
     inline bool isGlobalFrame() const;
     inline bool isEvalFrame() const;
     inline bool isDebuggerEvalFrame() const;