Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 09 Feb 2017 17:06:59 -0800
changeset 388378 d362fcfd6f331119c081f2b22f77baee914b7a38
parent 388377 085735f8d2ffbdec699ed665c7908e0706a9ca89 (current diff)
parent 388352 25a94c1047e793ef096d8556fa3c26dd72bd37d7 (diff)
child 388379 cdd7f21530b2e6cf819ec42a80c47ef2f0bfff98
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to autoland, a=merge MozReview-Commit-ID: D76dMYwFRUQ
browser/config/tooltool-manifests/macosx64/cross-releng.manifest
dom/base/test/file_bug1263696_frame_fail.html
dom/base/test/file_bug1263696_frame_pass.html
dom/base/test/test_bug1263696.html
js/src/threading/posix/ConditionVariable.cpp
js/src/threading/posix/MutexImpl.cpp
js/src/threading/posix/MutexPlatformData.h
js/src/threading/windows/ConditionVariable.cpp
js/src/threading/windows/MutexImpl.cpp
js/src/threading/windows/MutexPlatformData.h
layout/reftests/forms/input/checkbox/checked-notref.html
layout/reftests/forms/input/checkbox/checked.html
layout/reftests/forms/input/radio/checked-notref.html
layout/reftests/forms/input/radio/checked.html
taskcluster/ci/toolchain/linux.yml
testing/web-platform/meta/XMLHttpRequest/XMLHttpRequest-withCredentials.any.js.ini
testing/web-platform/meta/console/console-is-a-namespace.any.js.ini
testing/web-platform/meta/html/browsers/history/the-location-interface/location-protocol-setter.html.ini
testing/web-platform/tests/service-workers/service-worker/resources/get-host-info.sub.js
toolkit/components/satchel/nsFormHistory.js
toolkit/components/satchel/nsIFormHistory.idl
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -564,35 +564,22 @@ var Output = {
         if (highlightBox) {
           highlightBox.classList.remove('show');
         }
         break;
       }
     }
   },
 
-  get androidBridge() {
-    delete this.androidBridge;
-    if (Utils.MozBuildApp === 'mobile/android') {
-      this.androidBridge = Services.androidBridge;
-    } else {
-      this.androidBridge = null;
-    }
-    return this.androidBridge;
-  },
-
   Android: function Android(aDetails, aBrowser) {
     const ANDROID_VIEW_TEXT_CHANGED = 0x10;
     const ANDROID_VIEW_TEXT_SELECTION_CHANGED = 0x2000;
 
-    if (!this.androidBridge) {
-      return;
-    }
-
     for (let androidEvent of aDetails) {
+      androidEvent.type = 'Accessibility:Event';
       if (androidEvent.bounds) {
         androidEvent.bounds = AccessFu.adjustContentBounds(
           androidEvent.bounds, aBrowser);
       }
 
       switch(androidEvent.eventType) {
         case ANDROID_VIEW_TEXT_CHANGED:
           androidEvent.brailleOutput = this.brailleState.adjustText(
@@ -602,19 +589,18 @@ var Output = {
           androidEvent.brailleOutput = this.brailleState.adjustSelection(
             androidEvent.brailleOutput);
           break;
         default:
           androidEvent.brailleOutput = this.brailleState.init(
             androidEvent.brailleOutput);
           break;
       }
-      let win = Utils.win;
-      let view = win && win.QueryInterface(Ci.nsIAndroidView);
-      view.dispatch('Accessibility:Event', androidEvent);
+
+      Utils.win.WindowEventDispatcher.sendRequest(androidEvent);
     }
   },
 
   Braille: function Braille(aDetails) {
     Logger.debug('Braille output: ' + aDetails.output);
   }
 };
 
@@ -800,19 +786,17 @@ var Input = {
             return;
           } else {
             target.blur();
           }
         }
 
         if (Utils.MozBuildApp == 'mobile/android') {
           // Return focus to native Android browser chrome.
-          let win = Utils.win;
-          let view = win && win.QueryInterface(Ci.nsIAndroidView);
-          view.dispatch('ToggleChrome:Focus');
+          Utils.win.WindowEventDispatcher.dispatch('ToggleChrome:Focus');
         }
         break;
       case aEvent.DOM_VK_RETURN:
         if (this.editState.editing) {
           return;
         }
         this.activateCurrent();
         break;
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -310,17 +310,17 @@ AndroidPresenter.prototype.pivotChanged 
   };
 
 AndroidPresenter.prototype.actionInvoked =
   function AndroidPresenter_actionInvoked(aObject, aActionName) {
     let state = Utils.getState(aObject);
 
     // Checkable objects use TalkBack's text derived from the event state,
     // so we don't populate the text here.
-    let text = '';
+    let text = null;
     if (!state.contains(States.CHECKABLE)) {
       text = Utils.localize(UtteranceGenerator.genForAction(aObject,
         aActionName));
     }
 
     return {
       type: this.type,
       details: [{
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -487,17 +487,16 @@
 @RESPATH@/components/nsContentDispatchChooser.manifest
 @RESPATH@/components/nsContentDispatchChooser.js
 @RESPATH@/components/nsHandlerService.manifest
 @RESPATH@/components/nsHandlerService.js
 @RESPATH@/components/nsWebHandlerApp.manifest
 @RESPATH@/components/nsWebHandlerApp.js
 @RESPATH@/components/satchel.manifest
 @RESPATH@/components/nsFormAutoComplete.js
-@RESPATH@/components/nsFormHistory.js
 @RESPATH@/components/FormHistoryStartup.js
 @RESPATH@/components/nsInputListAutoComplete.js
 @RESPATH@/components/formautofill.manifest
 @RESPATH@/components/FormAutofillContentService.js
 @RESPATH@/components/FormAutofillStartup.js
 @RESPATH@/components/contentAreaDropListener.manifest
 @RESPATH@/components/contentAreaDropListener.js
 @RESPATH@/components/messageWakeupService.js
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -2138,22 +2138,26 @@
       <versionRange maxVersion="3.0.5.99999999999999" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p944">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="13.0.0.302" minVersion="13.0.0.302" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1495" os="Linux">
+      <match exp="" name="name"/>
       <match exp="libflashplayer\.so" name="filename"/>
+      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="24.0.0.186" minVersion="23.0.0.207" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
-    <pluginItem blockID="p1420">
+    <pluginItem blockID="p1420" os="">
+      <match exp="" name="name"/>
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
+      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="23.0.0.205" minVersion="23.0.0.185" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p248">
       <match exp="Scorch\.plugin" name="filename"/>
       <versionRange maxVersion="6.2.0b88" minVersion="0" severity="1"/>
     </pluginItem>
     <pluginItem blockID="p1141">
@@ -2197,17 +2201,19 @@
         </targetApplication>
       </versionRange>
     </pluginItem>
     <pluginItem blockID="p102">
       <match exp="npmozax\.dll" name="filename"/>
       <versionRange maxVersion="*" minVersion="0"/>
     </pluginItem>
     <pluginItem blockID="p1419" os="Linux">
+      <match exp="" name="name"/>
       <match exp="libflashplayer\.so" name="filename"/>
+      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="11.2.202.643" minVersion="11.2.202.637" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p80">
       <match exp="\(TM\)" name="name"/>
       <match exp="(npjp2\.dll)|(libnpjp2\.so)" name="filename"/>
       <match exp="[^\d\._]((0(\.\d+(\.\d+([_\.]\d+)?)?)?)|(1\.(([0-5](\.\d+([_\.]\d+)?)?)|(6(\.0([_\.](0?\d|1\d|2\d|30))?)?)|(7(\.0([_\.][0-2])?)?))))([^\d\._]|$)" name="description"/>
       <versionRange severity="1"/>
@@ -2454,18 +2460,20 @@
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="11.2.202.548" minVersion="11.2.202.540" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1026">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="18.0.0.232" minVersion="18.0.0.204" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
-    <pluginItem blockID="p1274">
+    <pluginItem blockID="p1274" os="">
+      <match exp="" name="name"/>
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
+      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="22.0.0.211" minVersion="22.0.0.192" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p422">
       <match exp="JavaAppletPlugin\.plugin" name="filename"/>
       <versionRange maxVersion="Java 7 Update 24" minVersion="Java 7 Update 16" severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="17.0"/>
@@ -2566,18 +2574,20 @@
     <pluginItem blockID="p34">
       <match exp="[Nn][Pp][Jj][Pp][Ii]1[56]0_[0-9]+\.[Dd][Ll][Ll]" name="filename"/>
       <versionRange>
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="*" minVersion="3.6a1pre"/>
         </targetApplication>
       </versionRange>
     </pluginItem>
-    <pluginItem blockID="p1494">
+    <pluginItem blockID="p1494" os="">
+      <match exp="" name="name"/>
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
+      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="24.0.0.186" minVersion="23.0.0.207" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p798">
       <match exp="(NPSWF32.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="15.0.0.242" minVersion="14.0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
@@ -2635,18 +2645,20 @@
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="21.0.0.197" minVersion="20.0.0.306" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p1235">
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="18.0.0.352" minVersion="18.0.0.343" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
-    <pluginItem blockID="p160">
+    <pluginItem blockID="p160" os="">
+      <match exp="" name="name"/>
       <match exp="NPSWF32\.dll" name="filename"/>
+      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="10.2.9999" minVersion="0" severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
           <versionRange maxVersion="16.*" minVersion="4.0"/>
         </targetApplication>
       </versionRange>
     </pluginItem>
     <pluginItem blockID="p129">
@@ -2805,17 +2817,19 @@
     <pluginItem blockID="p574">
       <match exp="NPDjVu\.plugin" name="filename"/>
       <versionRange maxVersion="6.1.1" minVersion="0" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p28">
       <match exp="NPFFAddOn.dll" name="filename"/>
     </pluginItem>
     <pluginItem blockID="p1421" os="Linux">
+      <match exp="" name="name"/>
       <match exp="libflashplayer\.so" name="filename"/>
+      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="23.0.0.207" minVersion="11.2.202.643" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p302">
       <match exp="Java\(TM\) Plug-in 1\.6\.0_(39|40|41)([^\d\._]|$)" name="name"/>
       <match exp="libnpjp2\.so" name="filename"/>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -3001,18 +3015,20 @@
       <match exp="npuplaypc\.dll" name="filename"/>
       <versionRange maxVersion="1.0.0.0" minVersion="0" severity="1"/>
     </pluginItem>
     <pluginItem blockID="p1247">
       <match exp="(nppdf32\.dll)|(AdobePDFViewerNPAPI\.plugin)" name="filename"/>
       <infoURL>https://get.adobe.com/reader</infoURL>
       <versionRange maxVersion="15.006.30174" minVersion="15.006.30174" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
-    <pluginItem blockID="p1422">
+    <pluginItem blockID="p1422" os="">
+      <match exp="" name="name"/>
       <match exp="(NPSWF32.*\.dll)|(NPSWF64.*\.dll)|(Flash\ Player\.plugin)" name="filename"/>
+      <match exp="" name="description"/>
       <infoURL>https://get.adobe.com/flashplayer/</infoURL>
       <versionRange maxVersion="23.0.0.207" minVersion="23.0.0.205" severity="0" vulnerabilitystatus="1"/>
     </pluginItem>
     <pluginItem blockID="p418">
       <match exp="Java\(TM\) Plug-in 1\.7\.0_(1[6-9]|2[0-4])([^\d\._]|$)" name="name"/>
       <match exp="libnpjp2\.so" name="filename"/>
       <versionRange severity="0" vulnerabilitystatus="1">
         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -1,14 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-// This test checks that a <select> with an <optgroup> opens and can be navigated
-// in a child process. This is different than single-process as a <menulist> is used
-// to implement the dropdown list.
+// This test tests <select> in a child process. This is different than
+// single-process as a <menulist> is used to implement the dropdown list.
 
 requestLongerTimeout(2);
 
 const XHTML_DTD = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
 
 const PAGECONTENT =
   "<html xmlns='http://www.w3.org/1999/xhtml'>" +
   "<body onload='gChangeEvents = 0;gInputEvents = 0; gClickEvents = 0; document.body.firstChild.focus()'>" +
@@ -846,8 +845,40 @@ add_task(function* test_colors_applied_t
     testOptionColors(idx, child, menulist);
     idx++;
     child = child.nextSibling;
   }
 
   yield hideSelectPopup(selectPopup, "escape");
   yield BrowserTestUtils.removeTab(tab);
 });
+
+// This test checks that the popup is closed when the select element is blurred.
+add_task(function* test_blur_hides_popup() {
+  const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+  yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
+    content.addEventListener("blur", function(event) {
+      event.preventDefault();
+      event.stopPropagation();
+    }, true);
+
+    content.document.getElementById("one").focus();
+  });
+
+  let menulist = document.getElementById("ContentSelectDropdown");
+  let selectPopup = menulist.menupopup;
+
+  yield openSelectPopup(selectPopup);
+
+  let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
+
+  yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
+    content.document.getElementById("one").blur();
+  });
+
+  yield popupHiddenPromise;
+
+  ok(true, "Blur closed popup");
+
+  yield BrowserTestUtils.removeTab(tab);
+});
--- a/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-releng.manifest
@@ -28,19 +28,20 @@
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "sccache2.tar.xz",
 "unpack": true,
 "digest": "b89c40dbf28c2bd54fadf017c15a8789f6e7611252a623cc3a1507e3dd6fc9e5a50d746e81776ba856e33fdc99b4a6413ba7c3ac0aed5f4835705da2b758ef22",
 "size": 1020700
 },
 {
-"size": 57060,
+"version": "https://github.com/andreas56/libdmg-hfsplus rev 81dd75fd1549b24bf8af9736ac25518b367e6b63",
+"size": 62032,
 "visibility": "public",
-"digest": "9649ca595f4cf088d118da26201f92cc94cda7af49c7c48112ee31cd13c83b2935b3e145de9dd78060cff2480b4c2e7ff5fb24235876956fed13c87852071998",
+"digest": "9073c41034784eb8823ec817aed42bbc65c8da63ad3fac572726fa48b36320ee302ca8f51b23576e7fdbeec6ab300610d0c58bbd9c52024577dfdb13d95aa2ec",
 "algorithm": "sha512",
 "unpack": true,
 "filename": "dmg.tar.xz"
 },
 {
 "version": "rustc 1.15.0 (10893a9a3 2017-01-19) repack",
 "size": 140312816,
 "digest": "9b464eae8e8ae0020609b019ce10407eabb65580b2bf97fd1719dee15d6211fa61cb8bf7671efb415515256eaa0c5e48270f74d322e0b9105ee56b61a0de4935",
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -460,17 +460,16 @@
 @RESPATH@/components/nsContentDispatchChooser.manifest
 @RESPATH@/components/nsContentDispatchChooser.js
 @RESPATH@/components/nsHandlerService.manifest
 @RESPATH@/components/nsHandlerService.js
 @RESPATH@/components/nsWebHandlerApp.manifest
 @RESPATH@/components/nsWebHandlerApp.js
 @RESPATH@/components/satchel.manifest
 @RESPATH@/components/nsFormAutoComplete.js
-@RESPATH@/components/nsFormHistory.js
 @RESPATH@/components/FormHistoryStartup.js
 @RESPATH@/components/nsInputListAutoComplete.js
 @RESPATH@/components/formautofill.manifest
 @RESPATH@/components/FormAutofillContentService.js
 @RESPATH@/components/FormAutofillStartup.js
 @RESPATH@/components/contentAreaDropListener.manifest
 @RESPATH@/components/contentAreaDropListener.js
 @RESPATH@/browser/components/BrowserProfileMigrators.manifest
--- a/devtools/client/shared/widgets/ColorWidget.js
+++ b/devtools/client/shared/widgets/ColorWidget.js
@@ -5,16 +5,18 @@
 /**
  * This file is a new working copy of Spectrum.js for the purposes of refreshing the color
  * widget. It is hidden behind a pref("devtools.inspector.colorWidget.enabled").
  */
 
 "use strict";
 
 const EventEmitter = require("devtools/shared/event-emitter");
+const {colorUtils} = require("devtools/shared/css/color");
+
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
 /**
  * ColorWidget creates a color picker widget in any container you give it.
  *
  * Simple usage example:
  *
  * const {ColorWidget} = require("devtools/client/shared/widgets/ColorWidget");
@@ -58,18 +60,45 @@ function ColorWidget(parentEl, rgb) {
         </div>
       </div>
     </div>
     <div class="colorwidget-alpha colorwidget-checker colorwidget-box">
       <div class="colorwidget-alpha-inner">
         <div class="colorwidget-alpha-handle colorwidget-slider-control"></div>
       </div>
     </div>
+    <div class="colorwidget-value">
+      <select class="colorwidget-select">
+        <option value="hex">Hex</option>
+        <option value="rgba">RGBA</option>
+        <option value="hsla">HSLA</option>
+      </select>
+      <div class="colorwidget-hex">
+        <input class="colorwidget-hex-input"/>
+      </div>
+      <div class="colorwidget-rgba colorwidget-hidden">
+        <input class="colorwidget-rgba-r" data-id="r" />
+        <input class="colorwidget-rgba-g" data-id="g" />
+        <input class="colorwidget-rgba-b" data-id="b" />
+        <input class="colorwidget-rgba-a" data-id="a" />
+      </div>
+      <div class="colorwidget-hsla colorwidget-hidden">
+        <input class="colorwidget-hsla-h" data-id="h" />
+        <input class="colorwidget-hsla-s" data-id="s" />
+        <input class="colorwidget-hsla-l" data-id="l" />
+        <input class="colorwidget-hsla-a" data-id="a" />
+      </div>
+    </div>
   `;
 
+  this.onSelectValueChange = this.onSelectValueChange.bind(this);
+  this.onHexInputChange = this.onHexInputChange.bind(this);
+  this.onRgbaInputChange = this.onRgbaInputChange.bind(this);
+  this.onHslaInputChange = this.onHslaInputChange.bind(this);
+
   this.onElementClick = this.onElementClick.bind(this);
   this.element.addEventListener("click", this.onElementClick);
 
   this.parentEl.appendChild(this.element);
 
   this.slider = this.element.querySelector(".colorwidget-hue");
   this.slideHelper = this.element.querySelector(".colorwidget-slider");
   ColorWidget.draggable(this.slider, this.onSliderMove.bind(this));
@@ -78,16 +107,41 @@ function ColorWidget(parentEl, rgb) {
   this.dragHelper = this.element.querySelector(".colorwidget-dragger");
   ColorWidget.draggable(this.dragger, this.onDraggerMove.bind(this));
 
   this.alphaSlider = this.element.querySelector(".colorwidget-alpha");
   this.alphaSliderInner = this.element.querySelector(".colorwidget-alpha-inner");
   this.alphaSliderHelper = this.element.querySelector(".colorwidget-alpha-handle");
   ColorWidget.draggable(this.alphaSliderInner, this.onAlphaSliderMove.bind(this));
 
+  this.colorSelect = this.element.querySelector(".colorwidget-select");
+  this.colorSelect.addEventListener("change", this.onSelectValueChange);
+
+  this.hexValue = this.element.querySelector(".colorwidget-hex");
+  this.hexValueInput = this.element.querySelector(".colorwidget-hex-input");
+  this.hexValueInput.addEventListener("input", this.onHexInputChange);
+
+  this.rgbaValue = this.element.querySelector(".colorwidget-rgba");
+  this.rgbaValueInputs = {
+    r: this.element.querySelector(".colorwidget-rgba-r"),
+    g: this.element.querySelector(".colorwidget-rgba-g"),
+    b: this.element.querySelector(".colorwidget-rgba-b"),
+    a: this.element.querySelector(".colorwidget-rgba-a"),
+  };
+  this.rgbaValue.addEventListener("input", this.onRgbaInputChange);
+
+  this.hslaValue = this.element.querySelector(".colorwidget-hsla");
+  this.hslaValueInputs = {
+    h: this.element.querySelector(".colorwidget-hsla-h"),
+    s: this.element.querySelector(".colorwidget-hsla-s"),
+    l: this.element.querySelector(".colorwidget-hsla-l"),
+    a: this.element.querySelector(".colorwidget-hsla-a"),
+  };
+  this.hslaValue.addEventListener("input", this.onHslaInputChange);
+
   if (rgb) {
     this.rgb = rgb;
     this.updateUI();
   }
 }
 
 module.exports.ColorWidget = ColorWidget;
 
@@ -132,16 +186,20 @@ ColorWidget.rgbToHsv = function (r, g, b
       case g: h = (b - r) / d + 2; break;
       case b: h = (r - g) / d + 4; break;
     }
     h /= 6;
   }
   return [h, s, v, a];
 };
 
+ColorWidget.hslToCssString = function (h, s, l, a) {
+  return `hsla(${h}, ${s}%, ${l}%, ${a})`;
+};
+
 ColorWidget.draggable = function (element, onmove, onstart, onstop) {
   onmove = onmove || function () {};
   onstart = onstart || function () {};
   onstop = onstop || function () {};
 
   let doc = element.ownerDocument;
   let dragging = false;
   let offset = {};
@@ -205,16 +263,19 @@ ColorWidget.draggable = function (elemen
   }
 
   element.addEventListener("mousedown", start);
 };
 
 ColorWidget.prototype = {
   set rgb(color) {
     this.hsv = ColorWidget.rgbToHsv(color[0], color[1], color[2], color[3]);
+
+    let { h, s, l } = new colorUtils.CssColor(this.rgbCssString)._getHSLATuple();
+    this.hsl = [h, s, l, color[3]];
   },
 
   get rgb() {
     let rgb = ColorWidget.hsvToRgb(this.hsv[0], this.hsv[1], this.hsv[2],
       this.hsv[3]);
     return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]),
             Math.round(rgb[3] * 100) / 100];
   },
@@ -245,29 +306,148 @@ ColorWidget.prototype = {
   },
 
   onElementClick: function (e) {
     e.stopPropagation();
   },
 
   onSliderMove: function (dragX, dragY) {
     this.hsv[0] = (dragY / this.slideHeight);
+    this.hsl[0] = (dragY / this.slideHeight) * 360;
     this.updateUI();
     this.onChange();
   },
 
   onDraggerMove: function (dragX, dragY) {
     this.hsv[1] = dragX / this.dragWidth;
     this.hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
+
+    this.hsl[2] = ((2 - this.hsv[1]) * this.hsv[2] / 2);
+    if (this.hsl[2] && this.hsl[2] < 1) {
+      this.hsl[1] = this.hsv[1] * this.hsv[2] /
+          (this.hsl[2] < 0.5 ? this.hsl[2] * 2 : 2 - this.hsl[2] * 2);
+      this.hsl[1] = this.hsl[1] * 100;
+    }
+    this.hsl[2] = this.hsl[2] * 100;
+
     this.updateUI();
     this.onChange();
   },
 
   onAlphaSliderMove: function (dragX, dragY) {
     this.hsv[3] = dragX / this.alphaSliderWidth;
+    this.hsl[3] = dragX / this.alphaSliderWidth;
+    this.updateUI();
+    this.onChange();
+  },
+
+  onSelectValueChange: function (event) {
+    const selection = event.target.value;
+    this.colorSelect.classList.remove("colorwidget-select-spacing");
+    this.hexValue.classList.add("colorwidget-hidden");
+    this.rgbaValue.classList.add("colorwidget-hidden");
+    this.hslaValue.classList.add("colorwidget-hidden");
+
+    switch (selection) {
+      case "hex":
+        this.hexValue.classList.remove("colorwidget-hidden");
+        break;
+      case "rgba":
+        this.colorSelect.classList.add("colorwidget-select-spacing");
+        this.rgbaValue.classList.remove("colorwidget-hidden");
+        break;
+      case "hsla":
+        this.colorSelect.classList.add("colorwidget-select-spacing");
+        this.hslaValue.classList.remove("colorwidget-hidden");
+        break;
+    }
+  },
+
+  onHexInputChange: function (event) {
+    const hex = event.target.value;
+    const color = new colorUtils.CssColor(hex, true);
+    if (!color.rgba) {
+      return;
+    }
+
+    const { r, g, b, a } = color._getRGBATuple();
+    this.rgb = [r, g, b, a];
+    this.updateUI();
+    this.onChange();
+  },
+
+  onRgbaInputChange: function (event) {
+    const field = event.target.dataset.id;
+    const value = event.target.value.toString();
+    if (!value || isNaN(value) || value.endsWith(".")) {
+      return;
+    }
+
+    let rgb = this.rgb;
+
+    switch (field) {
+      case "r":
+        rgb[0] = value;
+        break;
+      case "g":
+        rgb[1] = value;
+        break;
+      case "b":
+        rgb[2] = value;
+        break;
+      case "a":
+        rgb[3] = Math.min(value, 1);
+        break;
+    }
+
+    this.rgb = rgb;
+
+    this.updateUI();
+    this.onChange();
+  },
+
+  onHslaInputChange: function (event) {
+    const field = event.target.dataset.id;
+    let value = event.target.value.toString();
+    if ((field === "s" || field === "l") && !value.endsWith("%")) {
+      return;
+    }
+
+    if (value.endsWith("%")) {
+      value = value.substring(0, value.length - 1);
+    }
+
+    if (!value || isNaN(value) || value.endsWith(".")) {
+      return;
+    }
+
+    const hsl = this.hsl;
+
+    switch (field) {
+      case "h":
+        hsl[0] = value;
+        break;
+      case "s":
+        hsl[1] = value;
+        break;
+      case "l":
+        hsl[2] = value;
+        break;
+      case "a":
+        hsl[3] = Math.min(value, 1);
+        break;
+    }
+
+    const cssString = ColorWidget.hslToCssString(hsl[0], hsl[1], hsl[2], hsl[3]);
+    const { r, g, b, a } = new colorUtils.CssColor(cssString)._getRGBATuple();
+
+    this.rgb = [r, g, b, a];
+
+    this.hsl = hsl;
+
     this.updateUI();
     this.onChange();
   },
 
   onChange: function () {
     this.emit("changed", this.rgb, this.rgbCssString);
   },
 
@@ -302,16 +482,34 @@ ColorWidget.prototype = {
     // Placing the hue slider
     let slideY = (h * this.slideHeight) - this.slideHelperHeight / 2;
     this.slideHelper.style.top = slideY + "px";
 
     // Placing the alpha slider
     let alphaSliderX = (this.hsv[3] * this.alphaSliderWidth) -
       (this.alphaSliderHelperWidth / 2);
     this.alphaSliderHelper.style.left = alphaSliderX + "px";
+
+    const color = new colorUtils.CssColor(this.rgbCssString);
+
+    // Updating the hex field
+    this.hexValueInput.value = color.hex;
+
+    // Updating the RGBA fields
+    const rgb = this.rgb;
+    this.rgbaValueInputs.r.value = rgb[0];
+    this.rgbaValueInputs.g.value = rgb[1];
+    this.rgbaValueInputs.b.value = rgb[2];
+    this.rgbaValueInputs.a.value = parseFloat(rgb[3].toFixed(1));
+
+    // Updating the HSLA fields
+    this.hslaValueInputs.h.value = this.hsl[0];
+    this.hslaValueInputs.s.value = this.hsl[1] + "%";
+    this.hslaValueInputs.l.value = this.hsl[2] + "%";
+    this.hslaValueInputs.a.value = parseFloat(this.hsl[3].toFixed(1));
   },
 
   updateUI: function () {
     this.updateHelperLocations();
 
     let rgb = this.rgb;
     let rgbNoSatVal = this.rgbNoSatVal;
 
--- a/devtools/client/shared/widgets/color-widget.css
+++ b/devtools/client/shared/widgets/color-widget.css
@@ -148,8 +148,64 @@ http://www.briangrinstead.com/blog/keep-
 
 .colorwidget-slider {
   position: absolute;
   top: 0;
   height: 5px;
   left: -3px;
   right: -3px;
 }
+
+/**
+ * Color Widget Editor
+ */
+
+.colorwidget-value {
+  position: relative;
+  margin-top: 8px;
+}
+
+/**
+ * Color Widget Select
+ */
+
+.colorwidget-select {
+  width: 100%;
+}
+
+.colorwidget-select-spacing {
+  letter-spacing: 40px;
+}
+
+.colorwidget-select-spacing option {
+  letter-spacing: initial;
+}
+
+/**
+ * Color Widget Inputs
+ */
+
+.colorwidget-hidden {
+  display: none;
+}
+
+.colorwidget-hex,
+.colorwidget-rgba,
+.colorwidget-hsla {
+  width: 200px;
+  font-size: 0;
+}
+
+.colorwidget-hex-input {
+  width: 192px;
+}
+
+.colorwidget-rgba-r,
+.colorwidget-rgba-g,
+.colorwidget-rgba-b,
+.colorwidget-rgba-a,
+.colorwidget-hsla-h,
+.colorwidget-hsla-s,
+.colorwidget-hsla-l,
+.colorwidget-hsla-a {
+  width: 42px;
+  margin: 0;
+}
\ No newline at end of file
--- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
@@ -69,23 +69,23 @@ SwatchColorPickerTooltip.prototype = Her
     eyedropper.id = "eyedropper-button";
     eyedropper.className = "devtools-button";
     /* pointerEvents for eyedropper has to be set auto to display tooltip when
      * eyedropper is disabled in non-HTML documents.
      */
     eyedropper.style.pointerEvents = "auto";
     container.appendChild(eyedropper);
 
-    this.tooltip.setContent(container, { width: 218, height: 224 });
-
     let spectrum;
     if (NEW_COLOR_WIDGET) {
+      this.tooltip.setContent(container, { width: 218, height: 271 });
       const {ColorWidget} = require("devtools/client/shared/widgets/ColorWidget");
       spectrum = new ColorWidget(spectrumNode, color);
     } else {
+      this.tooltip.setContent(container, { width: 218, height: 224 });
       spectrum = new Spectrum(spectrumNode, color);
     }
 
     // Wait for the tooltip to be shown before calling spectrum.show
     // as it expect to be visible in order to compute DOM element sizes.
     this.tooltip.once("shown", () => {
       spectrum.show();
     });
--- a/devtools/shared/css/color.js
+++ b/devtools/shared/css/color.js
@@ -403,16 +403,33 @@ CssColor.prototype = {
   _getRGBATuple: function () {
     let tuple = colorToRGBA(this.authored, this.cssColor4);
 
     tuple.a = parseFloat(tuple.a.toFixed(1));
 
     return tuple;
   },
 
+  /**
+   * Returns a HSLA 4-Tuple representation of a color or transparent as
+   * appropriate.
+   */
+  _getHSLATuple: function () {
+    let {r, g, b, a} = colorToRGBA(this.authored, this.cssColor4);
+
+    let [h, s, l] = rgbToHsl([r, g, b]);
+
+    return {
+      h,
+      s,
+      l,
+      a: parseFloat(a.toFixed(1))
+    };
+  },
+
   _hsl: function (maybeAlpha) {
     if (this.lowerCased.startsWith("hsl(") && maybeAlpha === undefined) {
       // We can use it as-is.
       return this.authored;
     }
 
     let {r, g, b} = this._getRGBATuple();
     let [h, s, l] = rgbToHsl([r, g, b]);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -12602,17 +12602,17 @@ nsDocShell::LoadHistoryEntry(nsISHEntry*
   } else {
     srcdoc = NullString();
   }
 
   // If there is no valid triggeringPrincipal, we deny the load
   MOZ_ASSERT(triggeringPrincipal,
              "need a valid triggeringPrincipal to load from history");
   if (!triggeringPrincipal) {
-    return NS_ERROR_FAILURE;
+    triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
   }
 
   // Passing nullptr as aSourceDocShell gives the same behaviour as before
   // aSourceDocShell was introduced. According to spec we should be passing
   // the source browsing context that was used when the history entry was
   // first created. bug 947716 has been created to address this issue.
   rv = InternalLoad(uri,
                     originalURI,
--- a/dom/animation/AnimationUtils.cpp
+++ b/dom/animation/AnimationUtils.cpp
@@ -8,16 +8,18 @@
 
 #include "nsDebug.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsGlobalWindow.h"
 #include "nsString.h"
 #include "xpcpublic.h" // For xpc::NativeGlobal
+#include "mozilla/EffectSet.h"
+#include "mozilla/dom/KeyframeEffectReadOnly.h"
 #include "mozilla/Preferences.h"
 
 namespace mozilla {
 
 /* static */ void
 AnimationUtils::LogAsyncAnimationFailure(nsCString& aMessage,
                                          const nsIContent* aContent)
 {
@@ -78,9 +80,22 @@ AnimationUtils::IsCoreAPIEnabled()
 }
 
 /* static */ bool
 AnimationUtils::IsCoreAPIEnabledForCaller(dom::CallerType aCallerType)
 {
   return IsCoreAPIEnabled() || aCallerType == dom::CallerType::System;
 }
 
+/* static */ bool
+AnimationUtils::EffectSetContainsAnimatedScale(EffectSet& aEffects,
+                                               const nsIFrame* aFrame)
+{
+  for (const dom::KeyframeEffectReadOnly* effect : aEffects) {
+    if (effect->ContainsAnimatedScale(aFrame)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 } // namespace mozilla
--- a/dom/animation/AnimationUtils.h
+++ b/dom/animation/AnimationUtils.h
@@ -9,21 +9,23 @@
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsStringFwd.h"
 
 class nsIContent;
 class nsIDocument;
+class nsIFrame;
 struct JSContext;
 
 namespace mozilla {
 
 class ComputedTimingFunction;
+class EffectSet;
 
 class AnimationUtils
 {
 public:
   static dom::Nullable<double>
   TimeDurationToDouble(const dom::Nullable<TimeDuration>& aTime)
   {
     dom::Nullable<double> result;
@@ -68,13 +70,20 @@ public:
    */
   static bool IsCoreAPIEnabled();
 
   /**
    * Returns true if the preference to enable the core Web Animations API is
    * true or the caller is chrome.
    */
   static bool IsCoreAPIEnabledForCaller(dom::CallerType aCallerType);
+
+  /**
+   * Returns true if the given EffectSet contains a current effect that animates
+   * scale. |aFrame| is used for calculation of scale values.
+   */
+  static bool EffectSetContainsAnimatedScale(EffectSet& aEffects,
+                                             const nsIFrame* aFrame);
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -1758,10 +1758,59 @@ KeyframeEffectReadOnly::NeedsBaseStyle(n
     }
   }
   MOZ_ASSERT_UNREACHABLE(
     "Expected a property that can be run on the compositor");
 
   return false;
 }
 
+bool
+KeyframeEffectReadOnly::ContainsAnimatedScale(const nsIFrame* aFrame) const
+{
+  if (!IsCurrent()) {
+    return false;
+  }
+
+  for (const AnimationProperty& prop : mProperties) {
+    if (prop.mProperty != eCSSProperty_transform) {
+      continue;
+    }
+
+    if (NeedsBaseStyle(prop.mProperty)) {
+      StyleAnimationValue baseStyle =
+        EffectCompositor::GetBaseStyle(prop.mProperty, aFrame);
+      MOZ_ASSERT(!baseStyle.IsNull(), "The base value should be set");
+      if (baseStyle.IsNull()) {
+        // If we failed to get the base style, we consider it has scale value
+        // here for the safety.
+        return true;
+      }
+      gfxSize size = baseStyle.GetScaleValue(aFrame);
+      if (size != gfxSize(1.0f, 1.0f)) {
+        return true;
+      }
+    }
+
+    // This is actually overestimate because there are some cases that combining
+    // the base value and from/to value produces 1:1 scale. But it doesn't
+    // really matter.
+    for (const AnimationPropertySegment& segment : prop.mSegments) {
+      if (!segment.mFromValue.IsNull()) {
+        gfxSize from = segment.mFromValue.GetScaleValue(aFrame);
+        if (from != gfxSize(1.0f, 1.0f)) {
+          return true;
+        }
+      }
+      if (!segment.mToValue.IsNull()) {
+        gfxSize to = segment.mToValue.GetScaleValue(aFrame);
+        if (to != gfxSize(1.0f, 1.0f)) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffectReadOnly.h
+++ b/dom/animation/KeyframeEffectReadOnly.h
@@ -283,16 +283,20 @@ public:
   // See nsChangeHint_Hints_CanIgnoreIfNotVisible in nsChangeHint.h
   // in detail which change hint can be ignored.
   bool CanIgnoreIfNotVisible() const;
 
   // Returns true if the effect is run on the compositor for |aProperty| and
   // needs a base style to composite with.
   bool NeedsBaseStyle(nsCSSPropertyID aProperty) const;
 
+  // Returns true if the effect is current state and has scale animation.
+  // |aFrame| is used for calculation of scale values.
+  bool ContainsAnimatedScale(const nsIFrame* aFrame) const;
+
 protected:
   KeyframeEffectReadOnly(nsIDocument* aDocument,
                          const Maybe<OwningAnimationTarget>& aTarget,
                          AnimationEffectTimingReadOnly* aTiming,
                          const KeyframeEffectParams& aOptions);
 
   ~KeyframeEffectReadOnly() override = default;
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -39,16 +39,17 @@
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/Nullable.h"
 #include "Units.h"
 #include "DOMIntersectionObserver.h"
 
 class nsIFrame;
 class nsIDOMMozNamedAttrMap;
+class nsIMozBrowserFrame;
 class nsIURI;
 class nsIScrollableFrame;
 class nsAttrValueOrString;
 class nsContentList;
 class nsDOMTokenList;
 struct nsRect;
 class nsFocusManager;
 class nsGlobalWindow;
@@ -335,16 +336,26 @@ public:
   virtual bool IsLabelable() const;
 
   /**
    * Returns if the element is interactive content as per HTML specification.
    */
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const;
 
   /**
+   * Returns |this| as an nsIMozBrowserFrame* if the element is a frame or
+   * iframe element.
+   *
+   * We have this method, rather than using QI, so that we can use it during
+   * the servo traversal, where we can't QI DOM nodes because of non-thread-safe
+   * refcounts.
+   */
+  virtual nsIMozBrowserFrame* GetAsMozBrowserFrame() { return nullptr; }
+
+  /**
    * Is the attribute named stored in the mapped attributes?
    *
    * // XXXbz we use this method in HasAttributeDependentStyle, so svg
    *    returns true here even though it stores nothing in the mapped
    *    attributes.
    */
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
--- a/dom/base/Location.cpp
+++ b/dom/base/Location.cpp
@@ -28,16 +28,17 @@
 #include "nsITextToSubURI.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/Likely.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsNullPrincipal.h"
 #include "ScriptSettings.h"
+#include "mozilla/Unused.h"
 #include "mozilla/dom/LocationBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 static nsresult
 GetDocumentCharacterSetForURI(const nsAString& aHref, nsACString& aCharset)
 {
@@ -694,31 +695,59 @@ NS_IMETHODIMP
 Location::SetProtocol(const nsAString& aProtocol)
 {
   nsCOMPtr<nsIURI> uri;
   nsresult rv = GetWritableURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv) || !uri)) {
     return rv;
   }
 
-  rv = uri->SetScheme(NS_ConvertUTF16toUTF8(aProtocol));
+  nsAString::const_iterator start, end;
+  aProtocol.BeginReading(start);
+  aProtocol.EndReading(end);
+  nsAString::const_iterator iter(start);
+  Unused << FindCharInReadable(':', iter, end);
+
+  rv = uri->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+    // Oh, I wish nsStandardURL returned NS_ERROR_MALFORMED_URI for _all_ the
+    // malformed cases, not just some of them!
+    return NS_ERROR_DOM_SYNTAX_ERR;
   }
   nsAutoCString newSpec;
   rv = uri->GetSpec(newSpec);
   if (NS_FAILED(rv)) {
     return rv;
   }
   // We may want a new URI class for the new URI, so recreate it:
   rv = NS_NewURI(getter_AddRefs(uri), newSpec);
   if (NS_FAILED(rv)) {
+    if (rv == NS_ERROR_MALFORMED_URI) {
+      rv = NS_ERROR_DOM_SYNTAX_ERR;
+    }
     return rv;
   }
 
+  bool isHttp;
+  rv = uri->SchemeIs("http", &isHttp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool isHttps;
+  rv = uri->SchemeIs("https", &isHttps);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!isHttp && !isHttps) {
+    // No-op, per spec.
+    return NS_OK;
+  }
+
   return SetURI(uri);
 }
 
 NS_IMETHODIMP
 Location::GetSearch(nsAString& aSearch)
 {
   aSearch.SetLength(0);
 
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1224,17 +1224,17 @@ BeaconStreamListener::OnDataAvailable(ns
                                       uint32_t count)
 {
   MOZ_ASSERT(false);
   return NS_OK;
 }
 
 bool
 Navigator::SendBeacon(const nsAString& aUrl,
-                      const Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& aData,
+                      const Nullable<fetch::BodyInit>& aData,
                       ErrorResult& aRv)
 {
   if (aData.IsNull()) {
     return SendBeaconInternal(aUrl, nullptr, eBeaconTypeOther, aRv);
   }
 
   if (aData.Value().IsArrayBuffer()) {
     BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer());
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -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/. */
 
 #ifndef mozilla_dom_Navigator_h
 #define mozilla_dom_Navigator_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIDOMNavigator.h"
 #include "nsIMozNavigatorNetwork.h"
 #include "nsWrapperCache.h"
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 #include "nsString.h"
@@ -221,17 +222,17 @@ public:
 #endif // MOZ_TIME_MANAGER
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
 #endif // MOZ_AUDIO_CHANNEL_MANAGER
 
   Presentation* GetPresentation(ErrorResult& aRv);
 
   bool SendBeacon(const nsAString& aUrl,
-                  const Nullable<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& aData,
+                  const Nullable<fetch::BodyInit>& aData,
                   ErrorResult& aRv);
 
   void MozGetUserMedia(const MediaStreamConstraints& aConstraints,
                        NavigatorUserMediaSuccessCallback& aOnSuccess,
                        NavigatorUserMediaErrorCallback& aOnError,
                        CallerType aCallerType,
                        ErrorResult& aRv);
   void MozGetUserMediaDevices(const MediaStreamConstraints& aConstraints,
--- a/dom/base/nsAttrValue.cpp
+++ b/dom/base/nsAttrValue.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/HashFunctions.h"
 
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
 #include "nsIAtom.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoBindingTypes.h"
+#include "mozilla/ServoStyleSet.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "prprf.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "nsCSSParser.h"
 #include "nsStyledElement.h"
 #include "nsIURI.h"
@@ -642,17 +643,25 @@ nsAttrValue::ToString(nsAString& aResult
     }
     case eCSSDeclaration:
     {
       aResult.Truncate();
       MiscContainer *container = GetMiscContainer();
       if (DeclarationBlock* decl = container->mValue.mCSSDeclaration) {
         decl->ToString(aResult);
       }
-      const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
+
+      // We can reach this during parallel style traversal. If that happens,
+      // don't cache the string. The TLS overhead should't hurt us here, since
+      // main thread consumers will subsequently use the cache, and
+      // off-main-thread consumers only reach this in the rare case of selector
+      // matching on the "style" attribute.
+      if (!ServoStyleSet::IsInServoTraversal()) {
+        const_cast<nsAttrValue*>(this)->SetMiscAtomOrString(&aResult);
+      }
 
       break;
     }
     case eDoubleValue:
     {
       aResult.Truncate();
       aResult.AppendFloat(GetDoubleValue());
       break;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -4032,16 +4032,20 @@ nsIDocument::GetRootElement() const
 {
   return (mCachedRootElement && mCachedRootElement->GetParentNode() == this) ?
          mCachedRootElement : GetRootElementInternal();
 }
 
 Element*
 nsDocument::GetRootElementInternal() const
 {
+  // We invoke GetRootElement() immediately before the servo traversal, so we
+  // should always have a cache hit from Servo.
+  MOZ_ASSERT(NS_IsMainThread());
+
   // Loop backwards because any non-elements, such as doctypes and PIs
   // are likely to appear before the root element.
   uint32_t i;
   for (i = mChildren.ChildCount(); i > 0; --i) {
     nsIContent* child = mChildren.ChildAt(i - 1);
     if (child->IsElement()) {
       const_cast<nsDocument*>(this)->mCachedRootElement = child->AsElement();
       return child->AsElement();
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -83,32 +83,26 @@
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/PluginCrashedEvent.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/HTMLObjectElementBinding.h"
 #include "mozilla/dom/HTMLSharedObjectElement.h"
+#include "mozilla/dom/HTMLObjectElement.h"
 #include "nsChannelClassifier.h"
 
 #ifdef XP_WIN
 // Thanks so much, Microsoft! :(
 #ifdef CreateEvent
 #undef CreateEvent
 #endif
 #endif // XP_WIN
 
-#ifdef XP_MACOSX
-// HandlePluginCrashed() and HandlePluginInstantiated() needed from here to
-// fix bug 1147521.  Should later be replaced by proper interface methods,
-// maybe on nsIObjectLoadingContext.
-#include "mozilla/dom/HTMLObjectElement.h"
-#endif
-
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 static const char *kPrefJavaMIME = "plugin.java.mime";
 static const char *kPrefYoutubeRewrite = "plugins.rewrite_youtube_embeds";
 static const char *kPrefBlockURIs = "browser.safebrowsing.blockedURIs.enabled";
 static const char *kPrefFavorFallbackMode = "plugins.favorfallback.mode";
 static const char *kPrefFavorFallbackRules = "plugins.favorfallback.rules";
 
@@ -3013,50 +3007,53 @@ nsObjectLoadingContent::LoadFallback(Fal
 
   NS_ASSERTION(!mInstanceOwner && !mFrameLoader && !mChannel,
                "LoadFallback called with loaded content");
 
   //
   // Fixup mFallbackType
   //
   nsCOMPtr<nsIContent> thisContent =
-  do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "must be a content");
 
   if (!thisContent->IsHTMLElement() || mContentType.IsEmpty()) {
     // Don't let custom fallback handlers run outside HTML, tags without a
     // determined type should always just be alternate content
     aType = eFallbackAlternate;
   }
 
   // We'll set this to null no matter what now, doing it here means we'll load
   // child embeds as we find them in the upcoming loop.
   mType = eType_Null;
 
-  // Do a breadth-first traverse of node tree with the current element as root,
-  // looking for the first embed we can find.
+  bool thisIsObject = thisContent->IsHTMLElement(nsGkAtoms::object);
+
+  // Do a depth-first traverse of node tree with the current element as root,
+  // looking for <embed> or <object> elements that might now need to load.
   nsTArray<nsINodeList*> childNodes;
   if ((thisContent->IsHTMLElement(nsGkAtoms::object) ||
        thisContent->IsHTMLElement(nsGkAtoms::applet)) &&
       (aType == eFallbackUnsupported ||
        aType == eFallbackDisabled ||
        aType == eFallbackBlocklisted ||
        aType == eFallbackAlternate))
   {
     for (nsIContent* child = thisContent->GetFirstChild(); child;
          child = child->GetNextNode(thisContent)) {
       if (aType != eFallbackAlternate &&
           !child->IsHTMLElement(nsGkAtoms::param) &&
           nsStyleUtil::IsSignificantChild(child, true, false)) {
         aType = eFallbackAlternate;
       }
-      if (child->IsHTMLElement(nsGkAtoms::embed) &&
-          thisContent->IsHTMLElement(nsGkAtoms::object)) {
-        HTMLSharedObjectElement* object = static_cast<HTMLSharedObjectElement*>(child);
-        if (object) {
+      if (thisIsObject) {
+        if (child->IsHTMLElement(nsGkAtoms::embed)) {
+          HTMLSharedObjectElement* embed = static_cast<HTMLSharedObjectElement*>(child);
+          embed->StartObjectLoad(true, true);
+        } else if (auto object = HTMLObjectElement::FromContent(child)) {
           object->StartObjectLoad(true, true);
         }
       }
     }
   }
 
   mFallbackType = aType;
 
@@ -3812,16 +3809,48 @@ nsObjectLoadingContent::MaybeFireErrorEv
     RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
       new LoadBlockingAsyncEventDispatcher(thisContent,
                                            NS_LITERAL_STRING("error"),
                                            false, false);
     loadBlockingAsyncDispatcher->PostDOMEvent();
   }
 }
 
+bool
+nsObjectLoadingContent::BlockEmbedOrObjectContentLoading()
+{
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  if (!thisContent->IsHTMLElement(nsGkAtoms::embed) &&
+      !thisContent->IsHTMLElement(nsGkAtoms::object)) {
+    // Doesn't apply to other elements (i.e. <applet>)
+    return false;
+  }
+
+  // Traverse up the node tree to see if we have any ancestors that may block us
+  // from loading
+  for (nsIContent* parent = thisContent->GetParent();
+       parent;
+       parent = parent->GetParent()) {
+    if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
+      return true;
+    }
+    // If we have an ancestor that is an object with a source, it'll have an
+    // associated displayed type. If that type is not null, don't load content
+    // for the embed.
+    if (HTMLObjectElement* object = HTMLObjectElement::FromContent(parent)) {
+      uint32_t type = object->DisplayedType();
+      if (type != eType_Null) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 // SetupProtoChainRunner implementation
 nsObjectLoadingContent::SetupProtoChainRunner::SetupProtoChainRunner(
     nsObjectLoadingContent* aContent)
   : mContent(aContent)
 {
 }
 
 NS_IMETHODIMP
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -329,16 +329,31 @@ class nsObjectLoadingContent : public ns
     void UnbindFromTree(bool aDeep = true,
                         bool aNullParent = true);
 
     /**
      * Return the content policy type used for loading the element.
      */
     virtual nsContentPolicyType GetContentPolicyType() const = 0;
 
+    /**
+     * Decides whether we should load <embed>/<object> node content.
+     *
+     * If this is an <embed> or <object> node there are cases in which we should
+     * not try to load the content:
+     *
+     * - If the node is the child of a media element
+     * - If the node is the child of an <object> node that already has
+     *   content being loaded.
+     *
+     * In these cases, this function will return false, which will cause
+     * us to skip calling LoadObject.
+     */
+    bool BlockEmbedOrObjectContentLoading();
+
   private:
 
     // Object parameter changes returned by UpdateObjectParameters
     enum ParameterUpdateFlags {
       eParamNoChange           = 0,
       // Parameters that potentially affect the channel changed
       // - mOriginalURI, mOriginalContentType
       eParamChannelChanged     = 1u << 0,
deleted file mode 100644
--- a/dom/base/test/file_bug1263696_frame_fail.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/REC-html401-19991224/strict.dtd">
-<html>
- <head>
-  <title>Bug 1263696 - iframe that should not be loaded</title>
- </head>
- <body>
-   <script>
-    parent.SimpleTest.ok(false, "this iframe should not load");
-   </script>
- </body>
-</html>
deleted file mode 100644
--- a/dom/base/test/file_bug1263696_frame_pass.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/REC-html401-19991224/strict.dtd">
-<html>
- <head>
-  <title>Bug 1263696 - iframe that should be loaded</title>
- </head>
- <body>
-   <script>
-    parent.index = parent.index + 1;
-    parent.SimpleTest.ok(true, "this iframe should load");
-   </script>
- </body>
-</html>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -120,18 +120,16 @@ support-files =
   file_bug787778.sjs
   file_bug804395.jar
   file_bug869432.eventsource
   file_bug869432.eventsource^headers^
   file_bug902350.html
   file_bug902350_frame.html
   file_bug907892.html
   file_bug945152.jar
-  file_bug1263696_frame_pass.html
-  file_bug1263696_frame_fail.html
   file_bug1274806.html
   file_general_document.html
   file_htmlserializer_1.html
   file_htmlserializer_1_bodyonly.html
   file_htmlserializer_1_format.html
   file_htmlserializer_1_linebreak.html
   file_htmlserializer_1_links.html
   file_htmlserializer_1_nested_body.html
@@ -582,17 +580,16 @@ skip-if = toolkit == 'android'
 [test_bug1126851.html]
 [test_bug1163743.html]
 [test_bug1165501.html]
 [test_bug1187157.html]
 [test_bug1198095.html]
 [test_bug1238440.html]
 [test_bug1250148.html]
 [test_bug1259588.html]
-[test_bug1263696.html]
 [test_bug1268962.html]
 [test_bug1274806.html]
 [test_bug1281963.html]
 [test_bug1295852.html]
 [test_bug1307730.html]
 [test_bug1308069.html]
 [test_bug1314032.html]
 [test_bug1318303.html]
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -475,17 +475,17 @@ WorkerFetchResolver::FlushConsoleReport(
     return;
   }
 
   // Flush to dedicated worker
   mReporter->FlushConsoleReports(worker->GetLoadGroup());
 }
 
 nsresult
-ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
+ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentTypeWithCharset,
                           uint64_t& aContentLength)
 {
   MOZ_ASSERT(aStream);
   nsAutoCString charset;
   aContentTypeWithCharset.SetIsVoid(true);
 
@@ -528,17 +528,17 @@ ExtractByteStreamFromBody(const OwningAr
                             charset);
   }
 
   NS_NOTREACHED("Should never reach here");
   return NS_ERROR_FAILURE;
 }
 
 nsresult
-ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
+ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentTypeWithCharset,
                           uint64_t& aContentLength)
 {
   MOZ_ASSERT(aStream);
   MOZ_ASSERT(!*aStream);
 
   nsAutoCString charset;
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -22,51 +22,56 @@
 #include "mozilla/dom/RequestBinding.h"
 #include "mozilla/dom/workers/bindings/WorkerHolder.h"
 
 class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
-class ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
+class BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString;
 class BlobImpl;
 class InternalRequest;
-class OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams;
+class OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString;
 class RequestOrUSVString;
 enum class CallerType : uint32_t;
 
 namespace workers {
 class WorkerPrivate;
 } // namespace workers
 
 already_AddRefed<Promise>
 FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput,
              const RequestInit& aInit, CallerType aCallerType,
              ErrorResult& aRv);
 
 nsresult
 UpdateRequestReferrer(nsIGlobalObject* aGlobal, InternalRequest* aRequest);
 
+namespace fetch {
+typedef BlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString BodyInit;
+typedef OwningBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString OwningBodyInit;
+};
+
 /*
  * Creates an nsIInputStream based on the fetch specifications 'extract a byte
  * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
  * Stores content type in out param aContentType.
  */
 nsresult
-ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
+ExtractByteStreamFromBody(const fetch::OwningBodyInit& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentType,
                           uint64_t& aContentLength);
 
 /*
  * Non-owning version.
  */
 nsresult
-ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
+ExtractByteStreamFromBody(const fetch::BodyInit& aBodyInit,
                           nsIInputStream** aStream,
                           nsCString& aContentType,
                           uint64_t& aContentLength);
 
 template <class Derived> class FetchBodyWorkerHolder;
 
 /*
  * FetchBody's body consumption uses nsIInputStreamPump to read from the
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -542,21 +542,19 @@ Request::Constructor(const GlobalObject&
     // method is guaranteed to be uppercase due to step 14.2 above.
     if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
       aRv.ThrowTypeError<MSG_NO_BODY_ALLOWED_FOR_GET_AND_HEAD>();
       return nullptr;
     }
   }
 
   if (aInit.mBody.WasPassed()) {
-    const Nullable<OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& bodyInitNullable =
-      aInit.mBody.Value();
+    const Nullable<fetch::OwningBodyInit>& bodyInitNullable = aInit.mBody.Value();
     if (!bodyInitNullable.IsNull()) {
-      const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit =
-        bodyInitNullable.Value();
+      const fetch::OwningBodyInit& bodyInit = bodyInitNullable.Value();
       nsCOMPtr<nsIInputStream> stream;
       nsAutoCString contentTypeWithCharset;
       uint64_t contentLengthUnused;
       aRv = ExtractByteStreamFromBody(bodyInit,
                                       getter_AddRefs(stream),
                                       contentTypeWithCharset,
                                       contentLengthUnused);
       if (NS_WARN_IF(aRv.Failed())) {
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -99,17 +99,17 @@ Response::Redirect(const GlobalObject& a
     return nullptr;
   }
 
   if (aStatus != 301 && aStatus != 302 && aStatus != 303 && aStatus != 307 && aStatus != 308) {
     aRv.ThrowRangeError<MSG_INVALID_REDIRECT_STATUSCODE_ERROR>();
     return nullptr;
   }
 
-  Optional<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams> body;
+  Optional<fetch::BodyInit> body;
   ResponseInit init;
   init.mStatus = aStatus;
   RefPtr<Response> r = Response::Constructor(aGlobal, body, init, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   r->GetInternalHeaders()->Set(NS_LITERAL_CSTRING("Location"),
@@ -120,17 +120,17 @@ Response::Redirect(const GlobalObject& a
   r->GetInternalHeaders()->SetGuard(HeadersGuardEnum::Immutable, aRv);
   MOZ_ASSERT(!aRv.Failed());
 
   return r.forget();
 }
 
 /*static*/ already_AddRefed<Response>
 Response::Constructor(const GlobalObject& aGlobal,
-                      const Optional<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& aBody,
+                      const Optional<fetch::BodyInit>& aBody,
                       const ResponseInit& aInit, ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 
   if (aInit.mStatus < 200 || aInit.mStatus > 599) {
     aRv.ThrowRangeError<MSG_INVALID_RESPONSE_STATUSCODE_ERROR>();
     return nullptr;
   }
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -109,17 +109,17 @@ public:
   static already_AddRefed<Response>
   Error(const GlobalObject& aGlobal);
 
   static already_AddRefed<Response>
   Redirect(const GlobalObject& aGlobal, const nsAString& aUrl, uint16_t aStatus, ErrorResult& aRv);
 
   static already_AddRefed<Response>
   Constructor(const GlobalObject& aGlobal,
-              const Optional<ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& aBody,
+              const Optional<fetch::BodyInit>& aBody,
               const ResponseInit& aInit, ErrorResult& rv);
 
   nsIGlobalObject* GetParentObject() const
   {
     return mOwner;
   }
 
   already_AddRefed<Response>
--- a/dom/file/File.h
+++ b/dom/file/File.h
@@ -37,34 +37,34 @@ class nsIInputStream;
 namespace mozilla {
 namespace dom {
 
 struct BlobPropertyBag;
 struct ChromeFilePropertyBag;
 struct FilePropertyBag;
 class BlobImpl;
 class File;
-class OwningArrayBufferOrArrayBufferViewOrBlobOrUSVString;
+class OwningArrayBufferViewOrArrayBufferOrBlobOrUSVString;
 class Promise;
 
 class Blob : public nsIDOMBlob
            , public nsIXHRSendable
            , public nsIMutable
            , public nsSupportsWeakReference
            , public nsWrapperCache
 {
 public:
   NS_DECL_NSIDOMBLOB
   NS_DECL_NSIXHRSENDABLE
   NS_DECL_NSIMUTABLE
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Blob, nsIDOMBlob)
 
-  typedef OwningArrayBufferOrArrayBufferViewOrBlobOrUSVString BlobPart;
+  typedef OwningArrayBufferViewOrArrayBufferOrBlobOrUSVString BlobPart;
 
   // This creates a Blob or a File based on the type of BlobImpl.
   static Blob*
   Create(nsISupports* aParent, BlobImpl* aImpl);
 
   static already_AddRefed<Blob>
   CreateStringBlob(nsISupports* aParent, const nsACString& aData,
                    const nsAString& aContentType);
--- a/dom/file/FileCreatorHelper.cpp
+++ b/dom/file/FileCreatorHelper.cpp
@@ -94,16 +94,20 @@ FileCreatorHelper::CreateFileInternal(ns
 FileCreatorHelper::FileCreatorHelper(Promise* aPromise,
                                      nsPIDOMWindowInner* aWindow)
   : mPromise(aPromise)
   , mWindow(aWindow)
 {
   MOZ_ASSERT(aPromise);
 }
 
+FileCreatorHelper::~FileCreatorHelper()
+{
+}
+
 void
 FileCreatorHelper::SendRequest(nsIFile* aFile,
                                const ChromeFilePropertyBag& aBag,
                                bool aIsFromNsIFile,
                                ErrorResult& aRv)
 {
   MOZ_ASSERT(aFile);
 
--- a/dom/file/FileCreatorHelper.h
+++ b/dom/file/FileCreatorHelper.h
@@ -67,17 +67,17 @@ private:
                  const nsAString& aType,
                  const nsAString& aName,
                  bool aLastModifiedPassed,
                  int64_t aLastModified,
                  bool aIsFromNsIFile,
                  BlobImpl** aBlobImpl);
 
   FileCreatorHelper(Promise* aPromise, nsPIDOMWindowInner* aWindow);
-  ~FileCreatorHelper() = default;
+  ~FileCreatorHelper();
 
   void
   SendRequest(nsIFile* aFile, const ChromeFilePropertyBag& aBag,
               bool aIsFromNsIFile, ErrorResult& aRv);
 
   RefPtr<Promise> mPromise;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
 };
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2428,21 +2428,18 @@ HTMLInputElement::GetMaximum() const
 
   Decimal max;
   return ConvertStringToNumber(maxStr, max) ? max : defaultMaximum;
 }
 
 Decimal
 HTMLInputElement::GetStepBase() const
 {
-  MOZ_ASSERT(mType == NS_FORM_INPUT_NUMBER ||
-             mType == NS_FORM_INPUT_DATE ||
-             mType == NS_FORM_INPUT_TIME ||
-             mType == NS_FORM_INPUT_MONTH ||
-             mType == NS_FORM_INPUT_WEEK ||
+  MOZ_ASSERT(IsDateTimeInputType(mType) ||
+             mType == NS_FORM_INPUT_NUMBER ||
              mType == NS_FORM_INPUT_RANGE,
              "Check that kDefaultStepBase is correct for this new type");
 
   Decimal stepBase;
 
   // Do NOT use GetMinimum here - the spec says to use "the min content
   // attribute", not "the minimum".
   nsAutoString minStr;
@@ -8666,16 +8663,17 @@ HTMLInputElement::GetStepScaleFactor() c
 
   switch (mType) {
     case NS_FORM_INPUT_DATE:
       return kStepScaleFactorDate;
     case NS_FORM_INPUT_NUMBER:
     case NS_FORM_INPUT_RANGE:
       return kStepScaleFactorNumberRange;
     case NS_FORM_INPUT_TIME:
+    case NS_FORM_INPUT_DATETIME_LOCAL:
       return kStepScaleFactorTime;
     case NS_FORM_INPUT_MONTH:
       return kStepScaleFactorMonth;
     case NS_FORM_INPUT_WEEK:
       return kStepScaleFactorWeek;
     default:
       MOZ_ASSERT(false, "Unrecognized input type");
       return Decimal::nan();
@@ -8690,16 +8688,17 @@ HTMLInputElement::GetDefaultStep() const
   switch (mType) {
     case NS_FORM_INPUT_DATE:
     case NS_FORM_INPUT_MONTH:
     case NS_FORM_INPUT_WEEK:
     case NS_FORM_INPUT_NUMBER:
     case NS_FORM_INPUT_RANGE:
       return kDefaultStep;
     case NS_FORM_INPUT_TIME:
+    case NS_FORM_INPUT_DATETIME_LOCAL:
       return kDefaultStepTime;
     default:
       MOZ_ASSERT(false, "Unrecognized input type");
       return Decimal::nan();
   }
 }
 
 void
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -1065,21 +1065,17 @@ protected:
   /**
    * Returns if the min and max attributes apply for the current type.
    */
   bool DoesMinMaxApply() const;
 
   /**
    * Returns if the step attribute apply for the current type.
    */
-  bool DoesStepApply() const
-  {
-    // TODO: this is temporary until bug 888331 is fixed.
-    return DoesMinMaxApply() && mType != NS_FORM_INPUT_DATETIME_LOCAL;
-  }
+  bool DoesStepApply() const { return DoesMinMaxApply(); }
 
   /**
    * Returns if stepDown and stepUp methods apply for the current type.
    */
   bool DoStepDownStepUpApply() const { return DoesStepApply(); }
 
   /**
    * Returns if valueAsNumber attribute applies for the current type.
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -74,17 +74,17 @@ HTMLObjectElement::IsDoneAddingChildren(
 void
 HTMLObjectElement::DoneAddingChildren(bool aHaveNotified)
 {
   mIsDoneAddingChildren = true;
 
   // If we're already in a document, we need to trigger the load
   // Otherwise, BindToTree takes care of that.
   if (IsInComposedDoc()) {
-    StartObjectLoad(aHaveNotified);
+    StartObjectLoad(aHaveNotified, false);
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLObjectElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLObjectElement,
                                                   nsGenericHTMLFormElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
@@ -305,34 +305,36 @@ HTMLObjectElement::SetAttr(int32_t aName
   // if aNotify is false, we are coming from the parser or some such place;
   // we'll get bound after all the attributes have been set, so we'll do the
   // object load from BindToTree/DoneAddingChildren.
   // Skip the LoadObject call in that case.
   // We also don't want to start loading the object when we're not yet in
   // a document, just in case that the caller wants to set additional
   // attributes before inserting the node into the document.
   if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
-      aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::data) {
+      aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::data &&
+      !BlockEmbedOrObjectContentLoading()) {
     return LoadObject(aNotify, true);
   }
 
   return NS_OK;
 }
 
 nsresult
 HTMLObjectElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
                              bool aNotify)
 {
   nsresult rv = nsGenericHTMLFormElement::UnsetAttr(aNameSpaceID,
                                                     aAttribute, aNotify);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // See comment in SetAttr
   if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
-      aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data) {
+      aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::data &&
+      !BlockEmbedOrObjectContentLoading()) {
     return LoadObject(aNotify, true);
   }
 
   return NS_OK;
 }
 
 bool
 HTMLObjectElement::IsFocusableForTabIndex()
@@ -530,25 +532,26 @@ HTMLObjectElement::IsAttributeMapped(con
 
 nsMapRuleToAttributesFunc
 HTMLObjectElement::GetAttributeMappingFunction() const
 {
   return &MapAttributesIntoRule;
 }
 
 void
-HTMLObjectElement::StartObjectLoad(bool aNotify)
+HTMLObjectElement::StartObjectLoad(bool aNotify, bool aForce)
 {
   // BindToTree can call us asynchronously, and we may be removed from the tree
   // in the interim
-  if (!IsInComposedDoc() || !OwnerDoc()->IsActive()) {
+  if (!IsInComposedDoc() || !OwnerDoc()->IsActive() ||
+      BlockEmbedOrObjectContentLoading()) {
     return;
   }
 
-  LoadObject(aNotify);
+  LoadObject(aNotify, aForce);
   SetIsNetworkCreated(false);
 }
 
 EventStates
 HTMLObjectElement::IntrinsicState() const
 {
   return nsGenericHTMLFormElement::IntrinsicState() | ObjectState();
 }
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -93,17 +93,17 @@ public:
 
   // nsObjectLoadingContent
   virtual uint32_t GetCapabilities() const override;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
 
   nsresult CopyInnerTo(Element* aDest);
 
-  void StartObjectLoad() { StartObjectLoad(true); }
+  void StartObjectLoad() { StartObjectLoad(true, false); }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLObjectElement,
                                            nsGenericHTMLFormElement)
 
   // Web IDL binding methods
   // XPCOM GetData is ok; note that it's a URI attribute with a weird base URI
   void SetData(const nsAString& aValue, ErrorResult& aRv)
   {
@@ -242,22 +242,22 @@ public:
   }
 
   nsIDocument*
   GetSVGDocument(nsIPrincipal& aSubjectPrincipal)
   {
     return GetContentDocument(aSubjectPrincipal);
   }
 
-private:
   /**
    * Calls LoadObject with the correct arguments to start the plugin load.
    */
-  void StartObjectLoad(bool aNotify);
+  void StartObjectLoad(bool aNotify, bool aForceLoad);
 
+private:
   /**
    * Returns if the element is currently focusable regardless of it's tabindex
    * value. This is used to know the default tabindex value.
    */
   bool IsFocusableForTabIndex();
 
   nsContentPolicyType GetContentPolicyType() const override
   {
--- a/dom/html/HTMLSharedObjectElement.cpp
+++ b/dom/html/HTMLSharedObjectElement.cpp
@@ -175,17 +175,17 @@ HTMLSharedObjectElement::SetAttr(int32_t
   // we'll get bound after all the attributes have been set, so we'll do the
   // object load from BindToTree/DoneAddingChildren.
   // Skip the LoadObject call in that case.
   // We also don't want to start loading the object when we're not yet in
   // a document, just in case that the caller wants to set additional
   // attributes before inserting the node into the document.
   if (aNotify && IsInComposedDoc() && mIsDoneAddingChildren &&
       aNameSpaceID == kNameSpaceID_None && aName == URIAttrName()
-      && !BlockEmbedContentLoading()) {
+      && !BlockEmbedOrObjectContentLoading()) {
     return LoadObject(aNotify, true);
   }
 
   return NS_OK;
 }
 
 bool
 HTMLSharedObjectElement::IsHTMLFocusable(bool aWithMouse,
@@ -308,17 +308,17 @@ HTMLSharedObjectElement::GetAttributeMap
 }
 
 void
 HTMLSharedObjectElement::StartObjectLoad(bool aNotify, bool aForceLoad)
 {
   // BindToTree can call us asynchronously, and we may be removed from the tree
   // in the interim
   if (!IsInComposedDoc() || !OwnerDoc()->IsActive() ||
-      BlockEmbedContentLoading()) {
+      BlockEmbedOrObjectContentLoading()) {
     return;
   }
 
   LoadObject(aNotify, aForceLoad);
   SetIsNetworkCreated(false);
 }
 
 EventStates
@@ -384,36 +384,10 @@ HTMLSharedObjectElement::GetContentPolic
     // through RequestContext yet.
     return nsIContentPolicy::TYPE_INTERNAL_OBJECT;
   } else {
     MOZ_ASSERT(mNodeInfo->Equals(nsGkAtoms::embed));
     return nsIContentPolicy::TYPE_INTERNAL_EMBED;
   }
 }
 
-bool
-HTMLSharedObjectElement::BlockEmbedContentLoading()
-{
-  // Only check on embed elements
-  if (!IsHTMLElement(nsGkAtoms::embed)) {
-    return false;
-  }
-  // Traverse up the node tree to see if we have any ancestors that may block us
-  // from loading
-  for (nsIContent* parent = GetParent(); parent; parent = parent->GetParent()) {
-    if (parent->IsAnyOfHTMLElements(nsGkAtoms::video, nsGkAtoms::audio)) {
-      return true;
-    }
-    // If we have an ancestor that is an object with a source, it'll have an
-    // associated displayed type. If that type is not null, don't load content
-    // for the embed.
-    if (HTMLObjectElement* object = HTMLObjectElement::FromContent(parent)) {
-      uint32_t type = object->DisplayedType();
-      if (type != eType_Null) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLSharedObjectElement.h
+++ b/dom/html/HTMLSharedObjectElement.h
@@ -215,29 +215,14 @@ private:
   // mIsDoneAddingChildren is only really used for <applet>.  This boolean is
   // always true for <embed>, per the documentation in nsIContent.h.
   bool mIsDoneAddingChildren;
 
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     GenericSpecifiedValues* aGenericData);
-
-  /**
-   * Decides whether we should load embed node content.
-   *
-   * If this is an embed node there are cases in which we should not try to load
-   * the content:
-   *
-   * - If the embed node is the child of a media element
-   * - If the embed node is the child of an object node that already has
-   *   content being loaded.
-   *
-   * In these cases, this function will return false, which will cause
-   * us to skip calling LoadObject.
-   */
-  bool BlockEmbedContentLoading();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HTMLSharedObjectElement_h
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -448,28 +448,38 @@ nsGenericHTMLFrameElement::IsHTMLFocusab
 
   if (!*aIsFocusable && aTabIndex) {
     *aTabIndex = -1;
   }
 
   return false;
 }
 
+static bool sMozBrowserFramesEnabled = false;
+#ifdef DEBUG
+static bool sBoolVarCacheInitialized = false;
+#endif
+
+void
+nsGenericHTMLFrameElement::InitStatics()
+{
+  MOZ_ASSERT(!sBoolVarCacheInitialized);
+  MOZ_ASSERT(NS_IsMainThread());
+  Preferences::AddBoolVarCache(&sMozBrowserFramesEnabled,
+                               "dom.mozBrowserFramesEnabled");
+#ifdef DEBUG
+  sBoolVarCacheInitialized = true;
+#endif
+}
+
+
 bool
 nsGenericHTMLFrameElement::BrowserFramesEnabled()
 {
-  static bool sMozBrowserFramesEnabled = false;
-  static bool sBoolVarCacheInitialized = false;
-
-  if (!sBoolVarCacheInitialized) {
-    sBoolVarCacheInitialized = true;
-    Preferences::AddBoolVarCache(&sMozBrowserFramesEnabled,
-                                 "dom.mozBrowserFramesEnabled");
-  }
-
+  MOZ_ASSERT(sBoolVarCacheInitialized);
   return sMozBrowserFramesEnabled;
 }
 
 /**
  * Return true if this frame element really is a mozbrowser.  (It
  * needs to have the right attributes, and its creator must have the right
  * permissions.)
  */
--- a/dom/html/nsGenericHTMLFrameElement.h
+++ b/dom/html/nsGenericHTMLFrameElement.h
@@ -66,31 +66,34 @@ public:
                                 const nsAttrValue* aValue,
                                 bool aNotify) override;
   virtual void DestroyContent() override;
 
   nsresult CopyInnerTo(mozilla::dom::Element* aDest);
 
   virtual int32_t TabIndexDefault() override;
 
+  virtual nsIMozBrowserFrame* GetAsMozBrowserFrame() override { return this; }
+
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGenericHTMLFrameElement,
                                            nsGenericHTMLElement)
 
   void SwapFrameLoaders(mozilla::dom::HTMLIFrameElement& aOtherLoaderOwner,
                         mozilla::ErrorResult& aError);
 
   void SwapFrameLoaders(nsXULElement& aOtherLoaderOwner,
                         mozilla::ErrorResult& aError);
 
   void SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner,
                         mozilla::ErrorResult& rv);
 
   void PresetOpenerWindow(mozIDOMWindowProxy* aOpenerWindow,
                           mozilla::ErrorResult& aRv);
 
+  static void InitStatics();
   static bool BrowserFramesEnabled();
 
   /**
    * Helper method to map a HTML 'scrolling' attribute value to a nsIScrollable
    * enum value.  scrolling="no" (and its synonyms) maps to
    * nsIScrollable::Scrollbar_Never, and anything else (including nullptr) maps
    * to nsIScrollable::Scrollbar_Auto.
    * @param aValue the attribute value to map or nullptr
--- a/dom/html/test/forms/test_step_attribute.html
+++ b/dom/html/test/forms/test_step_attribute.html
@@ -26,18 +26,17 @@ var data = [
   { type: 'tel', apply: false },
   { type: 'url', apply: false },
   { type: 'email', apply: false },
   { type: 'password', apply: false },
   { type: 'date', apply: true },
   { type: 'month', apply: true },
   { type: 'week', apply: true },
   { type: 'time', apply: true },
-  // TODO: temporary set to false until bug 888331 is fixed.
-  { type: 'datetime-local', apply: false },
+  { type: 'datetime-local', apply: true },
   { type: 'number', apply: true },
   { type: 'range', apply: true },
   { type: 'color', apply: false },
   { type: 'checkbox', apply: false },
   { type: 'radio', apply: false },
   { type: 'file', apply: false },
   { type: 'submit', apply: false },
   { type: 'image', apply: false },
@@ -945,17 +944,114 @@ for (var test of data) {
       input.value = '1970-W01';
       checkValidity(input, false, apply, { low: "1969-W52", high: "1970-W02" });
 
       input.value = '1970-W02';
       checkValidity(input, true, apply);
 
       break;
     case 'datetime-local':
-      // TODO: this is temporary until bug 888331 is fixed.
+      // When step is invalid, every datetime is valid
+      input.step = 0;
+      input.value = '2017-02-06T12:00';
+      checkValidity(input, true, apply);
+
+      input.step = 'foo';
+      input.value = '1970-01-01T00:00';
+      checkValidity(input, true, apply);
+
+      input.step = '-1';
+      input.value = '1969-12-12 00:10';
+      checkValidity(input, true, apply);
+
+      input.removeAttribute('step');
+      input.value = '1500-01-01T12:00';
+      checkValidity(input, true, apply);
+
+      input.step = 'any';
+      input.value = '1966-12-12T12:00';
+      checkValidity(input, true, apply);
+
+      input.step = 'ANY';
+      input.value = '2017-01-01 12:00';
+      checkValidity(input, true, apply);
+
+      // When min is set to a valid datetime, there is a step base.
+      input.min = '2017-01-01T00:00:00';
+      input.step = '2';
+      input.value = '2017-01-01T00:00:02';
+      checkValidity(input, true, apply);
+
+      input.value = '2017-01-01T00:00:03';
+      checkValidity(input, false, apply,
+        { low: "2017-01-01T00:00:02", high: "2017-01-01T00:00:04" });
+
+      input.min = '2017-01-01T00:00:05';
+      input.value = '2017-01-01T00:00:08';
+      checkValidity(input, false, apply,
+        { low: "2017-01-01T00:00:07", high: "2017-01-01T00:00:09" });
+
+      input.min = '2000-01-01T00:00';
+      input.step = '120';
+      input.value = '2000-01-01T00:02';
+      checkValidity(input, true, apply);
+
+      // Without any step attribute the datetime is valid
+      input.removeAttribute('step');
+      checkValidity(input, true, apply);
+
+      input.min = '1950-01-01T00:00';
+      input.step = '129600'; // 1.5 day
+      input.value = '1950-01-02T00:00';
+      checkValidity(input, false, apply,
+        { low: "1950-01-01T00:00", high: "1950-01-02T12:00" });
+
+      input.step = '259200'; // 3 days
+      input.value = '1950-01-04T12:00';
+      checkValidity(input, false, apply,
+        { low: "1950-01-04T00:00", high: "1950-01-07T00:00" });
+
+      input.value = '1950-01-10T00:00';
+      checkValidity(input, true, apply);
+
+      input.step = '0.5'; // half a second
+      input.value = '1950-01-01T00:00:00.123';
+      checkValidity(input, false, apply,
+        { low: "1950-01-01T00:00", high: "1950-01-01T00:00:00.500" });
+
+      input.value = '2000-01-01T12:30:30.600';
+      checkValidity(input, false, apply,
+        { low: "2000-01-01T12:30:30.500", high: "2000-01-01T12:30:31" });
+
+      input.value = '1950-01-05T00:00:00.500';
+      checkValidity(input, true, apply);
+
+      input.step = '2.1';
+      input.min = '1991-01-01T12:00';
+      input.value = '1991-01-01T12:00';
+      checkValidity(input, true, apply);
+
+      input.value = '1991-01-01T12:00:03';
+      checkValidity(input, false, apply,
+        { low: "1991-01-01T12:00:02.100", high: "1991-01-01T12:00:04.200" });
+
+      input.value = '1991-01-01T12:00:06.3';
+      checkValidity(input, true, apply);
+
+      input.step = '2.1';
+      input.min = '1969-12-20T10:00:05';
+      input.value = '1969-12-20T10:00:05';
+      checkValidity(input, true, apply);
+
+      input.value = '1969-12-20T10:00:08';
+      checkValidity(input, false, apply,
+        { low: "1969-12-20T10:00:07.100", high: "1969-12-20T10:00:09.200" });
+
+      input.value = '1969-12-20T10:00:09.200';
+      checkValidity(input, true, apply);
 
       break;
     default:
       ok(false, "Implement the tests for <input type='" + test.type + " >");
       break;
   }
 }
 
--- a/dom/html/test/forms/test_stepup_stepdown.html
+++ b/dom/html/test/forms/test_stepup_stepdown.html
@@ -47,25 +47,20 @@ function checkAvailability()
     ["reset", false],
     ["button", false],
     ["number", true],
     ["range", true],
     ["date", true],
     ["time", true],
     ["month", true],
     ["week", true],
+    ["datetime-local", true],
     ["color", false],
   ];
 
-  var todoList =
-  [
-    ["datetime", true],
-    ["datetime-local", true],
-  ];
-
   var element = document.createElement("input");
   element.setAttribute('value', '0');
 
   for (data of testData) {
     var exceptionCaught = false;
     element.type = data[0];
     try {
       element.stepDown();
@@ -77,37 +72,16 @@ function checkAvailability()
     exceptionCaught = false;
     try {
       element.stepUp();
     } catch (e) {
       exceptionCaught = true;
     }
     is(exceptionCaught, !data[1], "stepUp() availability is not correct");
   }
-
-  for (data of todoList) {
-    var exceptionCaught = false;
-    element.type = data[0];
-    try {
-      element.stepDown();
-    } catch (e) {
-      exceptionCaught = true;
-    }
-    todo_is(exceptionCaught, !data[1],
-            "stepDown() availability is not correct");
-
-    exceptionCaught = false;
-    try {
-      element.stepUp();
-    } catch (e) {
-      exceptionCaught = true;
-    }
-    todo_is(exceptionCaught, !data[1],
-            "stepUp() availability is not correct");
-  }
 }
 
 function checkStepDown()
 {
   // This testData is very similar to the one in checkStepUp with some changes
   // relative to stepDown.
   var testData = [
   /* Initial value | step | min | max | stepDown arg | final value | exception */
@@ -504,16 +478,90 @@ function checkStepDown()
     // value = "" (NaN).
     [ '',   null,   null,  null,  null, '1970-W01',    false ],
     // With step = 'any'.
     [ '2016-W01',  'any',  null,  null,  1,  null,  true ],
     [ '2016-W01',  'ANY',  null,  null,  1,  null,  true ],
     [ '2016-W01',  'AnY',  null,  null,  1,  null,  true ],
     [ '2016-W01',  'aNy',  null,  null,  1,  null,  true ],
   ]},
+  { type: 'datetime-local', data: [
+    // Regular case.
+    [ '2017-02-07T09:30',  null,  null,  null,  null, '2017-02-07T09:29',   false ],
+    // Argument testing.
+    [ '2017-02-07T09:30',  null,  null,  null,  1,    '2017-02-07T09:29',   false ],
+    [ '2017-02-07T09:30',  null,  null,  null,  5,    '2017-02-07T09:25',   false ],
+    [ '2017-02-07T09:30',  null,  null,  null,  -1,   '2017-02-07T09:31',   false ],
+    [ '2017-02-07T09:30',  null,  null,  null,  0,    '2017-02-07T09:30',   false ],
+    // hour/minutes/seconds wrapping.
+    [ '2000-01-01T05:00',    null,  null, null, null,  '2000-01-01T04:59',        false ],
+    [ '2000-01-01T05:00:00', 1,     null, null, null,  '2000-01-01T04:59:59',     false ],
+    [ '2000-01-01T05:00:00', 0.1,   null, null, null,  '2000-01-01T04:59:59.900', false ],
+    [ '2000-01-01T05:00:00', 0.01,  null, null, null,  '2000-01-01T04:59:59.990', false ],
+    [ '2000-01-01T05:00:00', 0.001, null, null, null,  '2000-01-01T04:59:59.999', false ],
+    // month/year wrapping.
+    [ '2012-08-01T12:00',  null,  null,  null,  1440,  '2012-07-31T12:00',   false ],
+    [ '1969-01-02T12:00',  null,  null,  null,  5760,  '1968-12-29T12:00',   false ],
+    [ '1969-12-31T00:00',  null,  null,  null,  -1440, '1970-01-01T00:00',   false ],
+    [ '2012-02-29T00:00',  null,  null,  null,  -1440, '2012-03-01T00:00',   false ],
+    // stepDown() on '00:00' gives '23:59'.
+    [ '2017-02-07T00:00',  null,  null,  null,  1,    '2017-02-06T23:59',   false ],
+    [ '2017-02-07T00:00',  null,  null,  null,  3,    '2017-02-06T23:57',   false ],
+    // Some random step values..
+    [ '2017-02-07T16:07',  '0.5',    null,               null,  null, '2017-02-07T16:06:59.500', false ],
+    [ '2017-02-07T16:07',  '2',      null,               null,  null, '2017-02-07T16:06:58',     false ],
+    [ '2017-02-07T16:07',  '0.25',   null,               null,  4,    '2017-02-07T16:06:59',     false ],
+    [ '2017-02-07T16:07',  '1.1',    '2017-02-07T16:00', null,  1,    '2017-02-07T16:06:59.100', false ],
+    [ '2017-02-07T16:07',  '1.1',    '2017-02-07T16:00', null,  2,    '2017-02-07T16:06:58',     false ],
+    [ '2017-02-07T16:07',  '1.1',    '2017-02-07T16:00', null,  10,   '2017-02-07T16:06:49.200', false ],
+    [ '2017-02-07T16:07',  '129600', '2017-02-01T00:00', null,  2,    '2017-02-05T12:00',        false ],
+    // step = 0 isn't allowed (-> step = 1).
+    [ '2017-02-07T10:15',  '0',   null,  null,  null, '2017-02-07T10:14',   false ],
+    // step < 0 isn't allowed (-> step = 1).
+    [ '2017-02-07T10:15',  '-1',  null,  null,  null, '2017-02-07T10:14',   false ],
+    // step = NaN isn't allowed (-> step = 1).
+    [ '2017-02-07T10:15',  'foo', null,  null,  null, '2017-02-07T10:14',   false ],
+    // Min values testing.
+    [ '2012-02-02T17:02',  '60',     'foo',              null,  2,     '2012-02-02T17:00',  false ],
+    [ '2012-02-02T17:10',  '60',     '2012-02-02T17:09', null,  null,  '2012-02-02T17:09',  false ],
+    [ '2012-02-02T17:10',  '60',     '2012-02-02T17:10', null,  null,  '2012-02-02T17:10',  false ],
+    [ '2012-02-02T17:10',  '60',     '2012-02-02T17:30', null,  1,     '2012-02-02T17:10',  false ],
+    [ '2012-02-02T17:10',  '180',    '2012-02-02T17:05', null,  null,  '2012-02-02T17:08',  false ],
+    [ '2012-02-03T20:05',  '86400',  '2012-02-02T17:05', null,  null,  '2012-02-03T17:05',  false ],
+    [ '2012-02-03T18:00',  '129600', '2012-02-01T00:00', null,  null,  '2012-02-02T12:00',  false ],
+    // Max values testing.
+    [ '2012-02-02T17:15',  '60',     null,  'foo',               null,  '2012-02-02T17:14',  false ],
+    [ '2012-02-02T17:15',  null,     null,  '2012-02-02T17:20',  null,  '2012-02-02T17:14',  false ],
+    [ '2012-02-02T17:15',  null,     null,  '2012-02-02T17:15',  null,  '2012-02-02T17:14',  false ],
+    [ '2012-02-02T17:15',  null,     null,  '2012-02-02T17:13',  4,     '2012-02-02T17:11',  false ],
+    [ '2012-02-02T17:15',  '120',    null,  '2012-02-02T17:13',  3,     '2012-02-02T17:09',  false ],
+    [ '2012-02-03T20:05',  '86400',  null,  '2012-02-03T20:05',  null,  '2012-02-02T20:05',  false ],
+    [ '2012-02-03T18:00',  '129600', null,  '2012-02-03T20:00',  null,  '2012-02-02T06:00',  false ],
+    // Step mismatch.
+    [ '2017-02-07T17:19',  '120',    '2017-02-07T17:10',  null,               null,  '2017-02-07T17:18',  false ],
+    [ '2017-02-07T17:19',  '120',    '2017-02-07T17:10',  null,               2,     '2017-02-07T17:16',  false ],
+    [ '2017-02-07T17:19',  '120',    '2017-02-07T17:18',  '2017-02-07T17:25', null,  '2017-02-07T17:18',  false ],
+    [ '2017-02-07T17:19',  '120',    null,                null,               null,  '2017-02-07T17:17',  false ],
+    [ '2017-02-07T17:19',  '180',    null,                null,               null,  '2017-02-07T17:16',  false ],
+    [ '2017-02-07T17:19',  '172800', '2017-02-02T17:19', '2017-02-10T17:19',  null,  '2017-02-06T17:19',  false ],
+    // Clamping.
+    [ '2017-02-07T17:22',  null,   null,                '2017-02-07T17:11', null,  '2017-02-07T17:11',  false ],
+    [ '2017-02-07T17:22',  '120',  '2017-02-07T17:20',  '2017-02-07T17:22', null,  '2017-02-07T17:20',  false ],
+    [ '2017-02-07T17:22',  '300',  '2017-02-07T17:12',  '2017-02-07T17:20', 10,    '2017-02-07T17:12',  false ],
+    [ '2017-02-07T17:22',  '300',  '2017-02-07T17:18',  '2017-02-07T17:20', 2,     '2017-02-07T17:18',  false ],
+    [ '2017-02-07T17:22',  '600',  '2017-02-02T17:00',  '2017-02-07T17:00', 15,    '2017-02-07T15:00',  false ],
+    [ '2017-02-07T17:22',  '600',  '2017-02-02T17:00',  '2017-02-07T17:00', 2,     '2017-02-07T17:00',  false ],
+    // value = "" (NaN).
+    [ '',   null,   null,  null,  null, '1969-12-31T23:59',    false ],
+    // With step = 'any'.
+    [ '2017-02-07T15:20',  'any',  null,  null,  1,  null,  true ],
+    [ '2017-02-07T15:20',  'ANY',  null,  null,  1,  null,  true ],
+    [ '2017-02-07T15:20',  'AnY',  null,  null,  1,  null,  true ],
+    [ '2017-02-07T15:20',  'aNy',  null,  null,  1,  null,  true ],
+  ]},
   ];
 
   for (var test of testData) {
     for (var data of test.data) {
       var element = document.createElement("input");
       element.type = test.type;
 
       if (data[1] != null) {
@@ -953,16 +1001,88 @@ function checkStepUp()
     // value = "" (NaN).
     [ '',   null,   null,  null,  null, '1970-W02',    false ],
     // With step = 'any'.
     [ '2016-W01',  'any',  null,  null,  1,  null,  true ],
     [ '2016-W01',  'ANY',  null,  null,  1,  null,  true ],
     [ '2016-W01',  'AnY',  null,  null,  1,  null,  true ],
     [ '2016-W01',  'aNy',  null,  null,  1,  null,  true ],
   ]},
+  { type: 'datetime-local', data: [
+    // Regular case.
+    [ '2017-02-07T17:09',  null,  null,  null,  null, '2017-02-07T17:10',   false ],
+    // Argument testing.
+    [ '2017-02-07T17:10',  null,  null,  null,  1,    '2017-02-07T17:11',   false ],
+    [ '2017-02-07T17:10',  null,  null,  null,  5,    '2017-02-07T17:15',   false ],
+    [ '2017-02-07T17:10',  null,  null,  null,  -1,   '2017-02-07T17:09',   false ],
+    [ '2017-02-07T17:10',  null,  null,  null,  0,    '2017-02-07T17:10',   false ],
+    // hour/minutes/seconds wrapping.
+    [ '2000-01-01T04:59',        null,  null,  null, null, '2000-01-01T05:00',  false ],
+    [ '2000-01-01T04:59:59',     1,     null,  null, null, '2000-01-01T05:00',  false ],
+    [ '2000-01-01T04:59:59.900', 0.1,   null,  null, null, '2000-01-01T05:00',  false ],
+    [ '2000-01-01T04:59:59.990', 0.01,  null,  null, null, '2000-01-01T05:00',  false ],
+    [ '2000-01-01T04:59:59.999', 0.001, null,  null, null, '2000-01-01T05:00',  false ],
+    // month/year wrapping.
+    [ '2012-07-31T12:00',  null,  null,  null,  1440,  '2012-08-01T12:00',   false ],
+    [ '1968-12-29T12:00',  null,  null,  null,  5760,  '1969-01-02T12:00',   false ],
+    [ '1970-01-01T00:00',  null,  null,  null,  -1440, '1969-12-31T00:00',   false ],
+    [ '2012-03-01T00:00',  null,  null,  null,  -1440, '2012-02-29T00:00',   false ],
+    // stepUp() on '23:59' gives '00:00'.
+    [ '2017-02-07T23:59',  null,  null,  null,  1,    '2017-02-08T00:00',   false ],
+    [ '2017-02-07T23:59',  null,  null,  null,  3,    '2017-02-08T00:02',   false ],
+    // Some random step values..
+    [ '2017-02-07T17:40',  '0.5',    null,               null,  null, '2017-02-07T17:40:00.500', false ],
+    [ '2017-02-07T17:40',  '2',      null,               null,  null, '2017-02-07T17:40:02',     false ],
+    [ '2017-02-07T17:40',  '0.25',   null,               null,  4,    '2017-02-07T17:40:01',     false ],
+    [ '2017-02-07T17:40',  '1.1',    '2017-02-07T17:00', null,  1,    '2017-02-07T17:40:00.200', false ],
+    [ '2017-02-07T17:40',  '1.1',    '2017-02-07T17:00', null,  2,    '2017-02-07T17:40:01.300', false ],
+    [ '2017-02-07T17:40',  '1.1',    '2017-02-07T17:00', null,  10,   '2017-02-07T17:40:10.100', false ],
+    [ '2017-02-07T17:40',  '129600', '2017-02-01T00:00', null,  2,    '2017-02-10T00:00',        false ],
+    // step = 0 isn't allowed (-> step = 1).
+    [ '2017-02-07T17:39',  '0',   null,  null,  null, '2017-02-07T17:40',   false ],
+    // step < 0 isn't allowed (-> step = 1).
+    [ '2017-02-07T17:39',  '-1',  null,  null,  null, '2017-02-07T17:40',   false ],
+    // step = NaN isn't allowed (-> step = 1).
+    [ '2017-02-07T17:39',  'foo', null,  null,  null, '2017-02-07T17:40',   false ],
+    // Min values testing.
+    [ '2012-02-02T17:00',  '60',     'foo',              null,  2,     '2012-02-02T17:02',  false ],
+    [ '2012-02-02T17:10',  '60',     '2012-02-02T17:10', null,  null,  '2012-02-02T17:11',  false ],
+    [ '2012-02-02T17:10',  '60',     '2012-02-02T17:30', null,  1,     '2012-02-02T17:30',  false ],
+    [ '2012-02-02T17:10',  '180',    '2012-02-02T17:05', null,  null,  '2012-02-02T17:11',  false ],
+    [ '2012-02-02T17:10',  '86400',  '2012-02-02T17:05', null,  null,  '2012-02-03T17:05',  false ],
+    [ '2012-02-02T17:10',  '129600', '2012-02-01T00:00', null,  null,  '2012-02-04T00:00',  false ],
+    // Max values testing.
+    [ '2012-02-02T17:15',  '60',     null,  'foo',               null,  '2012-02-02T17:16',  false ],
+    [ '2012-02-02T17:15',  null,     null,  '2012-02-02T17:20',  null,  '2012-02-02T17:16',  false ],
+    [ '2012-02-02T17:15',  null,     null,  '2012-02-02T17:15',  null,  '2012-02-02T17:15',  false ],
+    [ '2012-02-02T17:15',  null,     null,  '2012-02-02T17:13',  4,     '2012-02-02T17:15',  false ],
+    [ '2012-02-02T20:05',  '86400',  null,  '2012-02-03T20:05',  null,  '2012-02-03T20:05',  false ],
+    [ '2012-02-02T18:00',  '129600', null,  '2012-02-04T20:00',  null,  '2012-02-04T06:00',  false ],
+    // Step mismatch.
+    [ '2017-02-07T17:19',  '120',  '2017-02-07T17:10',   null,               null,  '2017-02-07T17:20',  false ],
+    [ '2017-02-07T17:19',  '120',  '2017-02-07T17:10',   null,               2,     '2017-02-07T17:22',  false ],
+    [ '2017-02-07T17:19',  '120',  '2017-02-07T17:18',   '2017-02-07T17:25', null,  '2017-02-07T17:20',  false ],
+    [ '2017-02-07T17:19',  '120',  null,                 null,               null,  '2017-02-07T17:21',  false ],
+    [ '2017-02-07T17:19',  '180',  null,                 null,               null,  '2017-02-07T17:22',  false ],
+    [ '2017-02-03T17:19',  '172800', '2017-02-02T17:19', '2017-02-10T17:19', null,  '2017-02-04T17:19',  false ],
+    // Clamping.
+    [ '2017-02-07T17:22',  null,   null,                '2017-02-07T17:11', null,  '2017-02-07T17:22',  false ],
+    [ '2017-02-07T17:22',  '120',  '2017-02-07T17:20',  '2017-02-07T17:22', null,  '2017-02-07T17:22',  false ],
+    [ '2017-02-07T17:22',  '300',  '2017-02-07T17:12',  '2017-02-07T17:20', 10,    '2017-02-07T17:22',  false ],
+    [ '2017-02-07T17:22',  '300',  '2017-02-07T17:18',  '2017-02-07T17:20', 2,     '2017-02-07T17:22',  false ],
+    [ '2017-02-06T17:22',  '600',  '2017-02-02T17:00',  '2017-02-07T17:20', 15,    '2017-02-06T19:50',  false ],
+    [ '2017-02-06T17:22',  '600',  '2017-02-02T17:10',  '2017-02-07T17:20', 2,     '2017-02-06T17:40',  false ],
+    // value = "" (NaN).
+    [ '',   null,   null,  null,  null, '1970-01-01T00:01',    false ],
+    // With step = 'any'.
+    [ '2017-02-07T17:30',  'any',  null,  null,  1,  null,  true ],
+    [ '2017-02-07T17:30',  'ANY',  null,  null,  1,  null,  true ],
+    [ '2017-02-07T17:30',  'AnY',  null,  null,  1,  null,  true ],
+    [ '2017-02-07T17:30',  'aNy',  null,  null,  1,  null,  true ],
+  ]},
   ];
 
   for (var test of testData) {
     for (var data of test.data) {
       var element = document.createElement("input");
       element.type = test.type;
 
       if (data[1] != null) {
--- a/dom/ipc/ContentPrefs.cpp
+++ b/dom/ipc/ContentPrefs.cpp
@@ -37,16 +37,17 @@ const char* mozilla::dom::ContentPrefs::
   "dom.enable_resource_timing",
   "dom.event.handling-user-input-time-limit",
   "dom.event.touch.coalescing.enabled",
   "dom.forms.autocomplete.experimental",
   "dom.ipc.processPriorityManager.backgroundGracePeriodMS",
   "dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS",
   "dom.max_chrome_script_run_time",
   "dom.max_script_run_time",
+  "dom.mozBrowserFramesEnabled",
   "dom.performance.enable_notify_performance_timing",
   "dom.performance.enable_user_timing_logging",
   "dom.storage.testing",
   "dom.url.encode_decode_hash",
   "dom.url.getters_decode_hash",
   "dom.use_watchdog",
   "dom.vibrator.enabled",
   "dom.vibrator.max_vibrate_list_len",
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -155,17 +155,17 @@ parent:
 
   async Profile(nsCString aProfile);
 
   // Answers to request about site data
   async ReturnClearSiteData(NPError aRv, uint64_t aCallbackId);
 
   async ReturnSitesWithData(nsCString[] aSites, uint64_t aCallbackId);
 
-  sync GetKeyState(int32_t aVirtKey)
+  intr GetKeyState(int32_t aVirtKey)
     returns (int16_t aState);
 
   intr NPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(bool shouldRegister)
     returns (NPError result);
 };
 
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -2113,17 +2113,17 @@ PMCGetKeyState(int aVirtKey)
                           GetLastError()));
         MOZ_ASSERT(err != WAIT_FAILED);
         CloseHandle(semaphore);
         return 0;
     }
     PluginModuleChild* chromeInstance = PluginModuleChild::GetChrome();
     if (chromeInstance) {
         int16_t ret = 0;
-        if (chromeInstance->SendGetKeyState(aVirtKey, &ret)) {
+        if (chromeInstance->CallGetKeyState(aVirtKey, &ret)) {
           return ret;
         }
     }
     return sGetKeyStatePtrStub(aVirtKey);
 }
 #endif
 
 PPluginInstanceChild*
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -3398,24 +3398,24 @@ PluginModuleChromeParent::RecvProfile(co
 
     mProfile = aProfile;
     mGatherer->GatheredOOPProfile();
 #endif
     return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-PluginModuleParent::RecvGetKeyState(const int32_t& aVirtKey, int16_t* aRet)
+PluginModuleParent::AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet)
 {
     return IPC_FAIL_NO_REASON(this);
 }
 
 mozilla::ipc::IPCResult
-PluginModuleChromeParent::RecvGetKeyState(const int32_t& aVirtKey,
+PluginModuleChromeParent::AnswerGetKeyState(const int32_t& aVirtKey,
                                             int16_t* aRet)
 {
 #if defined(XP_WIN)
     *aRet = ::GetKeyState(aVirtKey);
     return IPC_OK();
 #else
-    return PluginModuleParent::RecvGetKeyState(aVirtKey, aRet);
+    return PluginModuleParent::AnswerGetKeyState(aVirtKey, aRet);
 #endif
 }
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -208,17 +208,17 @@ protected:
     static void TimeoutChanged(const char* aPref, void* aModule);
 
     virtual void UpdatePluginTimeout() {}
 
     virtual mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() override { return IPC_OK(); }
 
     virtual mozilla::ipc::IPCResult RecvProfile(const nsCString& aProfile) override { return IPC_OK(); }
 
-    virtual mozilla::ipc::IPCResult RecvGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
+    virtual mozilla::ipc::IPCResult AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
 
     virtual mozilla::ipc::IPCResult RecvReturnClearSiteData(const NPError& aRv,
                                                             const uint64_t& aCallbackId) override;
 
     virtual mozilla::ipc::IPCResult RecvReturnSitesWithData(nsTArray<nsCString>&& aSites,
                                                             const uint64_t& aCallbackId) override;
 
     void SetPluginFuncs(NPPluginFuncs* aFuncs);
@@ -502,17 +502,17 @@ class PluginModuleChromeParent
     void StartProfiler(nsIProfilerStartParams* aParams);
     void StopProfiler();
 #endif
 
     virtual mozilla::ipc::IPCResult
     RecvProfile(const nsCString& aProfile) override;
 
     virtual mozilla::ipc::IPCResult
-    RecvGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
+    AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override;
 
 private:
     virtual void
     EnteredCxxStack() override;
 
     void
     ExitedCxxStack() override;
 
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -119,17 +119,19 @@ skip-if = (toolkit == "cocoa" && e10s) #
 skip-if = toolkit != "gtk2"
 support-files = plugin_scroll_invalidation.html
 [test_plugin_scroll_painting.html]
 skip-if = true # Bug 596491
 [test_pluginstream_asfile.html]
 [test_pluginstream_asfileonly.html]
 [test_pluginstream_err.html]
 [test_pluginstream_geturl.html]
+skip-if = true # Bug 1267432
 [test_pluginstream_geturlnotify.html]
+skip-if = true # Bug 1267432
 [test_pluginstream_newstream.html]
 [test_pluginstream_post.html]
 [test_pluginstream_poststream.html]
 [test_pluginstream_referer.html]
 [test_pluginstream_seek.html]
 [test_pluginstream_seek_close.html]
 [test_pluginstream_src.html]
 [test_pluginstream_src_dynamic.html]
--- a/dom/tests/mochitest/general/resource_timing_cross_origin.html
+++ b/dom/tests/mochitest/general/resource_timing_cross_origin.html
@@ -165,23 +165,23 @@ function finishTests() {
 <body>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=822480"
      title="Add resource timing API.">
     Bug #822480 -  Add in the Resource Timing API
   </a>
   <p id="display"></p>
   <div id="content">
-    <object data="http://mochi.test:8888/tests/dom/tests/mochitest/general/res0.resource"> <!-- same origin, no header -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res0.resource"> <!-- cross origin, no header -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res1.resource"> <!-- cross origin, Timing-Allow-Origin: * header -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res2.resource"> <!-- cross origin redirect to test2.example.com, no header -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res3.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8888 header -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res4.resource"> <!-- cross origin redirect to mochi.test:8888/.../res1.resource, Timing-Allow-Origin: * -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res5.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8889 -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res6.resource"> <!-- cross origin, Timing-Allow-Origin: "" (empty string) -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res7.resource"> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8888 http://test1.com header -->
-    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res8.resource"> <!-- double cross origin redirect -->
+    <object data="http://mochi.test:8888/tests/dom/tests/mochitest/general/res0.resource"></object> <!-- same origin, no header -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res0.resource"></object> <!-- cross origin, no header -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res1.resource"></object> <!-- cross origin, Timing-Allow-Origin: * header -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res2.resource"></object> <!-- cross origin redirect to test2.example.com, no header -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res3.resource"></object> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8888 header -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res4.resource"></object> <!-- cross origin redirect to mochi.test:8888/.../res1.resource, Timing-Allow-Origin: * -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res5.resource"></object> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8889 -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res6.resource"></object> <!-- cross origin, Timing-Allow-Origin: "" (empty string) -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res7.resource"></object> <!-- cross origin, Timing-Allow-Origin: http://mochi.test:8888 http://test1.com header -->
+    <object data="http://test1.example.com/tests/dom/tests/mochitest/general/res8.resource"></object> <!-- double cross origin redirect -->
     <script type="text/javascript" src="http://mochi.test:8888/tests/dom/tests/mochitest/general/resource_timing.js"></script> <!-- same origin script -->
   </div>
 </body>
 
 </html>
--- a/dom/tests/mochitest/general/test_focusrings.xul
+++ b/dom/tests/mochitest/general/test_focusrings.xul
@@ -163,13 +163,13 @@ SimpleTest.waitForFocus(runTest);
 
 <listbox id="l1" class="plain" height="20"/>
 <listbox id="l2" class="plain" height="20"/>
 <listbox id="l3" class="plain" height="20"/>
 <button id="b1" label="Button"/>
 <button id="b2" label="Button"/>
 <button id="b3" label="Button"/>
 
-<iframe id="child" src="data:text/html,&lt;html&gt;&lt;style&gt;* { outline: none; -moz-appearance: none; } %23elem:focus { outline: 2px solid red; } %23elem:-moz-focusring { outline: 1px solid blue; }&lt;/style&gt;&lt;div id='container'&gt;&lt;/html&gt;"/>
+<iframe id="child" src="data:text/html,&lt;html&gt;&lt;style&gt;* { outline: none; -moz-appearance: none; min-width:10px; min-height:10px; } %23elem:focus { outline: 2px solid red; } %23elem:-moz-focusring { outline: 1px solid blue; }&lt;/style&gt;&lt;div id='container'&gt;&lt;/html&gt;"/>
 
 <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/>
 
 </window>
--- a/dom/url/URL.h
+++ b/dom/url/URL.h
@@ -146,16 +146,22 @@ public:
   virtual void
   SetHash(const nsAString& aHash, ErrorResult& aRv) = 0;
 
   void Stringify(nsAString& aRetval, ErrorResult& aRv) const
   {
     GetHref(aRetval, aRv);
   }
 
+  void
+  ToJSON(nsAString& aResult, ErrorResult& aRv) const
+  {
+    GetHref(aResult, aRv);
+  }
+
   // URLSearchParamsObserver
   void
   URLSearchParamsUpdated(URLSearchParams* aSearchParams) override;
 
 protected:
   virtual ~URL()
   {}
 
--- a/dom/url/tests/test_url.html
+++ b/dom/url/tests/test_url.html
@@ -445,10 +445,16 @@
     url.search = "";
     is(url.href, "scheme:path#newhash");
 
     // we don't implement a spec-compliant parser yet.
     // make sure we are bug compatible with existing implementations.
     url = new URL("data:text/html,<a href=\"http://example.org/?q\">Link</a>");
     is(url.href, "data:text/html,<a%20href=\"http://example.org/?q\">Link</a>");
   </script>
+
+  <script>
+    var u = new URL('http://www.example.org');
+    ok(u.toJSON(), 'http://www.example.org', "URL.toJSON()");
+    is(JSON.stringify(u), "\"http://www.example.org/\"", "JSON.stringify(u) works");
+  </script>
 </body>
 </html>
--- a/dom/webidl/Blob.webidl
+++ b/dom/webidl/Blob.webidl
@@ -1,21 +1,21 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
- * http://dev.w3.org/2006/webapi/FileAPI/#blob
+ * https://w3c.github.io/FileAPI/#blob
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-typedef (ArrayBuffer or ArrayBufferView or Blob or USVString) BlobPart;
+typedef (BufferSource or Blob or USVString) BlobPart;
 
 [Constructor(optional sequence<BlobPart> blobParts,
              optional BlobPropertyBag options),
  Exposed=(Window,Worker)]
 interface Blob {
 
   [GetterThrows]
   readonly attribute unsigned long long size;
--- a/dom/webidl/Fetch.webidl
+++ b/dom/webidl/Fetch.webidl
@@ -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/.
  *
  * The origin of this IDL file is
  * http://fetch.spec.whatwg.org/
  */
 
 typedef object JSON;
-typedef (ArrayBuffer or ArrayBufferView or Blob or FormData or USVString or URLSearchParams) BodyInit;
+typedef (Blob or BufferSource or FormData or URLSearchParams or USVString) BodyInit;
 
 [NoInterfaceObject, Exposed=(Window,Worker)]
 interface Body {
   readonly attribute boolean bodyUsed;
   [Throws]
   Promise<ArrayBuffer> arrayBuffer();
   [Throws]
   Promise<Blob> blob();
--- a/dom/webidl/MediaKeys.webidl
+++ b/dom/webidl/MediaKeys.webidl
@@ -20,10 +20,10 @@ enum MediaKeySessionType {
 
 interface MediaKeys {
   readonly attribute DOMString keySystem;
 
   [NewObject, Throws]
   MediaKeySession createSession(optional MediaKeySessionType sessionType = "temporary");
 
   [NewObject]
-  Promise<void> setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate);
+  Promise<void> setServerCertificate(BufferSource serverCertificate);
 };
--- a/dom/webidl/URL.webidl
+++ b/dom/webidl/URL.webidl
@@ -39,19 +39,22 @@ interface URL {
   [Throws]
            attribute USVString hostname;
   [Throws]
            attribute USVString port;
   [Throws]
            attribute USVString pathname;
   [Throws]
            attribute USVString search;
-  readonly attribute URLSearchParams searchParams;
+  [SameObject] readonly attribute URLSearchParams searchParams;
   [Throws]
            attribute USVString hash;
+
+  [Throws]
+  USVString toJSON();
 };
 
 partial interface URL {
   [Throws]
   static DOMString createObjectURL(Blob blob);
   [Throws]
   static DOMString createObjectURL(MediaStream stream);
   [Throws]
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -1859,17 +1859,21 @@ XMLHttpRequestMainThread::OnDataAvailabl
       // The nsIStreamListener contract mandates us to read from the stream
       // before returning.
       uint32_t totalRead;
       rv =
         inStr->ReadSegments(DummyStreamReaderFunc, nullptr, count, &totalRead);
       NS_ENSURE_SUCCESS(rv, rv);
 
       ChangeState(State::loading);
-      return request->Cancel(NS_OK);
+
+      // Cancel() must be called with an error. We use
+      // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
+      // just because we can retrieve the File from the channel directly.
+      return request->Cancel(NS_ERROR_FILE_ALREADY_EXISTS);
     }
   }
 
   uint32_t totalRead;
   rv = inStr->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc,
                            (void*)this, count, &totalRead);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2189,17 +2193,20 @@ XMLHttpRequestMainThread::OnStopRequest(
     mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
   }
 
   mXMLParserStreamListener = nullptr;
   mContext = nullptr;
 
   bool waitingForBlobCreation = false;
 
-  if (NS_SUCCEEDED(status) &&
+  // If we have this error, we have to deal with a file: URL + responseType =
+  // blob. We have this error because we canceled the channel. The status will
+  // be set to NS_OK.
+  if (status == NS_ERROR_FILE_ALREADY_EXISTS &&
       (mResponseType == XMLHttpRequestResponseType::Blob ||
        mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
     nsCOMPtr<nsIFile> file;
     nsresult rv = GetLocalFileFromChannel(request, getter_AddRefs(file));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
@@ -2219,51 +2226,58 @@ XMLHttpRequestMainThread::OnStopRequest(
       RefPtr<Promise> promise =
         FileCreatorHelper::CreateFile(global, file, bag, true, error);
       if (NS_WARN_IF(error.Failed())) {
         return error.StealNSResult();
       }
 
       FileCreationHandler::Create(promise, this);
       waitingForBlobCreation = true;
+      status = NS_OK;
+
+      NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
+      NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
+    }
+  }
+
+  if (NS_SUCCEEDED(status) &&
+      (mResponseType == XMLHttpRequestResponseType::Blob ||
+       mResponseType == XMLHttpRequestResponseType::Moz_blob) &&
+      !waitingForBlobCreation) {
+    // Smaller files may be written in cache map instead of separate files.
+    // Also, no-store response cannot be written in persistent cache.
+    nsAutoCString contentType;
+    mChannel->GetContentType(contentType);
+
+    if (mResponseType == XMLHttpRequestResponseType::Blob) {
+      // mBlobStorage can be null if the channel is non-file non-cacheable
+      // and if the response length is zero.
+      MaybeCreateBlobStorage();
+      mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
+      waitingForBlobCreation = true;
     } else {
-      // No local file.
-
-      // Smaller files may be written in cache map instead of separate files.
-      // Also, no-store response cannot be written in persistent cache.
-      nsAutoCString contentType;
-      mChannel->GetContentType(contentType);
-
-      if (mResponseType == XMLHttpRequestResponseType::Blob) {
-        // mBlobStorage can be null if the channel is non-file non-cacheable
-        // and if the response length is zero.
-        MaybeCreateBlobStorage();
-        mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
-        waitingForBlobCreation = true;
-      } else {
-        // mBlobSet can be null if the channel is non-file non-cacheable
-        // and if the response length is zero.
-        if (!mBlobSet) {
-          mBlobSet = new BlobSet();
-        }
-
-        ErrorResult error;
-        nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
-        RefPtr<BlobImpl> blobImpl =
-          MultipartBlobImpl::Create(Move(subImpls),
-                                    NS_ConvertASCIItoUTF16(contentType),
-                                    error);
-        mBlobSet = nullptr;
-
-        if (NS_WARN_IF(error.Failed())) {
-          return error.StealNSResult();
-        }
-
-        mResponseBlob = Blob::Create(GetOwner(), blobImpl);
+      // mBlobSet can be null if the channel is non-file non-cacheable
+      // and if the response length is zero.
+      if (!mBlobSet) {
+        mBlobSet = new BlobSet();
       }
+
+      ErrorResult error;
+      nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
+      RefPtr<BlobImpl> blobImpl =
+        MultipartBlobImpl::Create(Move(subImpls),
+                                  NS_ConvertASCIItoUTF16(contentType),
+                                  error);
+      mBlobSet = nullptr;
+
+      if (NS_WARN_IF(error.Failed())) {
+        return error.StealNSResult();
+      }
+
+      mResponseBlob = Blob::Create(GetOwner(), blobImpl);
     }
 
     NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
     NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
   } else if (NS_SUCCEEDED(status) &&
              ((mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
                !mIsMappedArrayBuffer) ||
               mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -1310,22 +1310,16 @@ EventRunnable::WorkerRun(JSContext* aCx,
       return true;
     }
   }
 
   if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
     return true;
   }
 
-  JS::Rooted<JSString*> type(aCx,
-    JS_NewUCStringCopyN(aCx, mType.get(), mType.Length()));
-  if (!type) {
-    return false;
-  }
-
   XMLHttpRequestEventTarget* target;
   if (mUploadEvent) {
     target = xhr->GetUploadObjectNoCreate();
   }
   else {
     target = xhr;
   }
 
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -669,23 +669,20 @@ EditorBase::GetSelection(SelectionType a
   return sel->AsSelection();
 }
 
 NS_IMETHODIMP
 EditorBase::DoTransaction(nsITransaction* aTxn)
 {
   if (mPlaceHolderBatch && !mPlaceHolderTxn) {
     nsCOMPtr<nsIAbsorbingTransaction> placeholderTransaction =
-      new PlaceholderTransaction();
+      new PlaceholderTransaction(*this, mPlaceHolderName, Move(mSelState));
 
     // Save off weak reference to placeholder transaction
     mPlaceHolderTxn = do_GetWeakReference(placeholderTransaction);
-    placeholderTransaction->Init(mPlaceHolderName, mSelState, this);
-    // placeholder txn took ownership of this pointer
-    mSelState = nullptr;
 
     // QI to an nsITransaction since that's what DoTransaction() expects
     nsCOMPtr<nsITransaction> transaction =
       do_QueryInterface(placeholderTransaction);
     // We will recurse, but will not hit this case in the nested call
     DoTransaction(transaction);
 
     if (mTxnMgr) {
@@ -920,17 +917,17 @@ EditorBase::BeginPlaceHolderTransaction(
   if (!mPlaceHolderBatch) {
     NotifyEditorObservers(eNotifyEditorObserversOfBefore);
     // time to turn on the batch
     BeginUpdateViewBatch();
     mPlaceHolderTxn = nullptr;
     mPlaceHolderName = aName;
     RefPtr<Selection> selection = GetSelection();
     if (selection) {
-      mSelState = new SelectionState();
+      mSelState = MakeUnique<SelectionState>();
       mSelState->SaveSelection(selection);
       // Composition transaction can modify multiple nodes and it merges text
       // node for ime into single text node.
       // So if current selection is into IME text node, it might be failed
       // to restore selection by UndoTransaction.
       // So we need update selection by range updater.
       if (mPlaceHolderName == nsGkAtoms::IMETxnName) {
         mRangeUpdater.RegisterSelectionState(*mSelState);
@@ -984,17 +981,16 @@ EditorBase::EndPlaceHolderTransaction()
     }
 
     if (mSelState) {
       // we saved the selection state, but never got to hand it to placeholder
       // (else we ould have nulled out this pointer), so destroy it to prevent leaks.
       if (mPlaceHolderName == nsGkAtoms::IMETxnName) {
         mRangeUpdater.DropSelectionState(*mSelState);
       }
-      delete mSelState;
       mSelState = nullptr;
     }
     // We might have never made a placeholder if no action took place.
     if (mPlaceHolderTxn) {
       nsCOMPtr<nsIAbsorbingTransaction> plcTxn = do_QueryReferent(mPlaceHolderTxn);
       if (plcTxn) {
         plcTxn->EndPlaceHolderBatch();
       } else {
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -6,16 +6,17 @@
 #ifndef mozilla_EditorBase_h
 #define mozilla_EditorBase_h
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc.
 #include "mozilla/FlushType.h"          // for FlushType enum
 #include "mozilla/OwningNonNull.h"      // for OwningNonNull
 #include "mozilla/SelectionState.h"     // for RangeUpdater, etc.
 #include "mozilla/StyleSheet.h"   // for StyleSheet
+#include "mozilla/UniquePtr.h"
 #include "mozilla/dom/Text.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed, nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsGkAtoms.h"
 #include "nsIEditor.h"                  // for nsIEditor::EDirection, etc.
 #include "nsIEditorIMESupport.h"        // for NS_DECL_NSIEDITORIMESUPPORT, etc.
 #include "nsIObserver.h"                // for NS_DECL_NSIOBSERVER, etc.
 #include "nsIPhonetic.h"                // for NS_DECL_NSIPHONETIC, etc.
@@ -991,17 +992,17 @@ protected:
   nsWeakPtr mSelConWeak;
   // Weak reference to placeholder for begin/end batch purposes.
   nsWeakPtr mPlaceHolderTxn;
   // Weak reference to the nsIDOMDocument.
   nsWeakPtr mDocWeak;
   // Name of placeholder transaction.
   nsIAtom* mPlaceHolderName;
   // Saved selection state for placeholder transaction batching.
-  SelectionState* mSelState;
+  mozilla::UniquePtr<SelectionState> mSelState;
   nsString* mPhonetic;
   // IME composition this is not null between compositionstart and
   // compositionend.
   RefPtr<TextComposition> mComposition;
 
   // Listens to all low level actions on the doc.
   typedef AutoTArray<OwningNonNull<nsIEditActionListener>, 5>
             AutoActionListenerArray;
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -11,23 +11,24 @@
 #include "HTMLEditUtils.h"
 #include "TextEditUtils.h"
 #include "WSRunObject.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/CSSEditUtils.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/mozalloc.h"
-#include "nsAutoPtr.h"
 #include "nsAString.h"
 #include "nsAlgorithm.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
@@ -4503,30 +4504,31 @@ HTMLEditRules::CreateStyleForInsertText(
                                          propItem->value);
     }
   }
 
   nsCOMPtr<Element> rootElement = aDoc.GetRootElement();
   NS_ENSURE_STATE(rootElement);
 
   // process clearing any styles first
-  nsAutoPtr<PropItem> item(mHTMLEditor->mTypeInState->TakeClearProperty());
+  UniquePtr<PropItem> item =
+    Move(mHTMLEditor->mTypeInState->TakeClearProperty());
   while (item && node != rootElement) {
     NS_ENSURE_STATE(mHTMLEditor);
     nsresult rv =
       mHTMLEditor->ClearStyle(address_of(node), &offset,
                               item->tag, &item->attr);
     NS_ENSURE_SUCCESS(rv, rv);
-    item = mHTMLEditor->mTypeInState->TakeClearProperty();
+    item = Move(mHTMLEditor->mTypeInState->TakeClearProperty());
     weDidSomething = true;
   }
 
   // then process setting any styles
   int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize();
-  item = mHTMLEditor->mTypeInState->TakeSetProperty();
+  item = Move(mHTMLEditor->mTypeInState->TakeSetProperty());
 
   if (item || relFontSize) {
     // we have at least one style to add; make a new text node to insert style
     // nodes above.
     if (RefPtr<Text> text = node->GetAsText()) {
       // if we are in a text node, split it
       NS_ENSURE_STATE(mHTMLEditor);
       offset = mHTMLEditor->SplitNodeDeep(*text, *text, offset);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -275,17 +275,17 @@ HTMLEditor::Init(nsIDOMDocument* aDoc,
 
     // disable Composer-only features
     if (IsMailEditor()) {
       SetAbsolutePositioningEnabled(false);
       SetSnapToGridEnabled(false);
     }
 
     // Init the HTML-CSS utils
-    mCSSEditUtils = new CSSEditUtils(this);
+    mCSSEditUtils = MakeUnique<CSSEditUtils>(this);
 
     // disable links
     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
     nsPresContext *context = presShell->GetPresContext();
     NS_ENSURE_TRUE(context, NS_ERROR_NULL_POINTER);
     if (!IsPlaintextEditor() && !IsInteractionAllowed()) {
       mLinkHandler = context->GetLinkHandler();
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -5,21 +5,21 @@
 
 #ifndef mozilla_HTMLEditor_h
 #define mozilla_HTMLEditor_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/CSSEditUtils.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/TextEditor.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 
 #include "nsAttrName.h"
-#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIContentFilter.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsIDocumentObserver.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMEventListener.h"
 #include "nsIEditor.h"
 #include "nsIEditorMailSupport.h"
@@ -817,17 +817,17 @@ protected:
 protected:
   nsTArray<OwningNonNull<nsIContentFilter>> mContentFilters;
 
   RefPtr<TypeInState> mTypeInState;
 
   bool mCRInParagraphCreatesParagraph;
 
   bool mCSSAware;
-  nsAutoPtr<CSSEditUtils> mCSSEditUtils;
+  UniquePtr<CSSEditUtils> mCSSEditUtils;
 
   // Used by GetFirstSelectedCell and GetNextSelectedCell
   int32_t  mSelectedCellIndex;
 
   nsString mLastStyleSheetURL;
   nsString mLastOverrideStyleSheetURL;
 
   // Maintain a list of associated style sheets and their urls.
--- a/editor/libeditor/PlaceholderTransaction.cpp
+++ b/editor/libeditor/PlaceholderTransaction.cpp
@@ -3,30 +3,36 @@
  * 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 "PlaceholderTransaction.h"
 
 #include "CompositionTransaction.h"
 #include "mozilla/EditorBase.h"
 #include "mozilla/dom/Selection.h"
+#include "mozilla/Move.h"
 #include "nsGkAtoms.h"
 #include "nsQueryObject.h"
 
 namespace mozilla {
 
 using namespace dom;
 
-PlaceholderTransaction::PlaceholderTransaction()
+PlaceholderTransaction::PlaceholderTransaction(
+                          EditorBase& aEditorBase,
+                          nsIAtom* aName,
+                          UniquePtr<SelectionState> aSelState)
   : mAbsorb(true)
   , mForwarding(nullptr)
   , mCompositionTransaction(nullptr)
   , mCommitted(false)
-  , mEditorBase(nullptr)
+  , mStartSel(Move(aSelState))
+  , mEditorBase(aEditorBase)
 {
+  mName = aName;
 }
 
 PlaceholderTransaction::~PlaceholderTransaction()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PlaceholderTransaction)
 
@@ -50,58 +56,45 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIAbsorbingTransaction)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END_INHERITING(EditAggregateTransaction)
 
 NS_IMPL_ADDREF_INHERITED(PlaceholderTransaction, EditAggregateTransaction)
 NS_IMPL_RELEASE_INHERITED(PlaceholderTransaction, EditAggregateTransaction)
 
 NS_IMETHODIMP
-PlaceholderTransaction::Init(nsIAtom* aName,
-                             SelectionState* aSelState,
-                             EditorBase* aEditorBase)
-{
-  NS_ENSURE_TRUE(aEditorBase && aSelState, NS_ERROR_NULL_POINTER);
-
-  mName = aName;
-  mStartSel = aSelState;
-  mEditorBase = aEditorBase;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 PlaceholderTransaction::DoTransaction()
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PlaceholderTransaction::UndoTransaction()
 {
   // Undo transactions.
   nsresult rv = EditAggregateTransaction::UndoTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ENSURE_TRUE(mStartSel, NS_ERROR_NULL_POINTER);
 
   // now restore selection
-  RefPtr<Selection> selection = mEditorBase->GetSelection();
+  RefPtr<Selection> selection = mEditorBase.GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   return mStartSel->RestoreSelection(selection);
 }
 
 NS_IMETHODIMP
 PlaceholderTransaction::RedoTransaction()
 {
   // Redo transactions.
   nsresult rv = EditAggregateTransaction::RedoTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // now restore selection
-  RefPtr<Selection> selection = mEditorBase->GetSelection();
+  RefPtr<Selection> selection = mEditorBase.GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   return mEndSel.RestoreSelection(selection);
 }
 
 
 NS_IMETHODIMP
 PlaceholderTransaction::Merge(nsITransaction* aTransaction,
                               bool* aDidMerge)
@@ -256,15 +249,15 @@ PlaceholderTransaction::Commit()
 {
   mCommitted = true;
   return NS_OK;
 }
 
 nsresult
 PlaceholderTransaction::RememberEndingSelection()
 {
-  RefPtr<Selection> selection = mEditorBase->GetSelection();
+  RefPtr<Selection> selection = mEditorBase.GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   mEndSel.SaveSelection(selection);
   return NS_OK;
 }
 
 } // namespace mozilla
--- a/editor/libeditor/PlaceholderTransaction.h
+++ b/editor/libeditor/PlaceholderTransaction.h
@@ -3,22 +3,22 @@
  * 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 PlaceholderTransaction_h
 #define PlaceholderTransaction_h
 
 #include "EditAggregateTransaction.h"
 #include "mozilla/EditorUtils.h"
+#include "mozilla/UniquePtr.h"
 #include "nsIAbsorbingTransaction.h"
 #include "nsIDOMNode.h"
 #include "nsCOMPtr.h"
 #include "nsWeakPtr.h"
 #include "nsWeakReference.h"
-#include "nsAutoPtr.h"
 
 namespace mozilla {
 
 class CompositionTransaction;
 
 /**
  * An aggregate transaction that knows how to absorb all subsequent
  * transactions with the same name.  This transaction does not "Do" anything.
@@ -28,32 +28,30 @@ class CompositionTransaction;
 
 class PlaceholderTransaction final : public EditAggregateTransaction,
                                      public nsIAbsorbingTransaction,
                                      public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
-  PlaceholderTransaction();
+  PlaceholderTransaction(EditorBase& aEditorBase, nsIAtom* aName,
+                         UniquePtr<SelectionState> aSelState);
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PlaceholderTransaction,
                                            EditAggregateTransaction)
 // ------------ EditAggregateTransaction -----------------------
 
   NS_DECL_EDITTRANSACTIONBASE
 
   NS_IMETHOD RedoTransaction() override;
   NS_IMETHOD Merge(nsITransaction* aTransaction, bool* aDidMerge) override;
 
 // ------------ nsIAbsorbingTransaction -----------------------
 
-  NS_IMETHOD Init(nsIAtom* aName, SelectionState* aSelState,
-                  EditorBase* aEditorBase) override;
-
   NS_IMETHOD GetTxnName(nsIAtom** aName) override;
 
   NS_IMETHOD StartSelectionEquals(SelectionState* aSelState,
                                   bool* aResult) override;
 
   NS_IMETHOD EndPlaceHolderBatch() override;
 
   NS_IMETHOD ForwardEndBatchTo(
@@ -75,18 +73,18 @@ protected:
   bool mCommitted;
 
   // These next two members store the state of the selection in a safe way.
   // Selection at the start of the transaction is stored, as is the selection
   // at the end.  This is so that UndoTransaction() and RedoTransaction() can
   // restore the selection properly.
 
   // Use a pointer because this is constructed before we exist.
-  nsAutoPtr<SelectionState> mStartSel;
+  UniquePtr<SelectionState> mStartSel;
   SelectionState mEndSel;
 
   // The editor for this transaction.
-  EditorBase* mEditorBase;
+  EditorBase& mEditorBase;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef PlaceholderTransaction_h
--- a/editor/libeditor/TypeInState.cpp
+++ b/editor/libeditor/TypeInState.cpp
@@ -188,45 +188,45 @@ TypeInState::ClearProp(nsIAtom* aProp,
   mClearedArray.AppendElement(item);
 }
 
 
 /**
  * TakeClearProperty() hands back next property item on the clear list.
  * Caller assumes ownership of PropItem and must delete it.
  */
-PropItem*
+UniquePtr<PropItem>
 TypeInState::TakeClearProperty()
 {
   size_t count = mClearedArray.Length();
   if (!count) {
     return nullptr;
   }
 
   --count; // indices are zero based
   PropItem* propItem = mClearedArray[count];
   mClearedArray.RemoveElementAt(count);
-  return propItem;
+  return UniquePtr<PropItem>(propItem);
 }
 
 /**
  * TakeSetProperty() hands back next poroperty item on the set list.
  * Caller assumes ownership of PropItem and must delete it.
  */
-PropItem*
+UniquePtr<PropItem>
 TypeInState::TakeSetProperty()
 {
   size_t count = mSetArray.Length();
   if (!count) {
     return nullptr;
   }
   count--; // indices are zero based
   PropItem* propItem = mSetArray[count];
   mSetArray.RemoveElementAt(count);
-  return propItem;
+  return UniquePtr<PropItem>(propItem);
 }
 
 /**
  * TakeRelativeFontSize() hands back relative font value, which is then
  * cleared out.
  */
 int32_t
 TypeInState::TakeRelativeFontSize()
--- a/editor/libeditor/TypeInState.h
+++ b/editor/libeditor/TypeInState.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef TypeInState_h
 #define TypeInState_h
 
+#include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISelectionListener.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nscore.h"
 
@@ -58,23 +59,23 @@ public:
 
   void ClearAllProps();
   void ClearProp(nsIAtom* aProp, const nsAString& aAttr);
 
   /**
    * TakeClearProperty() hands back next property item on the clear list.
    * Caller assumes ownership of PropItem and must delete it.
    */
-  PropItem* TakeClearProperty();
+  UniquePtr<PropItem> TakeClearProperty();
 
   /**
    * TakeSetProperty() hands back next property item on the set list.
    * Caller assumes ownership of PropItem and must delete it.
    */
-  PropItem* TakeSetProperty();
+  UniquePtr<PropItem> TakeSetProperty();
 
   /**
    * TakeRelativeFontSize() hands back relative font value, which is then
    * cleared out.
    */
   int32_t TakeRelativeFontSize();
 
   void GetTypingState(bool& isSet, bool& theSetting, nsIAtom* aProp);
--- a/editor/libeditor/nsIAbsorbingTransaction.h
+++ b/editor/libeditor/nsIAbsorbingTransaction.h
@@ -30,19 +30,16 @@ class SelectionState;
  * A transaction interface mixin - for transactions that can support.
  * the placeholder absorbtion idiom.
  */
 class nsIAbsorbingTransaction  : public nsISupports{
 public:
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IABSORBINGTRANSACTION_IID)
 
-  NS_IMETHOD Init(nsIAtom* aName, mozilla::SelectionState* aSelState,
-                  mozilla::EditorBase* aEditorBase) = 0;
-
   NS_IMETHOD EndPlaceHolderBatch()=0;
 
   NS_IMETHOD GetTxnName(nsIAtom **aName)=0;
 
   NS_IMETHOD StartSelectionEquals(mozilla::SelectionState* aSelState,
                                   bool* aResult) = 0;
 
   NS_IMETHOD ForwardEndBatchTo(nsIAbsorbingTransaction *aForwardingAddress)=0;
--- a/editor/reftests/reftest-stylo.list
+++ b/editor/reftests/reftest-stylo.list
@@ -83,17 +83,17 @@ fails needs-focus == spellcheck-non-lati
 fails needs-focus == spellcheck-non-latin-korean.html spellcheck-non-latin-korean.html
 fails == unneeded_scroll.html unneeded_scroll.html
 # == caret_on_presshell_reinit.html caret_on_presshell_reinit.html
 # == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-2.html
 fails == 642800.html 642800.html
 fails == selection_visibility_after_reframe.html selection_visibility_after_reframe.html
 fails == selection_visibility_after_reframe-2.html selection_visibility_after_reframe-2.html
 fails == selection_visibility_after_reframe-3.html selection_visibility_after_reframe-3.html
-fails == 672709.html 672709.html
+== 672709.html 672709.html
 fails == 338427-1.html 338427-1.html
 fails == 674212-spellcheck.html 674212-spellcheck.html
 fails == 338427-2.html 338427-2.html
 fails == 338427-3.html 338427-3.html
 fails == 462758-grabbers-resizers.html 462758-grabbers-resizers.html
 fails == readwrite-non-editable.html readwrite-non-editable.html
 fails == readwrite-editable.html readwrite-editable.html
 fails == readonly-non-editable.html readonly-non-editable.html
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -2422,16 +2422,23 @@ public:
 
   virtual void SetWidths(const BorderWidths& aWidths)
   {
     MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Widths", this));
     PodCopy(&mWidths[0], &aWidths[0], 4);
     Mutated();
   }
 
+  virtual void SetStyles(const BorderStyles& aBorderStyles)
+  {
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Widths", this));
+    PodCopy(&mBorderStyles[0], &aBorderStyles[0], 4);
+    Mutated();
+  }
+
   MOZ_LAYER_DECL_NAME("BorderLayer", TYPE_BORDER)
 
   virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
   {
     gfx::Matrix4x4 idealTransform = GetLocalTransform() * aTransformToSurface;
     mEffectiveTransform = SnapTransformTranslation(idealTransform, nullptr);
     ComputeEffectiveTransformForMaskLayers(aTransformToSurface);
   }
@@ -2449,16 +2456,17 @@ protected:
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
 
   virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override;
 
   BorderColors mColors;
   LayerRect mRect;
   BorderCorners mCorners;
   BorderWidths mWidths;
+  BorderStyles mBorderStyles;
 };
 
 /**
  * A Layer for HTML Canvas elements.  It's backed by either a
  * gfxASurface or a GLContext (for WebGL layers), and has some control
  * for intelligent updating from the source if necessary (for example,
  * if hardware compositing is not available, for reading from the GL
  * buffer into an image surface that we can layer composite.)
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -244,16 +244,17 @@ typedef gfx::Matrix4x4Typed<LayerPixel, 
 // matrices, a ViewAs operation is needed. A MultipleAsyncTransforms
 // PixelCastJustification is provided for this purpose.
 typedef gfx::Matrix4x4Typed<ParentLayerPixel, ParentLayerPixel> AsyncTransformComponentMatrix;
 typedef gfx::Matrix4x4Typed<CSSTransformedLayerPixel, ParentLayerPixel> AsyncTransformMatrix;
 
 typedef Array<gfx::Color, 4> BorderColors;
 typedef Array<LayerSize, 4> BorderCorners;
 typedef Array<LayerCoord, 4> BorderWidths;
+typedef Array<uint8_t, 4> BorderStyles;
 
 // This is used to communicate Layers across IPC channels. The Handle is valid
 // for layers in the same PLayerTransaction. Handles are created by ClientLayerManager,
 // and are cached in LayerTransactionParent on first use.
 class LayerHandle
 {
   friend struct IPC::ParamTraits<mozilla::layers::LayerHandle>;
 public:
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -306,41 +306,39 @@ ParentLayerCoord Axis::PanDistance(Paren
 
 void Axis::EndTouch(uint32_t aTimestampMs) {
   // mVelocityQueue is controller-thread only
   APZThreadUtils::AssertOnControllerThread();
 
   mAxisLocked = false;
   mVelocity = 0;
   int count = 0;
-  while (!mVelocityQueue.IsEmpty()) {
-    uint32_t timeDelta = (aTimestampMs - mVelocityQueue[0].first);
+  for (const auto& e : mVelocityQueue) {
+    uint32_t timeDelta = (aTimestampMs - e.first);
     if (timeDelta < gfxPrefs::APZVelocityRelevanceTime()) {
       count++;
-      mVelocity += mVelocityQueue[0].second;
+      mVelocity += e.second;
     }
-    mVelocityQueue.RemoveElementAt(0);
   }
+  mVelocityQueue.Clear();
   if (count > 1) {
     mVelocity /= count;
   }
   AXIS_LOG("%p|%s ending touch, computed velocity %f\n",
     mAsyncPanZoomController, Name(), mVelocity);
 }
 
 void Axis::CancelGesture() {
   // mVelocityQueue is controller-thread only
   APZThreadUtils::AssertOnControllerThread();
 
   AXIS_LOG("%p|%s cancelling touch, clearing velocity queue\n",
     mAsyncPanZoomController, Name());
   mVelocity = 0.0f;
-  while (!mVelocityQueue.IsEmpty()) {
-    mVelocityQueue.RemoveElementAt(0);
-  }
+  mVelocityQueue.Clear();
 }
 
 bool Axis::CanScroll() const {
   return GetPageLength() - GetCompositionLength() > COORDINATE_EPSILON;
 }
 
 bool Axis::CanScroll(ParentLayerCoord aDelta) const
 {
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -102,19 +102,19 @@ load 691581-1.html
 load 693143-1.html
 load 696936-1.html
 load 699563-1.html
 load 710149-1.html
 asserts-if(stylo,1) load 766452-1.html # bug 1324700
 load 766452-2.html
 load 768079-1.html
 asserts-if(stylo,2) load 783041-1.html # bug 1324661
-load 783041-2.html
-asserts-if(stylo,1) load 783041-3.html # bug 1324671
-load 783041-4.html
+asserts-if(stylo,2) load 783041-2.html # bug 1324661
+asserts-if(stylo,3) load 783041-3.html # bug 1324671
+asserts-if(stylo,2) load 783041-4.html # bug 1324661
 load 798853.html # bug 868792
 load 805760-1.html
 skip-if(Android) load 815489.html # bug 1216304
 load 836225-1.html
 load 839745-1.html
 load 856784-1.html
 load 893572-1.html
 load 893572-2.html
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -225,41 +225,52 @@ nsIconChannel::AsyncOpen(nsIStreamListen
              mLoadInfo->GetSecurityMode() == 0 ||
              mLoadInfo->GetInitialSecurityCheckDone() ||
              (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
               nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
              "security flags in loadInfo but asyncOpen2() not called");
 
   nsCOMPtr<nsIInputStream> inStream;
   nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+      mCallbacks = nullptr;
+      return rv;
+  }
 
   // Init our stream pump
   rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+      mCallbacks = nullptr;
+      return rv;
+  }
 
   rv = mPump->AsyncRead(this, ctxt);
   if (NS_SUCCEEDED(rv)) {
     // Store our real listener
     mListener = aListener;
     // Add ourself to the load group, if available
     if (mLoadGroup) {
       mLoadGroup->AddRequest(this, nullptr);
     }
+  } else {
+      mCallbacks = nullptr;
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsIconChannel::AsyncOpen2(nsIStreamListener* aListener)
 {
   nsCOMPtr<nsIStreamListener> listener = aListener;
   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+      mCallbacks = nullptr;
+      return rv;
+  }
   return AsyncOpen(listener, nullptr);
 }
 
 nsresult
 nsIconChannel::MakeInputStream(nsIInputStream** _retval,
                                         bool aNonBlocking)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -240,43 +240,51 @@ nsIconChannel::AsyncOpen(nsIStreamListen
              mLoadInfo->GetInitialSecurityCheckDone() ||
              (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
               nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
              "security flags in loadInfo but asyncOpen2() not called");
 
   nsCOMPtr<nsIInputStream> inStream;
   nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
   if (NS_FAILED(rv)) {
+    mCallbacks = nullptr;
     return rv;
   }
 
   // Init our streampump
   rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false);
   if (NS_FAILED(rv)) {
+    mCallbacks = nullptr;
     return rv;
   }
 
   rv = mPump->AsyncRead(this, ctxt);
   if (NS_SUCCEEDED(rv)) {
     // Store our real listener
     mListener = aListener;
     // Add ourself to the load group, if available
     if (mLoadGroup) {
       mLoadGroup->AddRequest(this, nullptr);
     }
+  } else {
+    mCallbacks = nullptr;
   }
+
   return rv;
 }
 
 NS_IMETHODIMP
 nsIconChannel::AsyncOpen2(nsIStreamListener* aListener)
 {
   nsCOMPtr<nsIStreamListener> listener = aListener;
   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    mCallbacks = nullptr;
+    return rv;
+  }
   return AsyncOpen(listener, nullptr);
 }
 
 static DWORD
 GetSpecialFolderIcon(nsIFile* aFile, int aFolder,
                                   SHFILEINFOW* aSFI, UINT aInfoFlags)
 {
   DWORD shellResult = 0;
--- a/intl/locale/LocaleService.cpp
+++ b/intl/locale/LocaleService.cpp
@@ -56,25 +56,16 @@ LocaleService::GetAppLocales(nsTArray<ns
 {
   if (mAppLocales.IsEmpty()) {
     ReadAppLocales(mAppLocales);
   }
   aRetVal = mAppLocales;
 }
 
 void
-LocaleService::GetAppLocale(nsACString& aRetVal)
-{
-  if (mAppLocales.IsEmpty()) {
-    ReadAppLocales(mAppLocales);
-  }
-  aRetVal = mAppLocales[0];
-}
-
-void
 LocaleService::Refresh()
 {
   nsTArray<nsCString> newLocales;
   ReadAppLocales(newLocales);
 
   if (mAppLocales != newLocales) {
     mAppLocales = Move(newLocales);
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
@@ -106,13 +97,16 @@ LocaleService::GetAppLocales(JSContext* 
     JS_DefineElement(aCtx, locales, i, value, JSPROP_ENUMERATE);
   }
 
   aRetVal.setObject(*locales);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-LocaleService::GetAppLocale(JSContext* aCtx, nsACString& aRetVal)
+LocaleService::GetAppLocale(nsACString& aRetVal)
 {
-  GetAppLocale(aRetVal);
+  if (mAppLocales.IsEmpty()) {
+    ReadAppLocales(mAppLocales);
+  }
+  aRetVal = mAppLocales[0];
   return NS_OK;
 }
--- a/intl/locale/LocaleService.h
+++ b/intl/locale/LocaleService.h
@@ -52,41 +52,24 @@ public:
    * The result is a sorted list of valid locale IDs and it should be
    * used for all APIs that accept list of locales, like ECMA402 and L10n APIs.
    *
    * This API always returns at least one locale.
    *
    * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
    *
    * Usage:
-   * nsTArray<nsCString> appLocales;
-   * LocaleService::GetInstance()->GetAppLocales(appLocales);
+   *   nsTArray<nsCString> appLocales;
+   *   LocaleService::GetInstance()->GetAppLocales(appLocales);
+   *
+   * (See mozILocaleService.idl for a JS-callable version of this.)
    */
   void GetAppLocales(nsTArray<nsCString>& aRetVal);
 
   /**
-   * Returns the best locale that the application should be localized to.
-   *
-   * The result is a valid locale IDs and it should be
-   * used for all APIs that do not handle language negotiation.
-   *
-   * Where possible, GetAppLocales should be preferred over this API and
-   * all callsites should handle some form of "best effort" language
-   * negotiation to respect user preferences in case the use case does
-   * not have data for the first locale in the list.
-   *
-   * Example: "zh-Hans-HK"
-   *
-   * Usage:
-   * nsAutoCString appLocale;
-   * LocaleService::GetInstance()->GetAppLocale(appLocale);
-   */
-  void GetAppLocale(nsACString& aRetVal);
-
-  /**
    * Triggers a refresh of the language negotiation process.
    *
    * If the result differs from the previous list, it will additionally
    * trigger a global event "intl:app-locales-changed".
    */
   void Refresh();
 
 protected:
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -3,16 +3,19 @@
 # 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/.
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
 if CONFIG['ENABLE_INTL_API']:
     SOURCES += ['OSPreferences.cpp']
+    EXPORTS.mozilla.intl += [
+        'OSPreferences.h',
+    ]
 
 toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
 
 if toolkit == 'windows':
     DIRS += ['windows']
 elif toolkit == 'cocoa':
     DIRS += ['mac']
 else:
@@ -35,17 +38,17 @@ EXPORTS += [
     'nsILanguageAtomService.h',
     'nsIPlatformCharset.h',
     'nsPosixLocale.h',
     'nsUConvPropertySearch.h',
     'nsWin32Locale.h',
 ]
 
 EXPORTS.mozilla.intl += [
-    'LocaleService.h'
+    'LocaleService.h',
 ]
 
 UNIFIED_SOURCES += [
     'LocaleService.cpp',
     'nsCollation.cpp',
     'nsLanguageAtomService.cpp',
     'nsLocale.cpp',
     'nsLocaleService.cpp',
--- a/intl/locale/mozILocaleService.idl
+++ b/intl/locale/mozILocaleService.idl
@@ -11,11 +11,37 @@
   { 0x92735ff4, 0x6384, 0x4ad6, { 0x85, 0x08, 0x75, 0x70, 0x10, 0xe1, 0x49, 0xee } }
 
 #define MOZ_LOCALESERVICE_CONTRACTID "@mozilla.org/intl/localeservice;1"
 %}
 
 [scriptable, uuid(C27F8983-B48B-4D1A-92D7-FEB8106F212D)]
 interface mozILocaleService : nsISupports
 {
+  /**
+   * Returns a list of locales that the application should be localized to.
+   *
+   * The result is a sorted list of valid locale IDs and it should be
+   * used for all APIs that accept list of locales, like ECMA402 and L10n APIs.
+   *
+   * This API always returns at least one locale.
+   *
+   * Example: ["en-US", "de", "pl", "sr-Cyrl", "zh-Hans-HK"]
+   *
+   * (See LocaleService.h for a more C++-friendly version of this.)
+   */
   [implicit_jscontext] jsval getAppLocales();
-  [implicit_jscontext] ACString getAppLocale();
+
+  /**
+   * Returns the best locale that the application should be localized to.
+   *
+   * The result is a valid locale ID and it should be
+   * used for all APIs that do not handle language negotiation.
+   *
+   * Where possible, getAppLocales() should be preferred over this API and
+   * all callsites should handle some form of "best effort" language
+   * negotiation to respect user preferences in case the use case does
+   * not have data for the first locale in the list.
+   *
+   * Example: "zh-Hans-HK"
+   */
+  ACString getAppLocale();
 };
--- a/intl/locale/tests/gtest/TestLocaleService.cpp
+++ b/intl/locale/tests/gtest/TestLocaleService.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
-#include "LocaleService.h"
+#include "mozilla/intl/LocaleService.h"
 #include "mozilla/Services.h"
 #include "nsIToolkitChromeRegistry.h"
 
 using namespace mozilla::intl;
 
 
 TEST(Intl_Locale_LocaleService, GetAppLocales) {
   nsTArray<nsCString> appLocales;
--- a/intl/locale/tests/gtest/TestOSPreferences.cpp
+++ b/intl/locale/tests/gtest/TestOSPreferences.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
-#include "OSPreferences.h"
+#include "mozilla/intl/OSPreferences.h"
 
 using namespace mozilla::intl;
 
 /**
  * We test that on all platforms we test against (irrelevant of the tier),
  * we will be able to retrieve at least a single locale out of the system.
  *
  * In theory, that may not be true, but if we encounter such platform we should
--- a/intl/locale/tests/gtest/moz.build
+++ b/intl/locale/tests/gtest/moz.build
@@ -9,13 +9,9 @@ UNIFIED_SOURCES += [
 ]
 
 if CONFIG['ENABLE_INTL_API']:
     UNIFIED_SOURCES += [
         'TestDateTimeFormat.cpp',
         'TestOSPreferences.cpp',
     ]
 
-LOCAL_INCLUDES += [
-    '/intl/locale',
-]
-
 FINAL_LIBRARY = 'xul-gtest'
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -41,17 +41,17 @@ struct ForEachTrackedOptimizationTypeInf
 // (via EnableRuntimeProfilingStack) before executing the callstack being
 // unwound.
 //
 // Note that the caller must not do anything that could cause GC to happen while
 // the iterator is alive, since this could invalidate Ion code and cause its
 // contents to become out of date.
 class JS_PUBLIC_API(ProfilingFrameIterator)
 {
-    JSRuntime* rt_;
+    JSContext* cx_;
     uint32_t sampleBufferGen_;
     js::Activation* activation_;
 
     // When moving past a JitActivation, we need to save the prevJitTop
     // from it to use as the exit-frame pointer when the next caller jit
     // activation (if any) comes around.
     void* savedPrevJitTop_;
 
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -137,18 +137,16 @@ template <typename T> struct IsHeapConst
     template <> struct IsHeapConstructibleType<T> { static constexpr bool value = true; };
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE)
 #undef DECLARE_IS_HEAP_CONSTRUCTIBLE_TYPE
 
 template <typename T, typename Wrapper>
 class PersistentRootedBase : public MutableWrappedPtrOperations<T, Wrapper> {};
 
-static void* const ConstNullValue = nullptr;
-
 namespace gc {
 struct Cell;
 template<typename T>
 struct PersistentRootedMarker;
 } /* namespace gc */
 
 // Important: Return a reference so passing a Rooted<T>, etc. to
 // something that takes a |const T&| is not a GC hazard.
@@ -466,17 +464,18 @@ class MOZ_NONHEAP_CLASS Handle : public 
         static_assert(sizeof(Handle<T>) == sizeof(T*),
                       "Handle must be binary compatible with T*.");
         ptr = reinterpret_cast<const T*>(handle.address());
     }
 
     MOZ_IMPLICIT Handle(decltype(nullptr)) {
         static_assert(mozilla::IsPointer<T>::value,
                       "nullptr_t overload not valid for non-pointer types");
-        ptr = reinterpret_cast<const T*>(&js::ConstNullValue);
+        static void* const ConstNullValue = nullptr;
+        ptr = reinterpret_cast<const T*>(&ConstNullValue);
     }
 
     MOZ_IMPLICIT Handle(MutableHandle<T> handle) {
         ptr = handle.address();
     }
 
     /*
      * Take care when calling this method!
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2150,17 +2150,17 @@ SetJitCompilerOption(JSContext* cx, unsi
     if (number < 0)
         number = -1;
 
     // Throw if disabling the JITs and there's JIT code on the stack, to avoid
     // assertion failures.
     if ((opt == JSJITCOMPILER_BASELINE_ENABLE || opt == JSJITCOMPILER_ION_ENABLE) &&
         number == 0)
     {
-        js::jit::JitActivationIterator iter(cx->runtime());
+        js::jit::JitActivationIterator iter(cx);
         if (!iter.done()) {
             JS_ReportErrorASCII(cx, "Can't turn off JITs with JIT code on the stack.");
             return false;
         }
     }
 
     JS_SetGlobalJitCompilerOption(cx, opt, uint32_t(number));
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -7927,28 +7927,21 @@ Parser<ParseHandler>::unaryExpr(YieldHan
       case TOK_AWAIT: {
         if (!pc->isAsync()) {
             // TOK_AWAIT can be returned in module, even if it's not inside
             // async function.
             error(JSMSG_RESERVED_ID, "await");
             return null();
         }
 
-        TokenKind nextSameLine = TOK_EOF;
-        if (!tokenStream.peekTokenSameLine(&nextSameLine, TokenStream::Operand))
-            return null();
-        if (nextSameLine != TOK_EOL) {
-            Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
-            if (!kid)
-                return null();
-            pc->lastAwaitOffset = begin;
-            return newAwaitExpression(begin, kid);
-        }
-        error(JSMSG_LINE_BREAK_AFTER_AWAIT);
-        return null();
+        Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked);
+        if (!kid)
+            return null();
+        pc->lastAwaitOffset = begin;
+        return newAwaitExpression(begin, kid);
       }
 
       default: {
         Node expr = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
                                possibleError, invoked);
         if (!expr)
             return null();
 
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -42,16 +42,17 @@ class MOZ_RAII AutoTraceSession
     JSRuntime* runtime;
 
   private:
     AutoTraceSession(const AutoTraceSession&) = delete;
     void operator=(const AutoTraceSession&) = delete;
 
     JS::HeapState prevState;
     AutoGeckoProfilerEntry pseudoFrame;
+    JSRuntime::AutoProhibitActiveContextChange prohibitActiveContextChange;
 };
 
 class MOZ_RAII AutoPrepareForTracing
 {
     mozilla::Maybe<AutoTraceSession> session_;
 
   public:
     AutoPrepareForTracing(JSContext* cx, ZoneSelector selector);
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -980,17 +980,17 @@ class GCRuntime
 
   public:
     JSRuntime* const rt;
 
     /* Embedders can use this zone however they wish. */
     UnprotectedData<JS::Zone*> systemZone;
 
     // List of all zone groups (protected by the GC lock).
-    ActiveThreadData<ZoneGroupVector> groups;
+    ActiveThreadOrGCTaskData<ZoneGroupVector> groups;
 
     // The unique atoms zone, which has no zone group.
     WriteOnceData<Zone*> atomsZone;
 
   private:
     UnprotectedData<gcstats::Statistics> stats_;
   public:
     gcstats::Statistics& stats() { return stats_.ref(); }
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -541,17 +541,17 @@ js::Nursery::maybeEndProfile(ProfileKey 
     if (enableProfiling_)
         endProfile(key);
 }
 
 void
 js::Nursery::collect(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
-    MOZ_RELEASE_ASSERT(TlsContext.get() == zoneGroup()->context);
+    MOZ_RELEASE_ASSERT(zoneGroup()->ownedByCurrentThread());
 
     if (!isEnabled() || isEmpty()) {
         // Our barriers are not always exact, and there may be entries in the
         // storebuffer even when the nursery is disabled or empty. It's not safe
         // to keep these entries as they may refer to tenured cells which may be
         // freed after this point.
         zoneGroup()->storeBuffer().clear();
     }
@@ -722,17 +722,17 @@ js::Nursery::doCollection(JS::gcreason::
     // Sweep compartments to update the array buffer object's view lists.
     maybeStartProfile(ProfileKey::SweepArrayBufferViewList);
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->sweepAfterMinorGC(&mover);
     maybeEndProfile(ProfileKey::SweepArrayBufferViewList);
 
     // Update any slot or element pointers whose destination has been tenured.
     maybeStartProfile(ProfileKey::UpdateJitActivations);
-    js::jit::UpdateJitActivationsForMinorGC(rt, &mover);
+    js::jit::UpdateJitActivationsForMinorGC(zoneGroup(), &mover);
     forwardedBuffers.finish();
     maybeEndProfile(ProfileKey::UpdateJitActivations);
 
     maybeStartProfile(ProfileKey::ObjectsTenuredCallback);
     rt->gc.callObjectsTenuredCallback();
     maybeEndProfile(ProfileKey::ObjectsTenuredCallback);
 
     // Sweep.
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -320,19 +320,22 @@ void
 js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
                                       AutoLockForExclusiveAccess& lock)
 {
     MOZ_ASSERT(!TlsContext.get()->suppressGC);
 
     {
         gcstats::AutoPhase ap(stats(), gcstats::PHASE_MARK_STACK);
 
-        // Trace active interpreter and JIT stack roots.
-        TraceInterpreterActivations(rt, trc);
-        jit::TraceJitActivations(rt, trc);
+        JSContext* cx = TlsContext.get();
+        for (const CooperatingContext& target : rt->cooperatingContexts()) {
+            // Trace active interpreter and JIT stack roots.
+            TraceInterpreterActivations(cx, target, trc);
+            jit::TraceJitActivations(cx, target, trc);
+        }
 
         // Trace legacy C stack roots.
         AutoGCRooter::traceAll(trc);
 
         for (RootRange r = rootsHash.ref().all(); !r.empty(); r.popFront()) {
             const RootEntry& entry = r.front();
             TraceRoot(trc, entry.key(), entry.value());
         }
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -354,22 +354,16 @@ Zone::notifyObservingDebuggers()
 #endif
                 return;
             }
         }
     }
 }
 
 bool
-js::ZonesIter::atAtomsZone(JSRuntime* rt)
-{
-    return rt->isAtomsZone(*it);
-}
-
-bool
 Zone::isOnList() const
 {
     return listNext_ != NotOnList;
 }
 
 Zone*
 Zone::nextZone() const
 {
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -597,64 +597,136 @@ struct Zone : public JS::shadow::Zone,
     friend bool js::CurrentThreadCanAccessZone(Zone* zone);
     friend class js::gc::GCRuntime;
 };
 
 } // namespace JS
 
 namespace js {
 
+// Iterate over all zone groups except those which may be in use by parse
+// threads. Pretty soon this will exclude zone groups in use by parse threads
+// (as for ZonesIter), i.e. the zone groups in use by cooperating threads,
+// except that right now parse threads use zones in the same zone group as
+// cooperating threads (bug 1323066).
+class ZoneGroupsIter
+{
+    gc::AutoEnterIteration iterMarker;
+    ZoneGroup** it;
+    ZoneGroup** end;
+
+  public:
+    explicit ZoneGroupsIter(JSRuntime* rt) : iterMarker(&rt->gc) {
+        it = rt->gc.groups.ref().begin();
+        end = rt->gc.groups.ref().end();
+    }
+
+    bool done() const { return it == end; }
+
+    void next() {
+        MOZ_ASSERT(!done());
+        it++;
+    }
+
+    ZoneGroup* get() const {
+        MOZ_ASSERT(!done());
+        return *it;
+    }
+
+    operator ZoneGroup*() const { return get(); }
+    ZoneGroup* operator->() const { return get(); }
+};
+
 // Using the atoms zone without holding the exclusive access lock is dangerous
 // because worker threads may be using it simultaneously. Therefore, it's
 // better to skip the atoms zone when iterating over zones. If you need to
 // iterate over the atoms zone, consider taking the exclusive access lock first.
 enum ZoneSelector {
     WithAtoms,
     SkipAtoms
 };
 
-class ZonesIter
+// Iterate over all zones in one zone group.
+class ZonesInGroupIter
 {
     gc::AutoEnterIteration iterMarker;
-    JSRuntime* rt;
     JS::Zone** it;
     JS::Zone** end;
 
   public:
-    ZonesIter(JSRuntime* rt, ZoneSelector selector) : iterMarker(&rt->gc), rt(rt) {
-        if (selector == WithAtoms && rt->gc.atomsZone)
-            it = const_cast<JS::Zone**>(&rt->gc.atomsZone.ref());
-        else
-            it = rt->zoneGroupFromAnyThread()->zones().begin();
-        end = rt->zoneGroupFromAnyThread()->zones().end();
+    explicit ZonesInGroupIter(ZoneGroup* group) : iterMarker(&group->runtime->gc) {
+        it = group->zones().begin();
+        end = group->zones().end();
     }
 
-    bool atAtomsZone(JSRuntime* rt);
-
     bool done() const { return it == end; }
 
     void next() {
         MOZ_ASSERT(!done());
-        do {
-            if (it == &rt->gc.atomsZone.ref())
-                it = rt->zoneGroupFromAnyThread()->zones().begin();
-            else
-                it++;
-        } while (!done() && (*it)->usedByExclusiveThread);
+        it++;
     }
 
     JS::Zone* get() const {
         MOZ_ASSERT(!done());
         return *it;
     }
 
     operator JS::Zone*() const { return get(); }
     JS::Zone* operator->() const { return get(); }
 };
 
+// Iterate over all zones in the runtime, except those which may be in use by
+// parse threads.
+class ZonesIter
+{
+    ZoneGroupsIter group;
+    Maybe<ZonesInGroupIter> zone;
+    JS::Zone* atomsZone;
+
+  public:
+    ZonesIter(JSRuntime* rt, ZoneSelector selector)
+      : group(rt), atomsZone(selector == WithAtoms ? rt->gc.atomsZone.ref() : nullptr)
+    {
+        if (!atomsZone && !done())
+            next();
+    }
+
+    bool atAtomsZone(JSRuntime* rt) const {
+        return !!atomsZone;
+    }
+
+    bool done() const { return !atomsZone && group.done(); }
+
+    void next() {
+        MOZ_ASSERT(!done());
+        if (atomsZone)
+            atomsZone = nullptr;
+        while (!group.done()) {
+            if (zone.isSome())
+                zone.ref().next();
+            else
+                zone.emplace(group);
+            if (zone.ref().done()) {
+                zone.reset();
+                group.next();
+            } else if (!zone.ref().get()->usedByExclusiveThread) {
+                break;
+            }
+        }
+    }
+
+    JS::Zone* get() const {
+        MOZ_ASSERT(!done());
+        return atomsZone ? atomsZone : zone.ref().get();
+    }
+
+    operator JS::Zone*() const { return get(); }
+    JS::Zone* operator->() const { return get(); }
+};
+
 struct CompartmentsInZoneIter
 {
     explicit CompartmentsInZoneIter(JS::Zone* zone) : zone(zone) {
         it = zone->compartments().begin();
     }
 
     bool done() const {
         MOZ_ASSERT(it);
--- a/js/src/gc/ZoneGroup.cpp
+++ b/js/src/gc/ZoneGroup.cpp
@@ -9,30 +9,28 @@
 #include "jscntxt.h"
 
 #include "jit/JitCompartment.h"
 
 namespace js {
 
 ZoneGroup::ZoneGroup(JSRuntime* runtime)
   : runtime(runtime),
-    context(TlsContext.get()),
+    ownerContext_(TlsContext.get()),
     enterCount(this, 1),
     zones_(),
     nursery_(this, this),
     storeBuffer_(this, runtime, nursery()),
     blocksToFreeAfterMinorGC((size_t) JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     caches_(this),
 #ifdef DEBUG
     ionBailAfter_(this, 0),
 #endif
     jitZoneGroup(this, nullptr),
-    debuggerList_(this),
-    profilingScripts(this, false),
-    scriptAndCountsVector(this, nullptr)
+    debuggerList_(this)
 {}
 
 bool
 ZoneGroup::init(size_t maxNurseryBytes)
 {
     if (!caches().init())
         return false;
 
@@ -51,34 +49,35 @@ ZoneGroup::~ZoneGroup()
 {
     js_delete(jitZoneGroup.ref());
 }
 
 void
 ZoneGroup::enter()
 {
     JSContext* cx = TlsContext.get();
-    if (context == cx) {
+    if (ownerContext().context() == cx) {
         MOZ_ASSERT(enterCount);
     } else {
-        JSContext* old = context.exchange(cx);
-        MOZ_RELEASE_ASSERT(old == nullptr);
+        MOZ_ASSERT(ownerContext().context() == nullptr);
         MOZ_ASSERT(enterCount == 0);
+        ownerContext_ = CooperatingContext(cx);
     }
     enterCount++;
 }
 
 void
 ZoneGroup::leave()
 {
     MOZ_ASSERT(ownedByCurrentThread());
     MOZ_ASSERT(enterCount);
     if (--enterCount == 0)
-        context = nullptr;
+        ownerContext_ = CooperatingContext(nullptr);
 }
 
 bool
 ZoneGroup::ownedByCurrentThread()
 {
-    return context == TlsContext.get();
+    MOZ_ASSERT(TlsContext.get());
+    return ownerContext().context() == TlsContext.get();
 }
 
 } // namespace js
--- a/js/src/gc/ZoneGroup.h
+++ b/js/src/gc/ZoneGroup.h
@@ -6,27 +6,26 @@
 
 #ifndef gc_ZoneGroup_h
 #define gc_ZoneGroup_h
 
 #include "jsgc.h"
 
 #include "gc/Statistics.h"
 #include "vm/Caches.h"
+#include "vm/Stack.h"
 
 namespace js {
 
 namespace jit { class JitZoneGroup; }
 
 class AutoKeepAtoms;
 
 typedef Vector<JS::Zone*, 4, SystemAllocPolicy> ZoneVector;
 
-using ScriptAndCountsVector = GCVector<ScriptAndCounts, 0, SystemAllocPolicy>;
-
 // Zone groups encapsulate data about a group of zones that are logically
 // related in some way. Currently, each runtime has a single zone group, and
 // all zones except the atoms zone (which has no group) are in that group.
 // This will change soon.
 //
 // When JSRuntimes become multithreaded (also happening soon; see bug 1323066),
 // zone groups will be the primary means by which threads ensure exclusive
 // access to the data they are using. Most data in a zone group, its zones,
@@ -37,22 +36,27 @@ using ScriptAndCountsVector = GCVector<S
 // are accessed by that thread even though it does not have exclusive access
 // to the entire zone group. This will also be changing soon.
 
 class ZoneGroup
 {
   public:
     JSRuntime* const runtime;
 
+  private:
     // The context with exclusive access to this zone group.
-    mozilla::Atomic<JSContext*, mozilla::ReleaseAcquire> context;
+    UnprotectedData<CooperatingContext> ownerContext_;
 
     // The number of times the context has entered this zone group.
     ZoneGroupData<size_t> enterCount;
 
+  public:
+    CooperatingContext& ownerContext() { return ownerContext_.ref(); }
+    void* addressOfOwnerContext() { return &ownerContext_.ref().cx; }
+
     void enter();
     void leave();
     bool ownedByCurrentThread();
 
     // All zones in the group.
   private:
     ActiveThreadOrGCTaskData<ZoneVector> zones_;
   public:
@@ -119,22 +123,16 @@ class ZoneGroup
 
     ZoneGroupData<jit::JitZoneGroup*> jitZoneGroup;
 
   private:
     /* Linked list of all Debugger objects in the group. */
     ZoneGroupData<mozilla::LinkedList<js::Debugger>> debuggerList_;
   public:
     mozilla::LinkedList<js::Debugger>& debuggerList() { return debuggerList_.ref(); }
-
-    /* If true, new scripts must be created with PC counter information. */
-    ZoneGroupOrIonCompileData<bool> profilingScripts;
-
-    /* Strong references on scripts held for PCCount profiling API. */
-    ZoneGroupData<JS::PersistentRooted<ScriptAndCountsVector>*> scriptAndCountsVector;
 };
 
 class MOZ_RAII AutoAccessZoneGroup
 {
     ZoneGroup* group;
 
   public:
     explicit AutoAccessZoneGroup(ZoneGroup* group)
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -152,17 +152,17 @@ NativeRegExpMacroAssembler::GenerateCode
     // Actually emit code to start a new stack frame.
     masm.reserveStack(frameSize);
     masm.checkStackAlignment();
 
     // Check if we have space on the stack. Use the *NoInterrupt stack limit to
     // avoid failing repeatedly when the regex code is called from Ion JIT code,
     // see bug 1208819.
     Label stack_ok;
-    void* context_addr = &cx->zone()->group()->context;
+    void* context_addr = cx->zone()->group()->addressOfOwnerContext();
     masm.loadPtr(AbsoluteAddress(context_addr), temp0);
     Address limit_addr(temp0, offsetof(JSContext, jitStackLimitNoInterrupt));
     masm.branchStackPtrRhs(Assembler::Below, limit_addr, &stack_ok);
 
     // Exit with an exception. There is not enough space on the stack
     // for our working registers.
     masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
     masm.jump(&return_temp0);
@@ -461,17 +461,17 @@ NativeRegExpMacroAssembler::GenerateCode
         Label return_from_overflow_handler;
         masm.branchTest32(Assembler::Zero, temp0, temp0, &return_from_overflow_handler);
 
         // Otherwise, store the new backtrack stack base and recompute the new
         // top of the stack.
         Address backtrackStackBaseAddress(temp2, offsetof(FrameData, backtrackStackBase));
         masm.subPtr(backtrackStackBaseAddress, backtrack_stack_pointer);
 
-        void* context_addr = &cx->zone()->group()->context;
+        void* context_addr = cx->zone()->group()->addressOfOwnerContext();
         size_t baseOffset = offsetof(JSContext, regexpStack) + RegExpStack::offsetOfBase();
         masm.loadPtr(AbsoluteAddress(context_addr), temp1);
         masm.loadPtr(Address(temp1, baseOffset), temp1);
         masm.storePtr(temp1, backtrackStackBaseAddress);
         masm.addPtr(temp1, backtrack_stack_pointer);
 
         // Resume execution in calling code.
         masm.bind(&return_from_overflow_handler);
@@ -543,17 +543,17 @@ NativeRegExpMacroAssembler::AdvanceRegis
 
 void
 NativeRegExpMacroAssembler::Backtrack()
 {
     JitSpew(SPEW_PREFIX "Backtrack");
 
     // Check for an interrupt.
     Label noInterrupt;
-    void* contextAddr = &cx->zone()->group()->context;
+    void* contextAddr = cx->zone()->group()->addressOfOwnerContext();
     masm.loadPtr(AbsoluteAddress(contextAddr), temp0);
     masm.branch32(Assembler::Equal, Address(temp0, offsetof(JSContext, interrupt_)), Imm32(0),
                   &noInterrupt);
     masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
     masm.jump(&exit_label_);
     masm.bind(&noInterrupt);
 
     // Pop code location from backtrack stack and jump to location.
@@ -1104,17 +1104,17 @@ NativeRegExpMacroAssembler::PopBacktrack
 }
 
 void
 NativeRegExpMacroAssembler::CheckBacktrackStackLimit()
 {
     JitSpew(SPEW_PREFIX "CheckBacktrackStackLimit");
 
     Label no_stack_overflow;
-    void* context_addr = &cx->zone()->group()->context;
+    void* context_addr = cx->zone()->group()->addressOfOwnerContext();
     size_t limitOffset = offsetof(JSContext, regexpStack) + RegExpStack::offsetOfLimit();
     masm.loadPtr(AbsoluteAddress(context_addr), temp1);
     masm.branchPtr(Assembler::AboveOrEqual, Address(temp1, limitOffset),
                    backtrack_stack_pointer, &no_stack_overflow);
 
     // Copy the stack pointer before the call() instruction modifies it.
     masm.moveStackPtrTo(temp2);
 
--- a/js/src/jit-test/tests/auto-regress/bug1334573.js
+++ b/js/src/jit-test/tests/auto-regress/bug1334573.js
@@ -1,6 +1,8 @@
 // |jit-test| error:Error
 
 if (this.Intl) {
     addIntlExtras(Intl);
     addIntlExtras(Intl);
+} else {
+    throw new Error();
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1304553.js
@@ -0,0 +1,21 @@
+var dbgGlobal = newGlobal();
+var dbg = new dbgGlobal.Debugger();
+dbg.addDebuggee(this);
+function evalInFrame() {
+    var frame = dbg.getNewestFrame().older.older;
+    frame.eval("1");
+};
+var actual = '';
+try {
+    function f() {
+        evalInFrame();
+    }
+} catch (e) {}
+let m = parseModule(`
+                    actual = '';
+                    for (var i = 0; i < 1; ++i)
+                        f(i);
+                    actual;
+                    `);
+m.declarationInstantiation();
+m.evaluation();
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -33,17 +33,17 @@ jit::Bailout(BailoutStack* sp, BaselineB
     MOZ_ASSERT(bailoutInfo);
 
     // We don't have an exit frame.
     MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) &&
                IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
                "Fake jitTop pointer should be within the first page.");
     cx->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
 
-    JitActivationIterator jitActivations(cx->runtime());
+    JitActivationIterator jitActivations(cx);
     BailoutFrameInfo bailoutData(jitActivations, sp);
     JitFrameIterator iter(jitActivations);
     MOZ_ASSERT(!iter.ionScript()->invalidated());
     CommonFrameLayout* currentFramePtr = iter.current();
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     TraceLogTimestamp(logger, TraceLogger_Bailout);
 
@@ -105,17 +105,17 @@ jit::InvalidationBailout(InvalidationBai
 {
     sp->checkInvariants();
 
     JSContext* cx = TlsContext.get();
 
     // We don't have an exit frame.
     cx->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
 
-    JitActivationIterator jitActivations(cx->runtime());
+    JitActivationIterator jitActivations(cx);
     BailoutFrameInfo bailoutData(jitActivations, sp);
     JitFrameIterator iter(jitActivations);
     CommonFrameLayout* currentFramePtr = iter.current();
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     TraceLogTimestamp(logger, TraceLogger_Invalidation);
 
     JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
@@ -193,17 +193,17 @@ jit::ExceptionHandlerBailout(JSContext* 
     MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending());
 
     uint8_t* prevJitTop = cx->jitTop;
     auto restoreJitTop = mozilla::MakeScopeExit([&]() { cx->jitTop = prevJitTop; });
     cx->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
 
     gc::AutoSuppressGC suppress(cx);
 
-    JitActivationIterator jitActivations(cx->runtime());
+    JitActivationIterator jitActivations(cx);
     BailoutFrameInfo bailoutData(jitActivations, frame.frame());
     JitFrameIterator iter(jitActivations);
     CommonFrameLayout* currentFramePtr = iter.current();
 
     BaselineBailoutInfo* bailoutInfo = nullptr;
     uint32_t retval;
 
     {
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -27,40 +27,44 @@ CacheRegisterAllocator::addressOf(MacroA
 {
     uint32_t offset = stackPushed_ + ICStackValueOffset + slot.slot() * sizeof(JS::Value);
     return Address(masm.getStackPointer(), offset);
 }
 
 // BaselineCacheIRCompiler compiles CacheIR to BaselineIC native code.
 class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler
 {
+#ifdef DEBUG
     // Some Baseline IC stubs can be used in IonMonkey through SharedStubs.
     // Those stubs have different machine code, so we need to track whether
     // we're compiling for Baseline or Ion.
     ICStubEngine engine_;
+#endif
 
     uint32_t stubDataOffset_;
     bool inStubFrame_;
     bool makesGCCalls_;
 
     MOZ_MUST_USE bool callVM(MacroAssembler& masm, const VMFunction& fun);
 
-    MOZ_MUST_USE bool callTypeUpdateIC(AutoStubFrame& stubFrame, Register obj, ValueOperand val,
-                                       Register scratch, LiveGeneralRegisterSet saveRegs);
+    MOZ_MUST_USE bool callTypeUpdateIC(Register obj, ValueOperand val, Register scratch,
+                                       LiveGeneralRegisterSet saveRegs);
 
     MOZ_MUST_USE bool emitStoreSlotShared(bool isFixed);
     MOZ_MUST_USE bool emitAddAndStoreSlotShared(CacheOp op);
 
   public:
     friend class AutoStubFrame;
 
     BaselineCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, ICStubEngine engine,
                             uint32_t stubDataOffset)
       : CacheIRCompiler(cx, writer, Mode::Baseline),
+#ifdef DEBUG
         engine_(engine),
+#endif
         stubDataOffset_(stubDataOffset),
         inStubFrame_(false),
         makesGCCalls_(false)
     {}
 
     MOZ_MUST_USE bool init(CacheKind kind);
 
     JitCode* compile();
@@ -79,75 +83,62 @@ class MOZ_RAII BaselineCacheIRCompiler :
 
 #define DEFINE_SHARED_OP(op) \
     bool BaselineCacheIRCompiler::emit##op() { return CacheIRCompiler::emit##op(); }
     CACHE_IR_SHARED_OPS(DEFINE_SHARED_OP)
 #undef DEFINE_SHARED_OP
 
 enum class CallCanGC { CanGC, CanNotGC };
 
-// Instructions that have to perform a callVM require a stub frame. Use
-// AutoStubFrame before allocating any registers, then call its enter() and
-// leave() methods to enter/leave the stub frame.
+// Instructions that have to perform a callVM require a stub frame. Call its
+// enter() and leave() methods to enter/leave the stub frame.
 class MOZ_RAII AutoStubFrame
 {
     BaselineCacheIRCompiler& compiler;
 #ifdef DEBUG
     uint32_t framePushedAtEnterStubFrame_;
 #endif
-    Maybe<AutoScratchRegister> tail;
 
     AutoStubFrame(const AutoStubFrame&) = delete;
     void operator=(const AutoStubFrame&) = delete;
 
   public:
     explicit AutoStubFrame(BaselineCacheIRCompiler& compiler)
-      : compiler(compiler),
+      : compiler(compiler)
 #ifdef DEBUG
-        framePushedAtEnterStubFrame_(0),
+        , framePushedAtEnterStubFrame_(0)
 #endif
-        tail()
-    {
-        // We use ICTailCallReg when entering the stub frame, so ensure it's not
-        // used for something else.
-        if (compiler.allocator.isAllocatable(ICTailCallReg))
-            tail.emplace(compiler.allocator, compiler.masm, ICTailCallReg);
-    }
+    { }
 
     void enter(MacroAssembler& masm, Register scratch, CallCanGC canGC = CallCanGC::CanGC) {
         MOZ_ASSERT(compiler.allocator.stackPushed() == 0);
-        if (compiler.engine_ == ICStubEngine::Baseline) {
-            EmitBaselineEnterStubFrame(masm, scratch);
+        MOZ_ASSERT(compiler.engine_ == ICStubEngine::Baseline);
+
+        EmitBaselineEnterStubFrame(masm, scratch);
+
 #ifdef DEBUG
-            framePushedAtEnterStubFrame_ = masm.framePushed();
+        framePushedAtEnterStubFrame_ = masm.framePushed();
 #endif
-        } else {
-            EmitIonEnterStubFrame(masm, scratch);
-        }
 
         MOZ_ASSERT(!compiler.inStubFrame_);
         compiler.inStubFrame_ = true;
         if (canGC == CallCanGC::CanGC)
             compiler.makesGCCalls_ = true;
     }
     void leave(MacroAssembler& masm, bool calledIntoIon = false) {
         MOZ_ASSERT(compiler.inStubFrame_);
         compiler.inStubFrame_ = false;
 
-        if (compiler.engine_ == ICStubEngine::Baseline) {
 #ifdef DEBUG
-            masm.setFramePushed(framePushedAtEnterStubFrame_);
-            if (calledIntoIon)
-                masm.adjustFrame(sizeof(intptr_t)); // Calls into ion have this extra.
+        masm.setFramePushed(framePushedAtEnterStubFrame_);
+        if (calledIntoIon)
+            masm.adjustFrame(sizeof(intptr_t)); // Calls into ion have this extra.
 #endif
 
-            EmitBaselineLeaveStubFrame(masm, calledIntoIon);
-        } else {
-            EmitIonLeaveStubFrame(masm);
-        }
+        EmitBaselineLeaveStubFrame(masm, calledIntoIon);
     }
 
 #ifdef DEBUG
     ~AutoStubFrame() {
         MOZ_ASSERT(!compiler.inStubFrame_);
     }
 #endif
 };
@@ -157,20 +148,19 @@ BaselineCacheIRCompiler::callVM(MacroAss
 {
     MOZ_ASSERT(inStubFrame_);
 
     JitCode* code = cx_->runtime()->jitRuntime()->getVMWrapper(fun);
     if (!code)
         return false;
 
     MOZ_ASSERT(fun.expectTailCall == NonTailCall);
-    if (engine_ == ICStubEngine::Baseline)
-        EmitBaselineCallVM(code, masm);
-    else
-        EmitIonCallVM(code, fun.explicitStackSlots(), masm);
+    MOZ_ASSERT(engine_ == ICStubEngine::Baseline);
+
+    EmitBaselineCallVM(code, masm);
     return true;
 }
 
 JitCode*
 BaselineCacheIRCompiler::compile()
 {
 #ifndef JS_USE_LINK_REGISTER
     // The first value contains the return addres,
@@ -372,18 +362,16 @@ BaselineCacheIRCompiler::emitLoadDynamic
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitCallScriptedGetterResult()
 {
     MOZ_ASSERT(engine_ == ICStubEngine::Baseline);
 
-    AutoStubFrame stubFrame(*this);
-
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address getterAddr(stubAddress(reader.stubOffset()));
 
     AutoScratchRegisterExcluding code(allocator, masm, ArgumentsRectifierReg);
     AutoScratchRegister callee(allocator, masm);
     AutoScratchRegister scratch(allocator, masm);
 
     // First, ensure our getter is non-lazy and has JIT code.
@@ -395,16 +383,17 @@ BaselineCacheIRCompiler::emitCallScripte
         masm.loadPtr(getterAddr, callee);
         masm.branchIfFunctionHasNoScript(callee, failure->label());
         masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), code);
         masm.loadBaselineOrIonRaw(code, code, failure->label());
     }
 
     allocator.discardStack(masm);
 
+    AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch);
 
     // Align the stack such that the JitFrameLayout is aligned on
     // JitStackAlignment.
     masm.alignJitStackBasedOnNArgs(0);
 
     // Getter is called with 0 arguments, just |obj| as thisv.
     // Note that we use Push, not push, so that callJit will align the stack
@@ -439,25 +428,24 @@ BaselineCacheIRCompiler::emitCallScripte
 
 typedef bool (*CallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue);
 static const VMFunction CallNativeGetterInfo =
     FunctionInfo<CallNativeGetterFn>(CallNativeGetter, "CallNativeGetter");
 
 bool
 BaselineCacheIRCompiler::emitCallNativeGetterResult()
 {
-    AutoStubFrame stubFrame(*this);
-
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address getterAddr(stubAddress(reader.stubOffset()));
 
     AutoScratchRegister scratch(allocator, masm);
 
     allocator.discardStack(masm);
 
+    AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch);
 
     // Load the callee in the scratch register.
     masm.loadPtr(getterAddr, scratch);
 
     masm.Push(obj);
     masm.Push(scratch);
 
@@ -470,25 +458,24 @@ BaselineCacheIRCompiler::emitCallNativeG
 
 typedef bool (*ProxyGetPropertyFn)(JSContext*, HandleObject, HandleId, MutableHandleValue);
 static const VMFunction ProxyGetPropertyInfo =
     FunctionInfo<ProxyGetPropertyFn>(ProxyGetProperty, "ProxyGetProperty");
 
 bool
 BaselineCacheIRCompiler::emitCallProxyGetResult()
 {
-    AutoStubFrame stubFrame(*this);
-
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address idAddr(stubAddress(reader.stubOffset()));
 
     AutoScratchRegister scratch(allocator, masm);
 
     allocator.discardStack(masm);
 
+    AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch);
 
     // Load the jsid in the scratch register.
     masm.loadPtr(idAddr, scratch);
 
     masm.Push(scratch);
     masm.Push(obj);
 
@@ -501,25 +488,24 @@ BaselineCacheIRCompiler::emitCallProxyGe
 
 typedef bool (*ProxyGetPropertyByValueFn)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
 static const VMFunction ProxyGetPropertyByValueInfo =
     FunctionInfo<ProxyGetPropertyByValueFn>(ProxyGetPropertyByValue, "ProxyGetPropertyByValue");
 
 bool
 BaselineCacheIRCompiler::emitCallProxyGetByValueResult()
 {
-    AutoStubFrame stubFrame(*this);
-
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     ValueOperand idVal = allocator.useValueRegister(masm, reader.valOperandId());
 
     AutoScratchRegister scratch(allocator, masm);
 
     allocator.discardStack(masm);
 
+    AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch);
 
     masm.Push(idVal);
     masm.Push(obj);
 
     if (!callVM(masm, ProxyGetPropertyByValueInfo))
         return false;
 
@@ -666,18 +652,18 @@ BaselineCacheIRCompiler::emitLoadEnviron
     masm.branchTestMagic(Assembler::Equal, slot, failure->label());
 
     // Load the value.
     masm.loadValue(slot, output.valueReg());
     return true;
 }
 
 bool
-BaselineCacheIRCompiler::callTypeUpdateIC(AutoStubFrame& stubFrame, Register obj, ValueOperand val,
-                                          Register scratch, LiveGeneralRegisterSet saveRegs)
+BaselineCacheIRCompiler::callTypeUpdateIC(Register obj, ValueOperand val, Register scratch,
+                                          LiveGeneralRegisterSet saveRegs)
 {
     // Ensure the stack is empty for the VM call below.
     allocator.discardStack(masm);
 
     // R0 contains the value that needs to be typechecked.
     MOZ_ASSERT(val == R0);
     MOZ_ASSERT(scratch == R1.scratchReg());
 
@@ -698,16 +684,17 @@ BaselineCacheIRCompiler::callTypeUpdateI
     if (CallClobbersTailReg)
         masm.pop(ICTailCallReg);
 
     // The update IC will store 0 or 1 in |scratch|, R1.scratchReg(), reflecting
     // if the value in R0 type-checked properly or not.
     Label done;
     masm.branch32(Assembler::Equal, scratch, Imm32(1), &done);
 
+    AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch, CallCanGC::CanNotGC);
 
     masm.PushRegsInMask(saveRegs);
 
     masm.Push(val);
     masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
     masm.Push(ICStubReg);
 
@@ -729,46 +716,45 @@ BaselineCacheIRCompiler::callTypeUpdateI
 bool
 BaselineCacheIRCompiler::emitStoreSlotShared(bool isFixed)
 {
     ObjOperandId objId = reader.objOperandId();
     Address offsetAddr = stubAddress(reader.stubOffset());
 
     // Allocate the fixed registers first. These need to be fixed for
     // callTypeUpdateIC.
-    AutoStubFrame stubFrame(*this);
-    AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
+    AutoScratchRegister scratch1(allocator, masm, R1.scratchReg());
     ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
 
     Register obj = allocator.useRegister(masm, objId);
+    Maybe<AutoScratchRegister> scratch2;
+    if (!isFixed)
+        scratch2.emplace(allocator, masm);
 
     LiveGeneralRegisterSet saveRegs;
     saveRegs.add(obj);
     saveRegs.add(val);
-    if (!callTypeUpdateIC(stubFrame, obj, val, scratch, saveRegs))
+    if (!callTypeUpdateIC(obj, val, scratch1, saveRegs))
         return false;
 
-    masm.load32(offsetAddr, scratch);
+    masm.load32(offsetAddr, scratch1);
 
     if (isFixed) {
-        BaseIndex slot(obj, scratch, TimesOne);
+        BaseIndex slot(obj, scratch1, TimesOne);
         EmitPreBarrier(masm, slot, MIRType::Value);
         masm.storeValue(val, slot);
     } else {
-        // To avoid running out of registers on x86, use ICStubReg as scratch.
-        // We don't need it anymore.
-        Register slots = ICStubReg;
-        masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), slots);
-        BaseIndex slot(slots, scratch, TimesOne);
+        masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch2.ref());
+        BaseIndex slot(scratch2.ref(), scratch1, TimesOne);
         EmitPreBarrier(masm, slot, MIRType::Value);
         masm.storeValue(val, slot);
     }
 
     if (cx_->nursery().exists())
-        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreFixedSlot()
 {
     return emitStoreSlotShared(true);
 }
@@ -782,21 +768,22 @@ BaselineCacheIRCompiler::emitStoreDynami
 bool
 BaselineCacheIRCompiler::emitAddAndStoreSlotShared(CacheOp op)
 {
     ObjOperandId objId = reader.objOperandId();
     Address offsetAddr = stubAddress(reader.stubOffset());
 
     // Allocate the fixed registers first. These need to be fixed for
     // callTypeUpdateIC.
-    AutoStubFrame stubFrame(*this);
-    AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
+    AutoScratchRegister scratch1(allocator, masm, R1.scratchReg());
     ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
 
     Register obj = allocator.useRegister(masm, objId);
+    AutoScratchRegister scratch2(allocator, masm);
+
     bool changeGroup = reader.readBool();
     Address newGroupAddr = stubAddress(reader.stubOffset());
     Address newShapeAddr = stubAddress(reader.stubOffset());
 
     if (op == CacheOp::AllocateAndStoreDynamicSlot) {
         // We have to (re)allocate dynamic slots. Do this first, as it's the
         // only fallible operation here. This simplifies the callTypeUpdateIC
         // call below: it does not have to worry about saving registers used by
@@ -805,90 +792,83 @@ BaselineCacheIRCompiler::emitAddAndStore
 
         FailurePath* failure;
         if (!addFailurePath(&failure))
             return false;
 
         AllocatableRegisterSet regs(RegisterSet::Volatile());
         LiveRegisterSet save(regs.asLiveSet());
 
-        // Use ICStubReg as second scratch.
-        if (!save.has(ICStubReg))
-            save.add(ICStubReg);
-
         masm.PushRegsInMask(save);
 
-        masm.setupUnalignedABICall(scratch);
-        masm.loadJSContext(scratch);
-        masm.passABIArg(scratch);
+        masm.setupUnalignedABICall(scratch1);
+        masm.loadJSContext(scratch1);
+        masm.passABIArg(scratch1);
         masm.passABIArg(obj);
-        masm.load32(numNewSlotsAddr, ICStubReg);
-        masm.passABIArg(ICStubReg);
+        masm.load32(numNewSlotsAddr, scratch2);
+        masm.passABIArg(scratch2);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NativeObject::growSlotsDontReportOOM));
-        masm.mov(ReturnReg, scratch);
+        masm.mov(ReturnReg, scratch1);
 
         LiveRegisterSet ignore;
-        ignore.add(scratch);
+        ignore.add(scratch1);
         masm.PopRegsInMaskIgnore(save, ignore);
 
-        masm.branchIfFalseBool(scratch, failure->label());
+        masm.branchIfFalseBool(scratch1, failure->label());
     }
 
     LiveGeneralRegisterSet saveRegs;
     saveRegs.add(obj);
     saveRegs.add(val);
-    if (!callTypeUpdateIC(stubFrame, obj, val, scratch, saveRegs))
+    if (!callTypeUpdateIC(obj, val, scratch1, saveRegs))
         return false;
 
     if (changeGroup) {
         // Changing object's group from a partially to fully initialized group,
         // per the acquired properties analysis. Only change the group if the
         // old group still has a newScript. This only applies to PlainObjects.
         Label noGroupChange;
-        masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
+        masm.loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch1);
         masm.branchPtr(Assembler::Equal,
-                       Address(scratch, ObjectGroup::offsetOfAddendum()),
+                       Address(scratch1, ObjectGroup::offsetOfAddendum()),
                        ImmWord(0),
                        &noGroupChange);
 
         // Reload the new group from the cache.
-        masm.loadPtr(newGroupAddr, scratch);
+        masm.loadPtr(newGroupAddr, scratch1);
 
         Address groupAddr(obj, JSObject::offsetOfGroup());
         EmitPreBarrier(masm, groupAddr, MIRType::ObjectGroup);
-        masm.storePtr(scratch, groupAddr);
+        masm.storePtr(scratch1, groupAddr);
 
         masm.bind(&noGroupChange);
     }
 
     // Update the object's shape.
     Address shapeAddr(obj, ShapedObject::offsetOfShape());
-    masm.loadPtr(newShapeAddr, scratch);
+    masm.loadPtr(newShapeAddr, scratch1);
     EmitPreBarrier(masm, shapeAddr, MIRType::Shape);
-    masm.storePtr(scratch, shapeAddr);
+    masm.storePtr(scratch1, shapeAddr);
 
     // Perform the store. No pre-barrier required since this is a new
     // initialization.
-    masm.load32(offsetAddr, scratch);
+    masm.load32(offsetAddr, scratch1);
     if (op == CacheOp::AddAndStoreFixedSlot) {
-        BaseIndex slot(obj, scratch, TimesOne);
+        BaseIndex slot(obj, scratch1, TimesOne);
         masm.storeValue(val, slot);
     } else {
         MOZ_ASSERT(op == CacheOp::AddAndStoreDynamicSlot ||
                    op == CacheOp::AllocateAndStoreDynamicSlot);
-        // To avoid running out of registers on x86, use ICStubReg as scratch.
-        // We don't need it anymore.
-        Register slots = ICStubReg;
-        masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), slots);
-        BaseIndex slot(slots, scratch, TimesOne);
+        masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch2);
+        BaseIndex slot(scratch2, scratch1, TimesOne);
         masm.storeValue(val, slot);
     }
 
     if (cx_->nursery().exists())
-        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch, LiveGeneralRegisterSet(), cx_);
+        BaselineEmitPostWriteBarrierSlot(masm, obj, val, scratch1, LiveGeneralRegisterSet(), cx_);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitAddAndStoreFixedSlot()
 {
     return emitAddAndStoreSlotShared(CacheOp::AddAndStoreFixedSlot);
 }
@@ -909,28 +889,27 @@ bool
 BaselineCacheIRCompiler::emitStoreUnboxedProperty()
 {
     ObjOperandId objId = reader.objOperandId();
     JSValueType fieldType = reader.valueType();
     Address offsetAddr = stubAddress(reader.stubOffset());
 
     // Allocate the fixed registers first. These need to be fixed for
     // callTypeUpdateIC.
-    AutoStubFrame stubFrame(*this);
     AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
     ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
 
     Register obj = allocator.useRegister(masm, objId);
 
     // We only need the type update IC if we are storing an object.
     if (fieldType == JSVAL_TYPE_OBJECT) {
         LiveGeneralRegisterSet saveRegs;
         saveRegs.add(obj);
         saveRegs.add(val);
-        if (!callTypeUpdateIC(stubFrame, obj, val, scratch, saveRegs))
+        if (!callTypeUpdateIC(obj, val, scratch, saveRegs))
             return false;
     }
 
     masm.load32(offsetAddr, scratch);
     BaseIndex fieldAddr(obj, scratch, TimesOne);
 
     // Note that the storeUnboxedProperty call here is infallible, as the
     // IR emitter is responsible for guarding on |val|'s type.
@@ -949,40 +928,36 @@ BaselineCacheIRCompiler::emitStoreTypedO
 {
     ObjOperandId objId = reader.objOperandId();
     Address offsetAddr = stubAddress(reader.stubOffset());
     TypedThingLayout layout = reader.typedThingLayout();
     ReferenceTypeDescr::Type type = reader.referenceTypeDescrType();
 
     // Allocate the fixed registers first. These need to be fixed for
     // callTypeUpdateIC.
-    AutoStubFrame stubFrame(*this);
     AutoScratchRegister scratch1(allocator, masm, R1.scratchReg());
     ValueOperand val = allocator.useFixedValueRegister(masm, reader.valOperandId(), R0);
 
     Register obj = allocator.useRegister(masm, objId);
+    AutoScratchRegister scratch2(allocator, masm);
 
-    // We don't need a type update IC if the property is always a string.
+    // We don't need a type update IC if the property is always a string.scratch
     if (type != ReferenceTypeDescr::TYPE_STRING) {
         LiveGeneralRegisterSet saveRegs;
         saveRegs.add(obj);
         saveRegs.add(val);
-        if (!callTypeUpdateIC(stubFrame, obj, val, scratch1, saveRegs))
+        if (!callTypeUpdateIC(obj, val, scratch1, saveRegs))
             return false;
     }
 
     // Compute the address being written to.
     LoadTypedThingData(masm, layout, obj, scratch1);
     masm.addPtr(offsetAddr, scratch1);
     Address dest(scratch1, 0);
 
-    // To avoid running out of registers on x86, use ICStubReg as second
-    // scratch. It won't be used after this.
-    Register scratch2 = ICStubReg;
-
     switch (type) {
       case ReferenceTypeDescr::TYPE_ANY:
         EmitPreBarrier(masm, dest, MIRType::Value);
         masm.storeValue(val, dest);
         break;
 
       case ReferenceTypeDescr::TYPE_OBJECT: {
         EmitPreBarrier(masm, dest, MIRType::Object);
@@ -1038,26 +1013,25 @@ BaselineCacheIRCompiler::emitStoreTypedO
 
 typedef bool (*CallNativeSetterFn)(JSContext*, HandleFunction, HandleObject, HandleValue);
 static const VMFunction CallNativeSetterInfo =
     FunctionInfo<CallNativeSetterFn>(CallNativeSetter, "CallNativeSetter");
 
 bool
 BaselineCacheIRCompiler::emitCallNativeSetter()
 {
-    AutoStubFrame stubFrame(*this);
-
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address setterAddr(stubAddress(reader.stubOffset()));
     ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
 
     AutoScratchRegister scratch(allocator, masm);
 
     allocator.discardStack(masm);
 
+    AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch);
 
     // Load the callee in the scratch register.
     masm.loadPtr(setterAddr, scratch);
 
     masm.Push(val);
     masm.Push(obj);
     masm.Push(scratch);
@@ -1067,106 +1041,102 @@ BaselineCacheIRCompiler::emitCallNativeS
 
     stubFrame.leave(masm);
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitCallScriptedSetter()
 {
-    AutoStubFrame stubFrame(*this);
-
-    // We don't have many registers available on x86, so we use a single
-    // scratch register.
-    AutoScratchRegisterExcluding scratch(allocator, masm, ArgumentsRectifierReg);
+    AutoScratchRegisterExcluding scratch1(allocator, masm, ArgumentsRectifierReg);
+    AutoScratchRegister scratch2(allocator, masm);
 
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Address setterAddr(stubAddress(reader.stubOffset()));
     ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
 
-    // First, ensure our setter is non-lazy and has JIT code.
+    // First, ensure our setter is non-lazy and has JIT code. This also loads
+    // the callee in scratch1.
     {
         FailurePath* failure;
         if (!addFailurePath(&failure))
             return false;
 
-        masm.loadPtr(setterAddr, scratch);
-        masm.branchIfFunctionHasNoScript(scratch, failure->label());
-        masm.loadPtr(Address(scratch, JSFunction::offsetOfNativeOrScript()), scratch);
-        masm.loadBaselineOrIonRaw(scratch, scratch, failure->label());
+        masm.loadPtr(setterAddr, scratch1);
+        masm.branchIfFunctionHasNoScript(scratch1, failure->label());
+        masm.loadPtr(Address(scratch1, JSFunction::offsetOfNativeOrScript()), scratch2);
+        masm.loadBaselineOrIonRaw(scratch2, scratch2, failure->label());
     }
 
     allocator.discardStack(masm);
 
-    stubFrame.enter(masm, scratch);
+    AutoStubFrame stubFrame(*this);
+    stubFrame.enter(masm, scratch2);
 
     // Align the stack such that the JitFrameLayout is aligned on
     // JitStackAlignment.
     masm.alignJitStackBasedOnNArgs(1);
 
     // Setter is called with 1 argument, and |obj| as thisv. Note that we use
     // Push, not push, so that callJit will align the stack properly on ARM.
     masm.Push(val);
     masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(obj)));
 
     // Now that the object register is no longer needed, use it as second
     // scratch.
-    Register scratch2 = obj;
     EmitBaselineCreateStubFrameDescriptor(masm, scratch2, JitFrameLayout::Size());
     masm.Push(Imm32(1));  // ActualArgc
 
     // Push callee.
-    masm.loadPtr(setterAddr, scratch);
-    masm.Push(scratch);
+    masm.Push(scratch1);
 
     // Push frame descriptor.
     masm.Push(scratch2);
 
     // Load callee->nargs in scratch2 and the JIT code in scratch.
     Label noUnderflow;
-    masm.load16ZeroExtend(Address(scratch, JSFunction::offsetOfNargs()), scratch2);
-    masm.loadPtr(Address(scratch, JSFunction::offsetOfNativeOrScript()), scratch);
-    masm.loadBaselineOrIonRaw(scratch, scratch, nullptr);
+    masm.load16ZeroExtend(Address(scratch1, JSFunction::offsetOfNargs()), scratch2);
+    masm.loadPtr(Address(scratch1, JSFunction::offsetOfNativeOrScript()), scratch1);
+    masm.loadBaselineOrIonRaw(scratch1, scratch1, nullptr);
 
     // Handle arguments underflow.
     masm.branch32(Assembler::BelowOrEqual, scratch2, Imm32(1), &noUnderflow);
     {
         // Call the arguments rectifier.
-        MOZ_ASSERT(ArgumentsRectifierReg != scratch);
+        MOZ_ASSERT(ArgumentsRectifierReg != scratch1);
 
         JitCode* argumentsRectifier = cx_->runtime()->jitRuntime()->getArgumentsRectifier();
-        masm.movePtr(ImmGCPtr(argumentsRectifier), scratch);
-        masm.loadPtr(Address(scratch, JitCode::offsetOfCode()), scratch);
+        masm.movePtr(ImmGCPtr(argumentsRectifier), scratch1);
+        masm.loadPtr(Address(scratch1, JitCode::offsetOfCode()), scratch1);
         masm.movePtr(ImmWord(1), ArgumentsRectifierReg);
     }
 
     masm.bind(&noUnderflow);
-    masm.callJit(scratch);
+    masm.callJit(scratch1);
 
     stubFrame.leave(masm, true);
     return true;
 }
 
 typedef bool (*SetArrayLengthFn)(JSContext*, HandleObject, HandleValue, bool);
 static const VMFunction SetArrayLengthInfo =
     FunctionInfo<SetArrayLengthFn>(SetArrayLength, "SetArrayLength");
 
 bool
 BaselineCacheIRCompiler::emitCallSetArrayLength()
 {
-    AutoStubFrame stubFrame(*this);
-
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     bool strict = reader.readBool();
     ValueOperand val = allocator.useValueRegister(masm, reader.valOperandId());
 
     AutoScratchRegister scratch(allocator, masm);
 
     allocator.discardStack(masm);
 
+    AutoStubFrame stubFrame(*this);
     stubFrame.enter(masm, scratch);
 
     masm.Push(Imm32(strict));
     masm.Push(val);
     masm.Push(obj);
 
     if (!callVM(masm, SetArrayLengthInfo))
         return false;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -545,17 +545,17 @@ BaselineCompiler::emitStackCheck(bool ea
     Label forceCall;
     if (!earlyCheck && needsEarlyStackCheck()) {
         masm.branchTest32(Assembler::NonZero,
                           frame.addressOfFlags(),
                           Imm32(BaselineFrame::OVER_RECURSED),
                           &forceCall);
     }
 
-    void* contextAddr = &cx->zone()->group()->context;
+    void* contextAddr = cx->zone()->group()->addressOfOwnerContext();
     masm.loadPtr(AbsoluteAddress(contextAddr), R0.scratchReg());
     masm.branchPtr(Assembler::BelowOrEqual,
                    Address(R0.scratchReg(), offsetof(JSContext, jitStackLimit)), R1.scratchReg(),
                    &skipCall);
 
     if (!earlyCheck && needsEarlyStackCheck())
         masm.bind(&forceCall);
 
@@ -694,17 +694,17 @@ static const VMFunction InterruptCheckIn
     FunctionInfo<InterruptCheckFn>(InterruptCheck, "InterruptCheck");
 
 bool
 BaselineCompiler::emitInterruptCheck()
 {
     frame.syncStack(0);
 
     Label done;
-    void* context = &cx->zone()->group()->context;
+    void* context = cx->zone()->group()->addressOfOwnerContext();
     masm.loadPtr(AbsoluteAddress(context), R0.scratchReg());
     masm.branch32(Assembler::Equal,
                   Address(R0.scratchReg(), offsetof(JSContext, interrupt_)), Imm32(0),
                   &done);
 
     prepareVMCall();
     if (!callVM(InterruptCheckInfo))
         return false;
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -342,17 +342,18 @@ static void
 SpewPatchStubFrame(ICStub* oldStub, ICStub* newStub)
 {
     JitSpew(JitSpew_BaselineDebugModeOSR,
             "Patch   stub %p -> %p on BaselineStub frame (%s)",
             oldStub, newStub, newStub ? ICStub::KindString(newStub->kind()) : "exception handler");
 }
 
 static void
-PatchBaselineFramesForDebugMode(JSContext* cx, const Debugger::ExecutionObservableSet& obs,
+PatchBaselineFramesForDebugMode(JSContext* cx, const CooperatingContext& target,
+                                const Debugger::ExecutionObservableSet& obs,
                                 const ActivationIterator& activation,
                                 DebugModeOSREntryVector& entries, size_t* start)
 {
     //
     // Recompile Patching Overview
     //
     // When toggling debug mode with live baseline scripts on the stack, we
     // could have entered the VM via the following ways from the baseline
@@ -416,17 +417,17 @@ PatchBaselineFramesForDebugMode(JSContex
                 // the baseline frame here, we resume right after the IC
                 // returns.
                 //
                 // Since we're using the same IC stub code, we can resume
                 // directly to the IC resume address.
                 uint8_t* retAddr = bl->returnAddressForIC(bl->icEntryFromPCOffset(pcOffset));
                 SpewPatchBaselineFrame(prev->returnAddress(), retAddr, script, kind, pc);
                 DebugModeOSRVolatileJitFrameIterator::forwardLiveIterators(
-                    cx, prev->returnAddress(), retAddr);
+                    target, prev->returnAddress(), retAddr);
                 prev->setReturnAddress(retAddr);
                 entryIndex++;
                 break;
             }
 
             if (kind == ICEntry::Kind_Invalid) {
                 // Case H above.
                 //
@@ -445,17 +446,17 @@ PatchBaselineFramesForDebugMode(JSContex
                 uint8_t* retAddr;
                 if (cx->runtime()->geckoProfiler().enabled())
                     retAddr = bl->nativeCodeForPC(script, pc);
                 else
                     retAddr = nullptr;
                 SpewPatchBaselineFrameFromExceptionHandler(prev->returnAddress(), retAddr,
                                                            script, pc);
                 DebugModeOSRVolatileJitFrameIterator::forwardLiveIterators(
-                    cx, prev->returnAddress(), retAddr);
+                    target, prev->returnAddress(), retAddr);
                 prev->setReturnAddress(retAddr);
                 entryIndex++;
                 break;
             }
 
             // Case F above.
             //
             // We undo a previous recompile by handling cases B, C, D, E, I or J
@@ -832,23 +833,25 @@ bool
 jit::RecompileOnStackBaselineScriptsForDebugMode(JSContext* cx,
                                                  const Debugger::ExecutionObservableSet& obs,
                                                  Debugger::IsObserving observing)
 {
     // First recompile the active scripts on the stack and patch the live
     // frames.
     Vector<DebugModeOSREntry> entries(cx);
 
-    for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
-        if (iter->isJit()) {
-            if (!CollectJitStackScripts(cx, obs, iter, entries))
-                return false;
-        } else if (iter->isInterpreter()) {
-            if (!CollectInterpreterStackScripts(cx, obs, iter, entries))
-                return false;
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        for (ActivationIterator iter(cx, target); !iter.done(); ++iter) {
+            if (iter->isJit()) {
+                if (!CollectJitStackScripts(cx, obs, iter, entries))
+                    return false;
+            } else if (iter->isInterpreter()) {
+                if (!CollectInterpreterStackScripts(cx, obs, iter, entries))
+                    return false;
+            }
         }
     }
 
     if (entries.empty())
         return true;
 
     // When the profiler is enabled, we need to have suppressed sampling,
     // since the basline jit scripts are in a state of flux.
@@ -887,21 +890,23 @@ jit::RecompileOnStackBaselineScriptsForD
 
     for (UniqueScriptOSREntryIter iter(entries); !iter.done(); ++iter) {
         const DebugModeOSREntry& entry = iter.entry();
         if (entry.recompiled())
             BaselineScript::Destroy(cx->runtime()->defaultFreeOp(), entry.oldBaselineScript);
     }
 
     size_t processed = 0;
-    for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
-        if (iter->isJit())
-            PatchBaselineFramesForDebugMode(cx, obs, iter, entries, &processed);
-        else if (iter->isInterpreter())
-            SkipInterpreterFrameEntries(obs, iter, entries, &processed);
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        for (ActivationIterator iter(cx, target); !iter.done(); ++iter) {
+            if (iter->isJit())
+                PatchBaselineFramesForDebugMode(cx, target, obs, iter, entries, &processed);
+            else if (iter->isInterpreter())
+                SkipInterpreterFrameEntries(obs, iter, entries, &processed);
+        }
     }
     MOZ_ASSERT(processed == entries.length());
 
     return true;
 }
 
 void
 BaselineDebugModeOSRInfo::popValueInto(PCMappingSlotInfo::SlotLocation loc, Value* vp)
@@ -1158,17 +1163,17 @@ JitRuntime::generateBaselineDebugModeOSR
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "BaselineDebugModeOSRHandler");
 #endif
 
     return code;
 }
 
 /* static */ void
-DebugModeOSRVolatileJitFrameIterator::forwardLiveIterators(JSContext* cx,
+DebugModeOSRVolatileJitFrameIterator::forwardLiveIterators(const CooperatingContext& cx,
                                                            uint8_t* oldAddr, uint8_t* newAddr)
 {
     DebugModeOSRVolatileJitFrameIterator* iter;
-    for (iter = cx->liveVolatileJitFrameIterators_; iter; iter = iter->prev) {
+    for (iter = cx.context()->liveVolatileJitFrameIterators_; iter; iter = iter->prev) {
         if (iter->returnAddressToFp_ == oldAddr)
             iter->returnAddressToFp_ = newAddr;
     }
 }
--- a/js/src/jit/BaselineDebugModeOSR.h
+++ b/js/src/jit/BaselineDebugModeOSR.h
@@ -99,17 +99,18 @@ class DebugModeOSRVolatileJitFrameIterat
         *stack = this;
     }
 
     ~DebugModeOSRVolatileJitFrameIterator() {
         MOZ_ASSERT(*stack == this);
         *stack = prev;
     }
 
-    static void forwardLiveIterators(JSContext* cx, uint8_t* oldAddr, uint8_t* newAddr);
+    static void forwardLiveIterators(const CooperatingContext& target,
+                                     uint8_t* oldAddr, uint8_t* newAddr);
 };
 
 //
 // Auxiliary info to help the DebugModeOSRHandler fix up state.
 //
 struct BaselineDebugModeOSRInfo
 {
     uint8_t* resumeAddr;
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1210,17 +1210,17 @@ jit::ToggleBaselineTraceLoggerEngine(JSR
                 continue;
             script->baselineScript()->toggleTraceLoggerEngine(enable);
         }
     }
 }
 #endif
 
 static void
-MarkActiveBaselineScripts(JSRuntime* rt, const JitActivationIterator& activation)
+MarkActiveBaselineScripts(JSContext* cx, const JitActivationIterator& activation)
 {
     for (jit::JitFrameIterator iter(activation); !iter.done(); ++iter) {
         switch (iter.type()) {
           case JitFrame_BaselineJS:
             iter.script()->baselineScript()->setActive();
             break;
           case JitFrame_Exit:
             if (iter.exitFrame()->is<LazyLinkExitFrameLayout>()) {
@@ -1228,26 +1228,28 @@ MarkActiveBaselineScripts(JSRuntime* rt,
                 ScriptFromCalleeToken(ll->jsFrame()->calleeToken())->baselineScript()->setActive();
             }
             break;
           case JitFrame_Bailout:
           case JitFrame_IonJS: {
             // Keep the baseline script around, since bailouts from the ion
             // jitcode might need to re-enter into the baseline jitcode.
             iter.script()->baselineScript()->setActive();
-            for (InlineFrameIterator inlineIter(rt, &iter); inlineIter.more(); ++inlineIter)
+            for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter)
                 inlineIter.script()->baselineScript()->setActive();
             break;
           }
           default:;
         }
     }
 }
 
 void
 jit::MarkActiveBaselineScripts(Zone* zone)
 {
-    JSRuntime* rt = zone->runtimeFromMainThread();
-    for (JitActivationIterator iter(rt); !iter.done(); ++iter) {
+    if (zone->isAtomsZone())
+        return;
+    JSContext* cx = TlsContext.get();
+    for (JitActivationIterator iter(cx, zone->group()->ownerContext()); !iter.done(); ++iter) {
         if (iter->compartment()->zone() == zone)
-            MarkActiveBaselineScripts(rt, iter);
+            MarkActiveBaselineScripts(cx, iter);
     }
 }
--- a/js/src/jit/C1Spewer.cpp
+++ b/js/src/jit/C1Spewer.cpp
@@ -132,19 +132,21 @@ C1Spewer::spewPass(GenericPrinter& out, 
     out.printf("    predecessors");
     for (uint32_t i = 0; i < block->numPredecessors(); i++) {
         MBasicBlock* pred = block->getPredecessor(i);
         out.printf(" \"B%d\"", pred->id());
     }
     out.printf("\n");
 
     out.printf("    successors");
-    for (uint32_t i = 0; i < block->numSuccessors(); i++) {
-        MBasicBlock* successor = block->getSuccessor(i);
-        out.printf(" \"B%d\"", successor->id());
+    if (block->hasLastIns()) {
+        for (uint32_t i = 0; i < block->numSuccessors(); i++) {
+            MBasicBlock* successor = block->getSuccessor(i);
+            out.printf(" \"B%d\"", successor->id());
+        }
     }
     out.printf("\n");
 
     out.printf("    xhandlers\n");
     out.printf("    flags\n");
 
     if (block->lir() && block->lir()->begin() != block->lir()->end()) {
         out.printf("    first_lir_id %d\n", block->lir()->firstId());
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -350,16 +350,17 @@ class MOZ_RAII CacheRegisterAllocator
 
     void allocateFixedRegister(MacroAssembler& masm, Register reg);
     void allocateFixedValueRegister(MacroAssembler& masm, ValueOperand reg);
 
     // Releases a register so it can be reused later.
     void releaseRegister(Register reg) {
         MOZ_ASSERT(currentOpRegs_.has(reg));
         availableRegs_.add(reg);
+        currentOpRegs_.take(reg);
     }
     void releaseValueRegister(ValueOperand reg) {
 #ifdef JS_NUNBOX32
         releaseRegister(reg.payloadReg());
         releaseRegister(reg.typeReg());
 #else
         releaseRegister(reg.valueReg());
 #endif
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -50,16 +50,22 @@ CompileRuntime::jitSupportsFloatingPoint
 }
 
 bool
 CompileRuntime::hadOutOfMemory()
 {
     return runtime()->hadOutOfMemory;
 }
 
+bool
+CompileRuntime::profilingScripts()
+{
+    return runtime()->profilingScripts;
+}
+
 const JSAtomState&
 CompileRuntime::names()
 {
     return *runtime()->commonNames;
 }
 
 const PropertyName*
 CompileRuntime::emptyString()
@@ -89,17 +95,17 @@ const WellKnownSymbols&
 CompileRuntime::wellKnownSymbols()
 {
     return *runtime()->wellKnownSymbols;
 }
 
 const void*
 CompileRuntime::addressOfActiveJSContext()
 {
-    return &runtime()->activeContext;
+    return runtime()->addressOfActiveContext();
 }
 
 #ifdef DEBUG
 bool
 CompileRuntime::isInsideNursery(gc::Cell* cell)
 {
     return UninlinedIsInsideNursery(cell);
 }
@@ -147,17 +153,17 @@ CompileZone::addressOfIonBailAfter()
 {
     return zone()->group()->addressOfIonBailAfter();
 }
 #endif
 
 const void*
 CompileZone::addressOfJSContext()
 {
-    return &zone()->group()->context;
+    return zone()->group()->addressOfOwnerContext();
 }
 
 const void*
 CompileZone::addressOfNeedsIncrementalBarrier()
 {
     return zone()->addressOfNeedsIncrementalBarrier();
 }
 
@@ -188,22 +194,16 @@ CompileZone::nurseryExists()
 
 void
 CompileZone::setMinorGCShouldCancelIonCompilations()
 {
     MOZ_ASSERT(CurrentThreadCanAccessZone(zone()));
     zone()->group()->storeBuffer().setShouldCancelIonCompilations();
 }
 
-bool
-CompileZone::profilingScripts()
-{
-    return zone()->group()->profilingScripts;
-}
-
 JSCompartment*
 CompileCompartment::compartment()
 {
     return reinterpret_cast<JSCompartment*>(this);
 }
 
 /* static */ CompileCompartment*
 CompileCompartment::get(JSCompartment* comp)
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -33,16 +33,17 @@ class CompileRuntime
 
     const JitRuntime* jitRuntime();
 
     // Compilation does not occur off thread when the Gecko Profiler is enabled.
     GeckoProfiler& geckoProfiler();
 
     bool jitSupportsFloatingPoint();
     bool hadOutOfMemory();
+    bool profilingScripts();
 
     const JSAtomState& names();
     const PropertyName* emptyString();
     const StaticStrings& staticStrings();
     const Value& NaNValue();
     const Value& positiveInfinityValue();
     const WellKnownSymbols& wellKnownSymbols();
     const void* addressOfActiveJSContext();
@@ -74,18 +75,16 @@ class CompileZone
     const void* addressOfJSContext();
     const void* addressOfNeedsIncrementalBarrier();
     const void* addressOfFreeList(gc::AllocKind allocKind);
     const void* addressOfNurseryPosition();
     const void* addressOfNurseryCurrentEnd();
 
     bool nurseryExists();
     void setMinorGCShouldCancelIonCompilations();
-
-    bool profilingScripts();
 };
 
 class JitCompartment;
 
 class CompileCompartment
 {
     JSCompartment* compartment();
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -3189,18 +3189,20 @@ InvalidateActivation(FreeOp* fop, const 
 void
 jit::InvalidateAll(FreeOp* fop, Zone* zone)
 {
     // The caller should previously have cancelled off thread compilation.
 #ifdef DEBUG
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         MOZ_ASSERT(!HasOffThreadIonCompile(comp));
 #endif
-
-    for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter) {
+    if (zone->isAtomsZone())
+        return;
+    JSContext* cx = TlsContext.get();
+    for (JitActivationIterator iter(cx, zone->group()->ownerContext()); !iter.done(); ++iter) {
         if (iter->compartment()->zone() == zone) {
             JitSpew(JitSpew_IonInvalidate, "Invalidating all frames for GC");
             InvalidateActivation(fop, iter, true);
         }
     }
 }
 
 
@@ -3236,17 +3238,25 @@ jit::Invalidate(TypeZone& types, FreeOp*
         numInvalidations++;
     }
 
     if (!numInvalidations) {
         JitSpew(JitSpew_IonInvalidate, " No IonScript invalidation.");
         return;
     }
 
-    for (JitActivationIterator iter(fop->runtime()); !iter.done(); ++iter)
+    // This method can be called both during GC and during the course of normal
+    // script execution. In the former case this class will already be on the
+    // stack, and in the latter case the invalidations will all be on the
+    // current thread's stack, but the assertion under ActivationIterator can't
+    // tell that this is a thread local use of the iterator.
+    JSRuntime::AutoProhibitActiveContextChange apacc(fop->runtime());
+
+    JSContext* cx = TlsContext.get();
+    for (JitActivationIterator iter(cx, types.zone()->group()->ownerContext()); !iter.done(); ++iter)
         InvalidateActivation(fop, iter, false);
 
     // Drop the references added above. If a script was never active, its
     // IonScript will be immediately destroyed. Otherwise, it will be held live
     // until its last invalidated frame is destroyed.
     for (size_t i = 0; i < invalid.length(); i++) {
         CompilerOutput* co = invalid[i].compilerOutput(types);
         if (!co)
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -83,17 +83,17 @@ class JitContext
     TempAllocator* temp;
 
     // Wrappers with information about the current runtime/compartment for use
     // during compilation.
     CompileRuntime* runtime;
     CompileCompartment* compartment;
 
     bool hasProfilingScripts() const {
-        return compartment && compartment->zone()->profilingScripts();
+        return runtime && !!runtime->profilingScripts();
     }
 
     int getNextAssemblerId() {
         return assemblerCount_++;
     }
   private:
     JitContext* prev_;
     int assemblerCount_;
--- a/js/src/jit/JSONSpewer.cpp
+++ b/js/src/jit/JSONSpewer.cpp
@@ -139,32 +139,36 @@ JSONSpewer::spewMIR(MIRGraph* mir)
     for (MBasicBlockIterator block(mir->begin()); block != mir->end(); block++) {
         beginObject();
 
         integerProperty("number", block->id());
         if (block->getHitState() == MBasicBlock::HitState::Count)
             integerProperty("count", block->getHitCount());
 
         beginListProperty("attributes");
-        if (block->isLoopBackedge())
-            stringValue("backedge");
-        if (block->isLoopHeader())
-            stringValue("loopheader");
-        if (block->isSplitEdge())
-            stringValue("splitedge");
+        if (block->hasLastIns()) {
+            if (block->isLoopBackedge())
+                stringValue("backedge");
+            if (block->isLoopHeader())
+                stringValue("loopheader");
+            if (block->isSplitEdge())
+                stringValue("splitedge");
+        }
         endList();
 
         beginListProperty("predecessors");
         for (size_t i = 0; i < block->numPredecessors(); i++)
             integerValue(block->getPredecessor(i)->id());
         endList();
 
         beginListProperty("successors");
-        for (size_t i = 0; i < block->numSuccessors(); i++)
-            integerValue(block->getSuccessor(i)->id());
+        if (block->hasLastIns()) {
+            for (size_t i = 0; i < block->numSuccessors(); i++)
+                integerValue(block->getSuccessor(i)->id());
+        }
         endList();
 
         beginListProperty("instructions");
         for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd(); phi++)
             spewMDef(*phi);
         for (MInstructionIterator i(block->begin()); i != block->end(); i++)
             spewMDef(*i);
         endList();
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -279,17 +279,17 @@ class JitProfilingFrameIterator
     MOZ_MUST_USE bool tryInitWithPC(void* pc);
     MOZ_MUST_USE bool tryInitWithTable(JitcodeGlobalTable* table, void* pc, JSRuntime* rt,
                                        bool forLastCallSite);
     void fixBaselineReturnAddress();
 
     void moveToNextFrame(CommonFrameLayout* frame);
 
   public:
-    JitProfilingFrameIterator(JSRuntime* rt,
+    JitProfilingFrameIterator(JSContext* cx,
                               const JS::ProfilingFrameIterator::RegisterState& state);
     explicit JitProfilingFrameIterator(void* exitFrame);
 
     void operator++();
     bool done() const { return fp_ == nullptr; }
 
     void* fp() const { MOZ_ASSERT(!done()); return fp_; }
     void* stackAddress() const { return fp(); }
@@ -651,17 +651,16 @@ class InlineFrameIterator
 
   private:
     void findNextFrame();
     JSObject* computeEnvironmentChain(const Value& envChainValue, MaybeReadFallback& fallback,
                                       bool* hasInitialEnv = nullptr) const;
 
   public:
     InlineFrameIterator(JSContext* cx, const JitFrameIterator* iter);
-    InlineFrameIterator(JSRuntime* rt, const JitFrameIterator* iter);
     InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter);
 
     bool more() const {
         return frame_ && framesRead_ < frameCount_;
     }
 
     // Due to optimizations, we are not always capable of reading the callee of
     // inlined frames without invalidating the IonCode. This function might
@@ -800,16 +799,17 @@ class InlineFrameIterator
     }
     jsbytecode* pc() const {
         return pc_;
     }
     SnapshotIterator snapshotIterator() const {
         return si_;
     }
     bool isFunctionFrame() const;
+    bool isModuleFrame() const;
     bool isConstructing() const;
 
     JSObject* environmentChain(MaybeReadFallback& fallback) const {
         SnapshotIterator s(si_);
 
         // envChain
         Value v = s.maybeRead(fallback);
         return computeEnvironmentChain(v, fallback);
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1501,54 +1501,54 @@ TraceJitActivation(JSTracer* trc, const 
             break;
           default:
             MOZ_CRASH("unexpected frame type");
         }
     }
 }
 
 void
-TraceJitActivations(JSRuntime* rt, JSTracer* trc)
+TraceJitActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc)
 {
-    for (JitActivationIterator activations(rt); !activations.done(); ++activations)
+    for (JitActivationIterator activations(cx, target); !activations.done(); ++activations)
         TraceJitActivation(trc, activations);
 }
 
 JSCompartment*
-TopmostIonActivationCompartment(JSRuntime* rt)
+TopmostIonActivationCompartment(JSContext* cx)
 {
-    for (JitActivationIterator activations(rt); !activations.done(); ++activations) {
+    for (JitActivationIterator activations(cx); !activations.done(); ++activations) {
         for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
             if (frames.type() == JitFrame_IonJS)
                 return activations.activation()->compartment();
         }
     }
     return nullptr;
 }
 
-void UpdateJitActivationsForMinorGC(JSRuntime* rt, JSTracer* trc)
+void UpdateJitActivationsForMinorGC(ZoneGroup* group, JSTracer* trc)
 {
     MOZ_ASSERT(JS::CurrentThreadIsHeapMinorCollecting());
-    for (JitActivationIterator activations(rt); !activations.done(); ++activations) {
+    JSContext* cx = TlsContext.get();
+    for (JitActivationIterator activations(cx, group->ownerContext()); !activations.done(); ++activations) {
         for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
             if (frames.type() == JitFrame_IonJS)
                 UpdateIonJSFrameForMinorGC(trc, frames);
         }
     }
 }
 
 void
 GetPcScript(JSContext* cx, JSScript** scriptRes, jsbytecode** pcRes)
 {
     JitSpew(JitSpew_IonSnapshots, "Recover PC & Script from the last frame.");
 
     // Recover the return address so that we can look it up in the
     // PcScriptCache, as script/pc computation is expensive.
-    JSRuntime* rt = cx->runtime();
-    JitActivationIterator iter(rt);
+    JitActivationIterator iter(cx);
     JitFrameIterator it(iter);
     uint8_t* retAddr;
     if (it.isExitFrame()) {
         ++it;
 
         // Skip rectifier frames.
         if (it.isRectifier()) {
             ++it;
@@ -1586,20 +1586,20 @@ GetPcScript(JSContext* cx, JSScript** sc
     uint32_t hash;
     if (retAddr) {
         hash = PcScriptCache::Hash(retAddr);
 
         // Lazily initialize the cache. The allocation may safely fail and will not GC.
         if (MOZ_UNLIKELY(cx->ionPcScriptCache == nullptr)) {
             cx->ionPcScriptCache = (PcScriptCache*)js_malloc(sizeof(struct PcScriptCache));
             if (cx->ionPcScriptCache)
-                cx->ionPcScriptCache->clear(rt->gc.gcNumber());
+                cx->ionPcScriptCache->clear(cx->runtime()->gc.gcNumber());
         }
 
-        if (cx->ionPcScriptCache && cx->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes))
+        if (cx->ionPcScriptCache && cx->ionPcScriptCache->get(cx->runtime(), hash, retAddr, scriptRes, pcRes))
             return;
     }
 
     // Lookup failed: undertake expensive process to recover the innermost inlined frame.
     jsbytecode* pc = nullptr;
     if (it.isIonJS() || it.isBailoutJS()) {
         InlineFrameIterator ifi(cx, &it);
         *scriptRes = ifi.script();
@@ -2300,24 +2300,16 @@ JitFrameIterator::osiIndex() const
 InlineFrameIterator::InlineFrameIterator(JSContext* cx, const JitFrameIterator* iter)
   : calleeTemplate_(cx),
     calleeRVA_(),
     script_(cx)
 {
     resetOn(iter);
 }
 
-InlineFrameIterator::InlineFrameIterator(JSRuntime* rt, const JitFrameIterator* iter)
-  : calleeTemplate_(TlsContext.get()),
-    calleeRVA_(),
-    script_(TlsContext.get())
-{
-    resetOn(iter);
-}
-
 InlineFrameIterator::InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter)
   : frame_(iter ? iter->frame_ : nullptr),
     framesRead_(0),
     frameCount_(iter ? iter->frameCount_ : UINT32_MAX),
     calleeTemplate_(cx),
     calleeRVA_(),
     script_(cx)
 {
@@ -2471,29 +2463,38 @@ InlineFrameIterator::computeEnvironmentC
     }
 
     // Note we can hit this case even for functions with a CallObject, in case
     // we are walking the frame during the function prologue, before the env
     // chain has been initialized.
     if (isFunctionFrame())
         return callee(fallback)->environment();
 
+    if (isModuleFrame())
+        return script()->module()->environment();
+
     // Ion does not handle non-function scripts that have anything other than
     // the global on their env chain.
     MOZ_ASSERT(!script()->isForEval());
     MOZ_ASSERT(!script()->hasNonSyntacticScope());
     return &script()->global().lexicalEnvironment();
 }
 
 bool
 InlineFrameIterator::isFunctionFrame() const
 {
     return !!calleeTemplate_;
 }
 
+bool
+InlineFrameIterator::isModuleFrame() const
+{
+    return script()->module();
+}
+
 MachineState
 MachineState::FromBailout(RegisterDump::GPRArray& regs, RegisterDump::FPUArray& fpregs)
 {
     MachineState machine;
 
     for (unsigned i = 0; i < Registers::Total; i++)
         machine.setRegisterLocation(Register::FromCode(i), &regs[i].r);
 #ifdef JS_CODEGEN_ARM
@@ -2818,21 +2819,20 @@ JitFrameIterator::verifyReturnAddressUsi
         }
     }
 
     return true;
 }
 #endif // DEBUG
 
 JitProfilingFrameIterator::JitProfilingFrameIterator(
-        JSRuntime* rt, const JS::ProfilingFrameIterator::RegisterState& state)
+        JSContext* cx, const JS::ProfilingFrameIterator::RegisterState& state)
 {
     // If no profilingActivation is live, initialize directly to
     // end-of-iteration state.
-    JSContext* cx = rt->unsafeContextFromAnyThread();
     if (!cx->profilingActivation()) {
         type_ = JitFrame_Entry;
         fp_ = nullptr;
         returnAddressToFp_ = nullptr;
         return;
     }
 
     MOZ_ASSERT(cx->profilingActivation()->isJit());
@@ -2848,36 +2848,36 @@ JitProfilingFrameIterator::JitProfilingF
         returnAddressToFp_ = nullptr;
         return;
     }
 
     // Get the fp from the current profilingActivation
     fp_ = (uint8_t*) act->lastProfilingFrame();
     void* lastCallSite = act->lastProfilingCallSite();
 
-    JitcodeGlobalTable* table = rt->jitRuntime()->getJitcodeGlobalTable();
+    JitcodeGlobalTable* table = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
 
     // Profiler sampling must NOT be suppressed if we are here.
     MOZ_ASSERT(cx->isProfilerSamplingEnabled());
 
     // Try initializing with sampler pc
     if (tryInitWithPC(state.pc))
         return;
 
     // Try initializing with sampler pc using native=>bytecode table.
-    if (tryInitWithTable(table, state.pc, rt, /* forLastCallSite = */ false))
+    if (tryInitWithTable(table, state.pc, cx->runtime(), /* forLastCallSite = */ false))
         return;
 
     // Try initializing with lastProfilingCallSite pc
     if (lastCallSite) {
         if (tryInitWithPC(lastCallSite))
             return;
 
         // Try initializing with lastProfilingCallSite pc using native=>bytecode table.
-        if (tryInitWithTable(table, lastCallSite, rt, /* forLastCallSite = */ true))
+        if (tryInitWithTable(table, lastCallSite, cx->runtime(), /* forLastCallSite = */ true))
             return;
     }
 
     MOZ_ASSERT(frameScript()->hasBaselineScript());
 
     // If nothing matches, for now just assume we are at the start of the last frame's
     // baseline jit code.
     type_ = JitFrame_BaselineJS;
@@ -3137,17 +3137,17 @@ InvalidationBailoutStack::checkInvariant
     uint8_t* osiPoint = osiPointReturnAddress();
     MOZ_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit);
 #endif
 }
 
 void
 AssertJitStackInvariants(JSContext* cx)
 {
-    for (JitActivationIterator activations(cx->runtime()); !activations.done(); ++activations) {
+    for (JitActivationIterator activations(cx); !activations.done(); ++activations) {
         JitFrameIterator frames(activations);
         size_t prevFrameSize = 0;
         size_t frameSize = 0;
         bool isScriptedCallee = false;
         for (; !frames.done(); ++frames) {
             size_t calleeFp = reinterpret_cast<size_t>(frames.fp());
             size_t callerFp = reinterpret_cast<size_t>(frames.prevFp());
             MOZ_ASSERT(callerFp >= calleeFp);
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -279,22 +279,22 @@ struct ResumeFromException
 
     BaselineBailoutInfo* bailoutInfo;
 };
 
 void HandleException(ResumeFromException* rfe);
 
 void EnsureBareExitFrame(JSContext* cx, JitFrameLayout* frame);
 
-void TraceJitActivations(JSRuntime* rt, JSTracer* trc);
+void TraceJitActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc);
 
 JSCompartment*
-TopmostIonActivationCompartment(JSRuntime* rt);
+TopmostIonActivationCompartment(JSContext* cx);
 
-void UpdateJitActivationsForMinorGC(JSRuntime* rt, JSTracer* trc);
+void UpdateJitActivationsForMinorGC(ZoneGroup* group, JSTracer* trc);
 
 static inline uint32_t
 EncodeFrameHeaderSize(size_t headerSize)
 {
     MOZ_ASSERT((headerSize % sizeof(uintptr_t)) == 0);
 
     uint32_t headerSizeWords = headerSize / sizeof(uintptr_t);
     MOZ_ASSERT(headerSizeWords <= FRAME_HEADER_SIZE_MASK);
--- a/js/src/jit/JitOptions.cpp
+++ b/js/src/jit/JitOptions.cpp
@@ -100,16 +100,19 @@ DefaultJitOptions::DefaultJitOptions()
     SET_DEFAULT(disableInlining, false);
 
     // Toggles whether loop invariant code motion is globally disabled.
     SET_DEFAULT(disableLicm, false);
 
     // Toggles whether Loop Unrolling is globally disabled.
     SET_DEFAULT(disableLoopUnrolling, true);
 
+    // Toggles wheter optimization tracking is globally disabled.
+    SET_DEFAULT(disableOptimizationTracking, true);
+
     // Toggle whether Profile Guided Optimization is globally disabled.
     SET_DEFAULT(disablePgo, false);
 
     // Toggles whether instruction reordering is globally disabled.
     SET_DEFAULT(disableInstructionReordering, false);
 
     // Toggles whether Range Analysis is globally disabled.
     SET_DEFAULT(disableRangeAnalysis, false);
--- a/js/src/jit/JitOptions.h
+++ b/js/src/jit/JitOptions.h
@@ -52,16 +52,17 @@ struct DefaultJitOptions
     bool disableEaa;
     bool disableEagerSimdUnbox;
     bool disableEdgeCaseAnalysis;
     bool disableFlowAA;
     bool disableGvn;
     bool disableInlining;
     bool disableLicm;
     bool disableLoopUnrolling;
+    bool disableOptimizationTracking;
     bool disablePgo;
     bool disableInstructionReordering;
     bool disableRangeAnalysis;
     bool disableRecoverIns;
     bool disableScalarReplacement;
     bool disableCacheIR;
     bool disableSharedStubs;
     bool disableSincos;
--- a/js/src/jit/JitSpewer.cpp
+++ b/js/src/jit/JitSpewer.cpp
@@ -512,18 +512,20 @@ jit::CheckLogging()
     if (ContainsFlag(env, "cacheflush"))
         EnableChannel(JitSpew_CacheFlush);
     if (ContainsFlag(env, "logs"))
         EnableIonDebugAsyncLogging();
     if (ContainsFlag(env, "logs-sync"))
         EnableIonDebugSyncLogging();
     if (ContainsFlag(env, "profiling"))
         EnableChannel(JitSpew_Profiling);
-    if (ContainsFlag(env, "trackopts"))
+    if (ContainsFlag(env, "trackopts")) {
+        JitOptions.disableOptimizationTracking = false;
         EnableChannel(JitSpew_OptimizationTracking);
+    }
     if (ContainsFlag(env, "trackopts-ext"))
         EnableChannel(JitSpew_OptimizationTrackingExtended);
     if (ContainsFlag(env, "dump-mir-expr"))
         EnableChannel(JitSpew_MIRExpressions);
     if (ContainsFlag(env, "cfg"))
         EnableChannel(JitSpew_CFG);
     if (ContainsFlag(env, "all"))
         LoggingBits = uint64_t(-1);
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -20,16 +20,17 @@
 #include "gc/Statistics.h"
 #include "jit/BaselineJIT.h"
 #include "jit/JitSpewer.h"
 #include "js/Vector.h"
 #include "vm/GeckoProfiler.h"
 
 #include "jsscriptinlines.h"
 
+#include "vm/GeckoProfiler-inl.h"
 #include "vm/TypeInference-inl.h"
 
 using mozilla::Maybe;
 
 namespace js {
 namespace jit {
 
 static inline JitcodeRegionEntry
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -97,17 +97,18 @@ class MIRGenerator
         return instrumentedProfiling_;
     }
 
     bool isProfilerInstrumentationEnabled() {
         return !compilingWasm() && instrumentedProfiling();
     }
 
     bool isOptimizationTrackingEnabled() {
-        return isProfilerInstrumentationEnabled() && !info().isAnalysis();
+        return isProfilerInstrumentationEnabled() && !info().isAnalysis() &&
+               !JitOptions.disableOptimizationTracking;
     }
 
     bool safeForMinorGC() const {
         return safeForMinorGC_;
     }
     void setNotSafeForMinorGC() {
         safeForMinorGC_ = false;
     }
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -1239,17 +1239,17 @@ IonTrackedOptimizationsTypeInfo::ForEach
             }
             op_.readType("native", name, name ? nullptr : locationBuf, Nothing());
             return;
         }
 
         const char* filename;
         Maybe<unsigned> lineno;
         InterpretedFunctionFilenameAndLineNumber(fun, &filename, &lineno);
-        op_.readType(tracked.constructor ? "constructor" : "function",
+        op_.readType(tracked.hasConstructor() ? "constructor" : "function",
                      name, filename, lineno);
         return;
     }
 
     const char* className = ty.objectKey()->clasp()->name;
     snprintf(buf, bufsize, "[object %s]", className);
 
     if (tracked.hasAllocationSite()) {
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -595,50 +595,43 @@ ICStubCompiler::callTypeUpdateIC(MacroAs
 
     EmitCallTypeUpdateIC(masm, code, objectOffset);
     return true;
 }
 
 void
 ICStubCompiler::enterStubFrame(MacroAssembler& masm, Register scratch)
 {
-    if (engine_ == Engine::Baseline) {
-        EmitBaselineEnterStubFrame(masm, scratch);
+    MOZ_ASSERT(engine_ == Engine::Baseline);
+    EmitBaselineEnterStubFrame(masm, scratch);
 #ifdef DEBUG
-        framePushedAtEnterStubFrame_ = masm.framePushed();
+    framePushedAtEnterStubFrame_ = masm.framePushed();
 #endif
-    } else {
-        EmitIonEnterStubFrame(masm, scratch);
-    }
 
     MOZ_ASSERT(!inStubFrame_);
     inStubFrame_ = true;
 
 #ifdef DEBUG
     entersStubFrame_ = true;
 #endif
 }
 
 void
 ICStubCompiler::leaveStubFrame(MacroAssembler& masm, bool calledIntoIon)
 {
     MOZ_ASSERT(entersStubFrame_ && inStubFrame_);
     inStubFrame_ = false;
 
-    if (engine_ == Engine::Baseline) {
+    MOZ_ASSERT(engine_ == Engine::Baseline);
 #ifdef DEBUG
-        masm.setFramePushed(framePushedAtEnterStubFrame_);
-        if (calledIntoIon)
-            masm.adjustFrame(sizeof(intptr_t)); // Calls into ion have this extra.
+    masm.setFramePushed(framePushedAtEnterStubFrame_);
+    if (calledIntoIon)
+        masm.adjustFrame(sizeof(intptr_t)); // Calls into ion have this extra.
 #endif
-
-        EmitBaselineLeaveStubFrame(masm, calledIntoIon);
-    } else {
-        EmitIonLeaveStubFrame(masm);
-    }
+    EmitBaselineLeaveStubFrame(masm, calledIntoIon);
 }
 
 void
 ICStubCompiler::pushStubPayload(MacroAssembler& masm, Register scratch)
 {
     if (engine_ == Engine::IonSharedIC) {
         masm.push(Imm32(0));
         return;
@@ -700,17 +693,17 @@ SharedStubInfo::SharedStubInfo(JSContext
         // outerScript_ is initialized lazily.
     }
 }
 
 HandleScript
 SharedStubInfo::outerScript(JSContext* cx)
 {
     if (!outerScript_) {
-        js::jit::JitActivationIterator iter(cx->runtime());
+        js::jit::JitActivationIterator iter(cx);
         JitFrameIterator it(iter);
         MOZ_ASSERT(it.isExitFrame());
         ++it;
         MOZ_ASSERT(it.isIonJS());
         outerScript_ = it.script();
         MOZ_ASSERT(!it.ionScript()->invalidated());
     }
     return outerScript_;
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1123,17 +1123,17 @@ StringReplace(JSContext* cx, HandleStrin
 
     return str_replace_string_raw(cx, string, pattern, repl);
 }
 
 bool
 RecompileImpl(JSContext* cx, bool force)
 {
     MOZ_ASSERT(cx->currentlyRunningInJit());
-    JitActivationIterator activations(cx->runtime());
+    JitActivationIterator activations(cx);
     JitFrameIterator iter(activations);
 
     MOZ_ASSERT(iter.type() == JitFrame_Exit);
     ++iter;
 
     RootedScript script(cx, iter.script());
     MOZ_ASSERT(script->hasIonScript());
 
--- a/js/src/jit/arm/SharedICHelpers-arm.h
+++ b/js/src/jit/arm/SharedICHelpers-arm.h
@@ -195,30 +195,16 @@ EmitBaselineEnterStubFrame(MacroAssemble
     masm.Push(BaselineFrameReg);
     masm.mov(BaselineStackReg, BaselineFrameReg);
 
     // We pushed 4 words, so the stack is still aligned to 8 bytes.
     masm.checkStackAlignment();
 }
 
 inline void
-EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
-{
-    MOZ_ASSERT(ICTailCallReg == lr);
-
-    // In arm the link register contains the return address,
-    // but in jit frames we expect it to be on the stack. As a result
-    // push the link register (which is actually part of the previous frame.
-    // Therefore using push instead of Push).
-    masm.push(ICTailCallReg);
-
-    masm.Push(ICStubReg);
-}
-
-inline void
 EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     ScratchRegisterScope scratch(masm);
 
     // Ion frames do not save and restore the frame pointer. If we called into
     // Ion, we have to restore the stack pointer from the frame descriptor. If
     // we performed a VM call, the descriptor has been popped already so in that
     // case we use the frame pointer.
@@ -236,23 +222,16 @@ EmitBaselineLeaveStubFrame(MacroAssemble
     // Load the return address.
     masm.Pop(ICTailCallReg);
 
     // Discard the frame descriptor.
     masm.Pop(scratch);
 }
 
 inline void
-EmitIonLeaveStubFrame(MacroAssembler& masm)
-{
-    masm.Pop(ICStubReg);
-    masm.pop(ICTailCallReg); // See EmitIonEnterStubFrame for explanation on pop/Pop.
-}
-
-inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
     MOZ_ASSERT(values >= 0 && values <= 2);
     switch(values) {
       case 1:
         // Stow R0.
         masm.Push(R0);
         break;
--- a/js/src/jit/arm64/SharedICHelpers-arm64.h
+++ b/js/src/jit/arm64/SharedICHelpers-arm64.h
@@ -168,22 +168,16 @@ EmitBaselineEnterStubFrame(MacroAssemble
     // Update the frame register.
     masm.Mov(BaselineFrameReg64, masm.GetStackPointer64());
 
     // Stack should remain 16-byte aligned.
     masm.checkStackAlignment();
 }
 
 inline void
-EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
-{
-    MOZ_CRASH("Not implemented yet.");
-}
-
-inline void
 EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     vixl::UseScratchRegisterScope temps(&masm.asVIXL());
     const ARMRegister scratch64 = temps.AcquireX();
 
     // Ion frames do not save and restore the frame pointer. If we called
     // into Ion, we have to restore the stack pointer from the frame descriptor.
     // If we performed a VM call, the descriptor has been popped already so
@@ -199,22 +193,16 @@ EmitBaselineLeaveStubFrame(MacroAssemble
     // Pop values, discarding the frame descriptor.
     masm.pop(BaselineFrameReg, ICStubReg, ICTailCallReg, scratch64.asUnsized());
 
     // Stack should remain 16-byte aligned.
     masm.checkStackAlignment();
 }
 
 inline void
-EmitIonLeaveStubFrame(MacroAssembler& masm)
-{
-    MOZ_CRASH("Not implemented yet.");
-}
-
-inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
     switch (values) {
       case 1:
         // Stow R0.
         masm.Push(R0);
         break;
       case 2:
--- a/js/src/jit/mips-shared/SharedICHelpers-mips-shared.h
+++ b/js/src/jit/mips-shared/SharedICHelpers-mips-shared.h
@@ -198,30 +198,16 @@ EmitBaselineEnterStubFrame(MacroAssemble
                                             offsetof(BaselineStubFrame, savedFrame)));
     masm.movePtr(BaselineStackReg, BaselineFrameReg);
 
     // Stack should remain aligned.
     masm.assertStackAlignment(sizeof(Value), 0);
 }
 
 inline void
-EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
-{
-    MOZ_ASSERT(ICTailCallReg == ra);
-
-    // In MIPS the ra register contains the return address,
-    // but in jit frames we expect it to be on the stack. As a result
-    // push the link register (which is actually part of the previous frame.
-    // Therefore using push instead of Push).
-    masm.push(ICTailCallReg);
-
-    masm.Push(ICStubReg);
-}
-
-inline void
 EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     // Ion frames do not save and restore the frame pointer. If we called
     // into Ion, we have to restore the stack pointer from the frame descriptor.
     // If we performed a VM call, the descriptor has been popped already so
     // in that case we use the frame pointer.
     if (calledIntoIon) {
         masm.pop(ScratchRegister);
@@ -241,23 +227,16 @@ EmitBaselineLeaveStubFrame(MacroAssemble
                  ICTailCallReg);
 
     // Discard the frame descriptor.
     masm.loadPtr(Address(StackPointer, offsetof(BaselineStubFrame, descriptor)), ScratchRegister);
     masm.addPtr(Imm32(STUB_FRAME_SIZE), StackPointer);
 }
 
 inline void
-EmitIonLeaveStubFrame(MacroAssembler& masm)
-{
-    masm.Pop(ICStubReg);
-    masm.pop(ICTailCallReg); // See EmitIonEnterStubFrame for explanation on pop/Pop.
-}
-
-inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
     MOZ_ASSERT(values >= 0 && values <= 2);
     switch(values) {
       case 1:
         // Stow R0
         masm.Push(R0);
         break;
--- a/js/src/jit/none/SharedICHelpers-none.h
+++ b/js/src/jit/none/SharedICHelpers-none.h
@@ -21,19 +21,17 @@ inline void EmitEnterTypeMonitorIC(Macro
 inline void EmitReturnFromIC(MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitChangeICReturnAddress(MacroAssembler&, Register) { MOZ_CRASH(); }
 inline void EmitBaselineTailCallVM(JitCode*, MacroAssembler&, uint32_t) { MOZ_CRASH(); }
 inline void EmitIonTailCallVM(JitCode*, MacroAssembler&, uint32_t) { MOZ_CRASH(); }
 inline void EmitBaselineCreateStubFrameDescriptor(MacroAssembler&, Register, uint32_t) { MOZ_CRASH(); }
 inline void EmitBaselineCallVM(JitCode*, MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitIonCallVM(JitCode*, size_t, MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitBaselineEnterStubFrame(MacroAssembler&, Register) { MOZ_CRASH(); }
-inline void EmitIonEnterStubFrame(MacroAssembler&, Register) { MOZ_CRASH(); }
 inline void EmitBaselineLeaveStubFrame(MacroAssembler&, bool v = false) { MOZ_CRASH(); }
-inline void EmitIonLeaveStubFrame(MacroAssembler&) { MOZ_CRASH(); }
 inline void EmitStowICValues(MacroAssembler&, int) { MOZ_CRASH(); }
 inline void EmitUnstowICValues(MacroAssembler&, int, bool v = false) { MOZ_CRASH(); }
 inline void EmitCallTypeUpdateIC(MacroAssembler&, JitCode*, uint32_t) { MOZ_CRASH(); }
 inline void EmitStubGuardFailure(MacroAssembler&) { MOZ_CRASH(); }
 
 template <typename T> inline void EmitPreBarrier(MacroAssembler&, T, MIRType) { MOZ_CRASH(); }
 
 } // namespace jit
--- a/js/src/jit/x64/SharedICHelpers-x64.h
+++ b/js/src/jit/x64/SharedICHelpers-x64.h
@@ -157,49 +157,54 @@ EmitIonCallVM(JitCode* target, size_t st
 
 // Size of vales pushed by EmitEnterStubFrame.
 static const uint32_t STUB_FRAME_SIZE = 4 * sizeof(void*);
 static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = sizeof(void*);
 
 inline void
 EmitBaselineEnterStubFrame(MacroAssembler& masm, Register)
 {
-    EmitRestoreTailCallReg(masm);
-
     ScratchRegisterScope scratch(masm);
 
-    // Compute frame size.
+    // Compute frame size. Because the return address is still on the stack,
+    // this is:
+    //
+    //   BaselineFrameReg
+    //   + BaselineFrame::FramePointerOffset
+    //   - BaselineStackReg
+    //   - sizeof(return address)
+    //
+    // The two constants cancel each other out, so we can just calculate
+    // BaselineFrameReg - BaselineStackReg.
+
+    static_assert(BaselineFrame::FramePointerOffset == sizeof(void*),
+                  "FramePointerOffset must be the same as the return address size");
+
     masm.movq(BaselineFrameReg, scratch);
-    masm.addq(Imm32(BaselineFrame::FramePointerOffset), scratch);
     masm.subq(BaselineStackReg, scratch);
 
     masm.store32(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
 
     // Note: when making changes here,  don't forget to update STUB_FRAME_SIZE
     // if needed.
 
-    // Push frame descriptor and return address.
+    // Push the return address that's currently on top of the stack.
+    masm.Push(Operand(BaselineStackReg, 0));
+
+    // Replace the original return address with the frame descriptor.
     masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS, BaselineStubFrameLayout::Size());
-    masm.Push(scratch);
-    masm.Push(ICTailCallReg);
+    masm.storePtr(scratch, Address(BaselineStackReg, sizeof(uintptr_t)));
 
     // Save old frame pointer, stack pointer and stub reg.
     masm.Push(ICStubReg);
     masm.Push(BaselineFrameReg);
     masm.mov(BaselineStackReg, BaselineFrameReg);
 }
 
 inline void
-EmitIonEnterStubFrame(MacroAssembler& masm, Register)
-{
-    masm.loadPtr(Address(masm.getStackPointer(), 0), ICTailCallReg);
-    masm.Push(ICStubReg);
-}
-
-inline void
 EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     // Ion frames do not save and restore the frame pointer. If we called
     // into Ion, we have to restore the stack pointer from the frame descriptor.
     // If we performed a VM call, the descriptor has been popped already so
     // in that case we use the frame pointer.
     if (calledIntoIon) {
         ScratchRegisterScope scratch(masm);
@@ -208,28 +213,21 @@ EmitBaselineLeaveStubFrame(MacroAssemble
         masm.addq(scratch, BaselineStackReg);
     } else {
         masm.mov(BaselineFrameReg, BaselineStackReg);
     }
 
     masm.Pop(BaselineFrameReg);
     masm.Pop(ICStubReg);
 
-    // Pop return address.
-    masm.Pop(ICTailCallReg);
-
-    // Overwrite frame descriptor with return address, so that the stack matches
-    // the state before entering the stub frame.
-    masm.storePtr(ICTailCallReg, Address(BaselineStackReg, 0));
-}
-
-inline void
-EmitIonLeaveStubFrame(MacroAssembler& masm)
-{
-    masm.Pop(ICStubReg);
+    // The return address is on top of the stack, followed by the frame
+    // descriptor. Use a pop instruction to overwrite the frame descriptor
+    // with the return address. Note that pop increments the stack pointer
+    // before computing the address.
+    masm.Pop(Operand(BaselineStackReg, 0));
 }
 
 inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
     MOZ_ASSERT(values >= 0 && values <= 2);
     switch(values) {
       case 1:
--- a/js/src/jit/x86/SharedICHelpers-x86.h
+++ b/js/src/jit/x86/SharedICHelpers-x86.h
@@ -155,81 +155,75 @@ EmitIonCallVM(JitCode* target, size_t st
 
 // Size of vales pushed by EmitEnterStubFrame.
 static const uint32_t STUB_FRAME_SIZE = 4 * sizeof(void*);
 static const uint32_t STUB_FRAME_SAVED_STUB_OFFSET = sizeof(void*);
 
 inline void
 EmitBaselineEnterStubFrame(MacroAssembler& masm, Register scratch)
 {
-    MOZ_ASSERT(scratch != ICTailCallReg);
-
-    EmitRestoreTailCallReg(masm);
+    // Compute frame size. Because the return address is still on the stack,
+    // this is:
+    //
+    //   BaselineFrameReg
+    //   + BaselineFrame::FramePointerOffset
+    //   - BaselineStackReg
+    //   - sizeof(return address)
+    //
+    // The two constants cancel each other out, so we can just calculate
+    // BaselineFrameReg - BaselineStackReg.
 
-    // Compute frame size.
+    static_assert(BaselineFrame::FramePointerOffset == sizeof(void*),
+                  "FramePointerOffset must be the same as the return address size");
+
     masm.movl(BaselineFrameReg, scratch);
-    masm.addl(Imm32(BaselineFrame::FramePointerOffset), scratch);
     masm.subl(BaselineStackReg, scratch);
 
     masm.store32(scratch, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
 
     // Note: when making changes here,  don't forget to update STUB_FRAME_SIZE
     // if needed.
 
-    // Push frame descriptor and return address.
+    // Push the return address that's currently on top of the stack.
+    masm.Push(Operand(BaselineStackReg, 0));
+
+    // Replace the original return address with the frame descriptor.
     masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS, BaselineStubFrameLayout::Size());
-    masm.Push(scratch);
-    masm.Push(ICTailCallReg);
+    masm.storePtr(scratch, Address(BaselineStackReg, sizeof(uintptr_t)));
 
     // Save old frame pointer, stack pointer and stub reg.
     masm.Push(ICStubReg);
     masm.Push(BaselineFrameReg);
     masm.mov(BaselineStackReg, BaselineFrameReg);
 }
 
 inline void
-EmitIonEnterStubFrame(MacroAssembler& masm, Register scratch)
-{
-    MOZ_ASSERT(scratch != ICTailCallReg);
-
-    masm.loadPtr(Address(masm.getStackPointer(), 0), ICTailCallReg);
-    masm.Push(ICStubReg);
-}
-
-inline void
 EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
 {
     // Ion frames do not save and restore the frame pointer. If we called
     // into Ion, we have to restore the stack pointer from the frame descriptor.
     // If we performed a VM call, the descriptor has been popped already so
     // in that case we use the frame pointer.
     if (calledIntoIon) {
-        Register scratch = ICTailCallReg;
+        Register scratch = ICStubReg;
         masm.Pop(scratch);
         masm.shrl(Imm32(FRAMESIZE_SHIFT), scratch);
         masm.addl(scratch, BaselineStackReg);
     } else {
         masm.mov(BaselineFrameReg, BaselineStackReg);
     }
 
     masm.Pop(BaselineFrameReg);
     masm.Pop(ICStubReg);
 
-    // Pop return address.
-    masm.Pop(ICTailCallReg);
-
-    // Overwrite frame descriptor with return address, so that the stack matches
-    // the state before entering the stub frame.
-    masm.storePtr(ICTailCallReg, Address(BaselineStackReg, 0));
-}
-
-inline void
-EmitIonLeaveStubFrame(MacroAssembler& masm)
-{
-    masm.Pop(ICStubReg);
+    // The return address is on top of the stack, followed by the frame
+    // descriptor. Use a pop instruction to overwrite the frame descriptor
+    // with the return address. Note that pop increments the stack pointer
+    // before computing the address.
+    masm.Pop(Operand(BaselineStackReg, 0));
 }
 
 inline void
 EmitStowICValues(MacroAssembler& masm, int values)
 {
     MOZ_ASSERT(values >= 0 && values <= 2);
     switch(values) {
       case 1:
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -265,17 +265,16 @@ MSG_DEF(JSMSG_ILLEGAL_CHARACTER,       0
 MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level of a module")
 MSG_DEF(JSMSG_OF_AFTER_FOR_LOOP_DECL,  0, JSEXN_SYNTAXERR, "a declaration in the head of a for-of loop can't have an initializer")
 MSG_DEF(JSMSG_IN_AFTER_LEXICAL_FOR_DECL,0,JSEXN_SYNTAXERR, "a lexical declaration in the head of a for-in loop can't have an initializer")
 MSG_DEF(JSMSG_INVALID_FOR_IN_DECL_WITH_INIT,0,JSEXN_SYNTAXERR,"for-in loop head declarations may not have initializers")
 MSG_DEF(JSMSG_LABEL_NOT_FOUND,         0, JSEXN_SYNTAXERR, "label not found")
 MSG_DEF(JSMSG_LET_COMP_BINDING,        0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
 MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,   1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
 MSG_DEF(JSMSG_LEXICAL_DECL_LABEL,      1, JSEXN_SYNTAXERR, "{0} declarations cannot be labelled")
-MSG_DEF(JSMSG_LINE_BREAK_AFTER_AWAIT,  0, JSEXN_SYNTAXERR, "no line break is allowed after 'await'")
 MSG_DEF(JSMSG_GENERATOR_LABEL,         0, JSEXN_SYNTAXERR, "generator functions cannot be labelled")
 MSG_DEF(JSMSG_FUNCTION_LABEL,          0, JSEXN_SYNTAXERR, "functions cannot be labelled")
 MSG_DEF(JSMSG_SLOPPY_FUNCTION_LABEL,   0, JSEXN_SYNTAXERR, "functions can only be labelled inside blocks")
 MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW,  0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
 MSG_DEF(JSMSG_LINE_BREAK_BEFORE_ARROW, 0, JSEXN_SYNTAXERR, "no line break is allowed before '=>'")
 MSG_DEF(JSMSG_MALFORMED_ESCAPE,        1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
 MSG_DEF(JSMSG_MISSING_BINARY_DIGITS,   0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
 MSG_DEF(JSMSG_MISSING_EXPONENT,        0, JSEXN_SYNTAXERR, "missing exponent")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6626,17 +6626,17 @@ DescribeScriptedCaller(JSContext* cx, Au
 // 'outer' frames. That's fine: each activation is tied to a single compartment,
 // so if an activation contains at least one non-self-hosted frame, we can use
 // the activation's global for GetScriptedCallerGlobal. If, however, all 'outer'
 // frames are self-hosted, it's possible Ion inlined a non-self-hosted script,
 // so we must return false and use the slower path.
 static bool
 GetScriptedCallerActivationFast(JSContext* cx, Activation** activation)
 {
-    ActivationIterator activationIter(cx->runtime());
+    ActivationIterator activationIter(cx);
 
     if (activationIter.done()) {
         *activation = nullptr;
         return true;
     }
 
     *activation = activationIter.activation();
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -201,77 +201,77 @@ ToId(JSContext* cx, double index, Mutabl
 {
     if (index == uint32_t(index))
         return IndexToId(cx, uint32_t(index), id);
 
     Value tmp = DoubleValue(index);
     return ValueToId<CanGC>(cx, HandleValue::fromMarkedLocation(&tmp), id);
 }
 
+/*
+ * If the property at the given index exists, get its value into |vp| and set
+ * |*hole| to false. Otherwise set |*hole| to true and |vp| to Undefined.
+ */
 static bool
-ToId(JSContext* cx, uint32_t index, MutableHandleId id)
+GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index, bool* hole,
+           MutableHandleValue vp)
 {
-    return IndexToId(cx, index, id);
-}
-
-/*
- * If the property at the given index exists, get its value into location
- * pointed by vp and set *hole to false. Otherwise set *hole to true and *vp
- * to JSVAL_VOID. This function assumes that the location pointed by vp is
- * properly rooted and can be used as GC-protected storage for temporaries.
- */
-static inline bool
-DoGetElement(JSContext* cx, HandleObject obj, HandleObject receiver,
-             uint32_t index, bool* hole, MutableHandleValue vp)
-{
+    if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
+        vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, index));
+        if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
+            *hole = false;
+            return true;
+        }
+    }
+    if (obj->is<ArgumentsObject>()) {
+        if (obj->as<ArgumentsObject>().maybeGetElement(index, vp)) {
+            *hole = false;
+            return true;
+        }
+    }
+
     RootedId id(cx);
-    if (!ToId(cx, index, &id))
+    if (!IndexToId(cx, index, &id))
         return false;
 
     bool found;
     if (!HasProperty(cx, obj, id, &found))
         return false;
 
     if (found) {
         if (!GetProperty(cx, obj, receiver, id, vp))
             return false;
     } else {
         vp.setUndefined();
     }
     *hole = !found;
     return true;
 }
 
-static bool
-GetElement(JSContext* cx, HandleObject obj, HandleObject receiver,
-           uint32_t index, bool* hole, MutableHandleValue vp)
-{
-    if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
-        vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, uint32_t(index)));
-        if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
-            *hole = false;
-            return true;
-        }
-    }
-    if (obj->is<ArgumentsObject>()) {
-        if (obj->as<ArgumentsObject>().maybeGetElement(uint32_t(index), vp)) {
-            *hole = false;
-            return true;
-        }
-    }
-
-    return DoGetElement(cx, obj, receiver, index, hole, vp);
-}
-
 static inline bool
 GetElement(JSContext* cx, HandleObject obj, uint32_t index, bool* hole, MutableHandleValue vp)
 {
     return GetElement(cx, obj, obj, index, hole, vp);
 }
 
+static bool
+GetElement(JSContext* cx, HandleObject obj, uint32_t index, MutableHandleValue vp)
+{
+    if (index < GetAnyBoxedOrUnboxedInitializedLength(obj)) {
+        vp.set(GetAnyBoxedOrUnboxedDenseElement(obj, index));
+        if (!vp.isMagic(JS_ELEMENTS_HOLE))
+            return true;
+    }
+    if (obj->is<ArgumentsObject>()) {
+        if (obj->as<ArgumentsObject>().maybeGetElement(index, vp))
+            return true;
+    }
+    return GetElement(cx, obj, obj, index, vp);
+}
+
 bool
 ElementAdder::append(JSContext* cx, HandleValue v)
 {
     MOZ_ASSERT(index_ < length_);
     if (resObj_) {
         DenseElementResult result =
             SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, resObj_, index_, v.address(), 1);
         if (result == DenseElementResult::Failure)
@@ -1055,18 +1055,20 @@ ArrayJoinDenseKernel(JSContext* cx, Sepa
     // length > initLength we rely on the second loop to add the
     // other elements.
     MOZ_ASSERT(*numProcessed == 0);
     uint32_t initLength = Min<uint32_t>(GetBoxedOrUnboxedInitializedLength<Type>(obj), length);
     while (*numProcessed < initLength) {
         if (!CheckForInterrupt(cx))
             return DenseElementResult::Failure;
 
+        // Step 7.b.
         const Value& elem = GetBoxedOrUnboxedDenseElement<Type>(obj, *numProcessed);
 
+        // Steps 7.c-d.
         if (elem.isString()) {
             if (!sb.append(elem.toString()))
                 return DenseElementResult::Failure;
         } else if (elem.isNumber()) {
             if (!NumberValueToStringBuffer(cx, elem, sb))
                 return DenseElementResult::Failure;
         } else if (elem.isBoolean()) {
             if (!BooleanToStringBuffer(elem.toBoolean(), sb))
@@ -1080,16 +1082,17 @@ ArrayJoinDenseKernel(JSContext* cx, Sepa
              * Symbol stringifying is a TypeError, so into the slow path
              * with those as well.
              */
             break;
         } else {
             MOZ_ASSERT(elem.isMagic(JS_ELEMENTS_HOLE) || elem.isNullOrUndefined());
         }
 
+        // Steps 7.a, 7.e.
         if (++(*numProcessed) != length && !sepOp(cx, sb))
             return DenseElementResult::Failure;
     }
 
     return DenseElementResult::Incomplete;
 }
 
 template <typename SeparatorOp>
@@ -1112,118 +1115,123 @@ struct ArrayJoinDenseKernelFunctor {
     }
 };
 
 template <typename SeparatorOp>
 static bool
 ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
                StringBuffer& sb)
 {
+    // Step 6.
     uint32_t i = 0;
 
     if (!ObjectMayHaveExtraIndexedProperties(obj)) {
         ArrayJoinDenseKernelFunctor<SeparatorOp> functor(cx, sepOp, obj, length, sb, &i);
         DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
         if (result == DenseElementResult::Failure)
             return false;
     }
 
+    // Step 7.
     if (i != length) {
         RootedValue v(cx);
         while (i < length) {
             if (!CheckForInterrupt(cx))
                 return false;
 
-            bool hole;
-            if (!GetElement(cx, obj, i, &hole, &v))
+            // Step 7.b.
+            if (!GetElement(cx, obj, i, &v))
                 return false;
-            if (!hole && !v.isNullOrUndefined()) {
+
+            // Steps 7.c-d.
+            if (!v.isNullOrUndefined()) {
                 if (!ValueToStringBuffer(cx, v, sb))
                     return false;
             }
 
+            // Steps 7.a, 7.e.
             if (++i != length && !sepOp(cx, sb))
                 return false;
         }
     }
 
     return true;
 }
 
-/* ES5 15.4.4.5 */
+// ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
+// 22.1.3.13 Array.prototype.join ( separator )
 bool
 js::array_join(JSContext* cx, unsigned argc, Value* vp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.join");
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    // Step 1
+    // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     AutoCycleDetector detector(cx, obj);
     if (!detector.init())
         return false;
 
     if (detector.foundCycle()) {
         args.rval().setString(cx->names().empty);
         return true;
     }
 
-    // Steps 2 and 3
+    // Step 2.
     uint32_t length;
     if (!GetLengthProperty(cx, obj, &length))
         return false;
 
-    // Steps 4 and 5
+    // Steps 3-4.
     RootedLinearString sepstr(cx);
     if (args.hasDefined(0)) {
         JSString *s = ToString<CanGC>(cx, args[0]);
         if (!s)
             return false;
         sepstr = s->ensureLinear(cx);
         if (!sepstr)
             return false;
     } else {
         sepstr = cx->names().comma;
     }
 
-    // Step 6 is implicit in the loops below.
-
-    // An optimized version of a special case of steps 7-11: when length==1 and
+    // An optimized version of a special case of steps 5-8: when length==1 and
     // the 0th element is a string, ToString() of that element is a no-op and
     // so it can be immediately returned as the result.
     if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
         Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0);
         if (elem0.isString()) {
             args.rval().set(elem0);
             return true;
         }
     }
 
+    // Step 5.
     StringBuffer sb(cx);
     if (sepstr->hasTwoByteChars() && !sb.ensureTwoByteChars())
         return false;
 
     // The separator will be added |length - 1| times, reserve space for that
     // so that we don't have to unnecessarily grow the buffer.
     size_t seplen = sepstr->length();
     CheckedInt<uint32_t> res = CheckedInt<uint32_t>(seplen) * (length - 1);
     if (length > 0 && !res.isValid()) {
         ReportAllocationOverflow(cx);
         return false;
     }
 
     if (length > 0 && !sb.reserve(res.value()))
         return false;
 
-    // Various optimized versions of steps 7-10.
+    // Various optimized versions of steps 6-7.
     if (seplen == 0) {
         EmptySeparatorOp op;
         if (!ArrayJoinKernel(cx, op, obj, length, sb))
             return false;
     } else if (seplen == 1) {
         char16_t c = sepstr->latin1OrTwoByteChar(0);
         if (c <= JSString::MAX_LATIN1_CHAR) {
             CharSeparatorOp<Latin1Char> op(c);
@@ -1235,17 +1243,17 @@ js::array_join(JSContext* cx, unsigned a
                 return false;
         }
     } else {
         StringSeparatorOp op(sepstr);
         if (!ArrayJoinKernel(cx, op, obj, length, sb))
             return false;
     }
 
-    // Step 11
+    // Step 8.
     JSString *str = sb.finishString();
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
@@ -1402,25 +1410,30 @@ ArrayReverseDenseKernel(JSContext* cx, H
     }
 
     return DenseElementResult::Success;
 }
 
 DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel,
                              JSContext*, HandleObject, uint32_t);
 
+// ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
+// 22.1.3.21 Array.prototype.reverse ( )
 bool
 js::array_reverse(JSContext* cx, unsigned argc, Value* vp)
 {
     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.reverse");
     CallArgs args = CallArgsFromVp(argc, vp);
+
+    // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
+    // Step 2.
     uint32_t len;
     if (!GetLengthProperty(cx, obj, &len))
         return false;
 
     if (!ObjectMayHaveExtraIndexedProperties(obj)) {
         ArrayReverseDenseKernelFunctor functor(cx, obj, len);
         DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
         if (result != DenseElementResult::Incomplete) {
@@ -1429,16 +1442,17 @@ js::array_reverse(JSContext* cx, unsigne
              * array has trailing holes (and thus the original array began with
              * holes).
              */
             args.rval().setObject(*obj);
             return result == DenseElementResult::Success;
         }
     }
 
+    // Steps 3-5.
     RootedValue lowval(cx), hival(cx);
     for (uint32_t i = 0, half = len / 2; i < half; i++) {
         bool hole, hole2;
         if (!CheckForInterrupt(cx) ||
             !GetElement(cx, obj, i, &hole, &lowval) ||
             !GetElement(cx, obj, len - i - 1, &hole2, &hival))
         {
             return false;
@@ -1458,16 +1472,18 @@ js::array_reverse(JSContext* cx, unsigne
             if (!DeletePropertyOrThrow(cx, obj, i))
                 return false;
             if (!SetElement(cx, obj, len - i - 1, lowval))
                 return false;
         } else {
             // No action required.
         }
     }
+
+    // Step 6.
     args.rval().setObject(*obj);
     return true;
 }
 
 static inline bool
 CompareStringValues(JSContext* cx, const Value& a, const Value& b, bool* lessOrEqualp)
 {
     if (!CheckForInterrupt(cx))
@@ -2067,29 +2083,30 @@ js::NewbornArrayPush(JSContext* cx, Hand
         return false;
 
     arr->setDenseInitializedLength(length + 1);
     arr->setLengthInt32(length + 1);
     arr->initDenseElementWithType(cx, length, v);
     return true;
 }
 
-/* ES5 15.4.4.7 */
+// ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
+// 22.1.3.18 Array.prototype.push ( ...items )
 bool
 js::array_push(JSContext* cx, unsigned argc, Value* vp)
 {
     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.push");
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    /* Step 1. */
+    // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
-    /* Steps 2-3. */
+    // Step 2.
     uint32_t length;
     if (!GetLengthProperty(cx, obj, &length))
         return false;
 
     if (!ObjectMayHaveExtraIndexedProperties(obj)) {
         DenseElementResult result =
             SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, length,
                                                       args.array(), args.length());
@@ -2107,62 +2124,62 @@ js::array_push(JSContext* cx, unsigned a
                 MOZ_ASSERT(obj->is<NativeObject>());
                 return SetLengthProperty(cx, obj, newlength);
             }
 
             return true;
         }
     }
 
-    /* Steps 4-5. */
+    // Steps 3-6.
     if (!SetArrayElements(cx, obj, length, args.length(), args.array()))
         return false;
 
-    /* Steps 6-7. */
+    // Steps 7-8.
     double newlength = length + double(args.length());
     args.rval().setNumber(newlength);
     return SetLengthProperty(cx, obj, newlength);
 }
 
-/* ES6 20130308 draft 15.4.4.6. */
+// ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
+// 22.1.3.17 Array.prototype.pop ( )
 bool
 js::array_pop(JSContext* cx, unsigned argc, Value* vp)
 {
     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.pop");
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    /* Step 1. */
+    // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
-    /* Steps 2-3. */
+    // Step 2.
     uint32_t index;
     if (!GetLengthProperty(cx, obj, &index))
         return false;
 
-    /* Steps 4-5. */
+    // Steps 3-4.
     if (index == 0) {
-        /* Step 4b. */
+        // Step 3.b.
         args.rval().setUndefined();
     } else {
-        /* Step 5a. */
+        // Steps 4.a-b.
         index--;
 
-        /* Step 5b, 5e. */
-        bool hole;
-        if (!GetElement(cx, obj, index, &hole, args.rval()))
+        // Steps 4.c, 4.f.
+        if (!GetElement(cx, obj, index, args.rval()))
             return false;
 
-        /* Step 5c. */
-        if (!hole && !DeletePropertyOrThrow(cx, obj, index))
+        // Steps 4.d.
+        if (!DeletePropertyOrThrow(cx, obj, index))
             return false;
     }
 
-    /* Steps 4a, 5d. */
+    // Steps 3.a, 4.e.
     return SetLengthProperty(cx, obj, index);
 }
 
 template <JSValueType Type>
 static inline DenseElementResult
 ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
 {
     MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
@@ -2251,99 +2268,105 @@ ArrayShiftDenseKernel(JSContext* cx, Han
 
     SetBoxedOrUnboxedInitializedLength<Type>(cx, obj, initlen - 1);
     return DenseElementResult::Success;
 }
 
 DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel,
                              JSContext*, HandleObject, MutableHandleValue);
 
-/* ES5 15.4.4.9 */
+// ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
+// 22.1.3.22 Array.prototype.shift ( )
 bool
 js::array_shift(JSContext* cx, unsigned argc, Value* vp)
 {
     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.shift");
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    /* Step 1. */
+    // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
-    /* Steps 2-3. */
+    // Step 2.
     uint32_t len;
     if (!GetLengthProperty(cx, obj, &len))
         return false;
 
-    /* Step 4. */
+    // Step 3.
     if (len == 0) {
-        /* Step 4a. */
+        // Step 3.a.
         if (!SetLengthProperty(cx, obj, 0))
             return false;
 
-        /* Step 4b. */
+        // Step 3.b.
         args.rval().setUndefined();
         return true;
     }
 
     uint32_t newlen = len - 1;
 
     /* Fast paths. */
     ArrayShiftDenseKernelFunctor functor(cx, obj, args.rval());
     DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
     if (result != DenseElementResult::Incomplete) {
         if (result == DenseElementResult::Failure)
             return false;
 
         return SetLengthProperty(cx, obj, newlen);
     }
 
-    /* Steps 5, 10. */
-    bool hole;
-    if (!GetElement(cx, obj, 0, &hole, args.rval()))
+    // Steps 4, 9.
+    if (!GetElement(cx, obj, 0, args.rval()))
         return false;
 
-    /* Steps 6-7. */
+    // Steps 5-6.
     RootedValue value(cx);
     for (uint32_t i = 0; i < newlen; i++) {
         if (!CheckForInterrupt(cx))
             return false;
+        bool hole;
         if (!GetElement(cx, obj, i + 1, &hole, &value))
             return false;
         if (hole) {
             if (!DeletePropertyOrThrow(cx, obj, i))
                 return false;
         } else {
             if (!SetElement(cx, obj, i, value))
                 return false;
         }
     }
 
-    /* Step 8. */
+    // Step 7.
     if (!DeletePropertyOrThrow(cx, obj, newlen))
         return false;
 
-    /* Step 9. */
+    // Step 8.
     return SetLengthProperty(cx, obj, newlen);
 }
 
+// ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
+// 22.1.3.29 Array.prototype.unshift ( ...items )
 bool
 js::array_unshift(JSContext* cx, unsigned argc, Value* vp)
 {
     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.unshift");
     CallArgs args = CallArgsFromVp(argc, vp);
+
+    // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
+    // Step 2.
     uint32_t length;
     if (!GetLengthProperty(cx, obj, &length))
         return false;
 
-    double newlen = length;
+    // Steps 3-4.
     if (args.length() > 0) {
         /* Slide up the array to make room for all args at the bottom. */
         if (length > 0) {
             // Only include a fast path for boxed arrays. Unboxed arrays can't
             // be optimized here because unshifting temporarily places holes at
             // the start of the array.
             // TODO: Implement unboxed array optimization similar to the one in
             // array_splice_impl(), unshift() is a special version of splice():
@@ -2369,49 +2392,53 @@ js::array_unshift(JSContext* cx, unsigne
                     break;
                 }
                 aobj->moveDenseElements(args.length(), 0, length);
                 for (uint32_t i = 0; i < args.length(); i++)
                     aobj->setDenseElement(i, MagicValue(JS_ELEMENTS_HOLE));
                 optimized = true;
             } while (false);
 
+            // Steps 4.b-c.
             if (!optimized) {
                 uint32_t last = length;
                 double upperIndex = double(last) + args.length();
                 RootedValue value(cx);
                 do {
                     --last, --upperIndex;
-                    bool hole;
                     if (!CheckForInterrupt(cx))
                         return false;
+                    bool hole;
                     if (!GetElement(cx, obj, last, &hole, &value))
                         return false;
                     if (hole) {
                         if (!DeletePropertyOrThrow(cx, obj, upperIndex))
                             return false;
                     } else {
                         if (!SetElement(cx, obj, upperIndex, value))
                             return false;
                     }
                 } while (last != 0);
             }
         }
 
+        // Steps 4.d-f.
         /* Copy from args to the bottom of the array. */
         if (!SetArrayElements(cx, obj, 0, args.length(), args.array()))
             return false;
-
-        newlen += args.length();
     }
-    if (!SetLengthProperty(cx, obj, newlen))
+
+    // Step 5.
+    double newlength = length + double(args.length());
+    if (!SetLengthProperty(cx, obj, newlength))
         return false;
 
+    // Step 6.
     /* Follow Perl by returning the new array length. */
-    args.rval().setNumber(newlen);
+    args.rval().setNumber(newlength);
     return true;
 }
 
 /*
  * Returns true if this is a dense or unboxed array whose |count| properties
  * starting from |startingIndex| may be accessed (get, set, delete) directly
  * through its contiguous vector of elements without fear of getters, setters,
  * etc. along the prototype chain, or of enumerators requiring notification of
@@ -2453,38 +2480,38 @@ js::array_splice(JSContext* cx, unsigned
 {
     return array_splice_impl(cx, argc, vp, true);
 }
 
 static inline bool
 ArraySpliceCopy(JSContext* cx, HandleObject arr, HandleObject obj,
                 uint32_t actualStart, uint32_t actualDeleteCount)
 {
-    /* Steps 14, 15, 15.e. */
+    // Steps 10, 11, 11.d.
     RootedValue fromValue(cx);
     for (uint32_t k = 0; k < actualDeleteCount; k++) {
-        /* Step 15.a (implicit). */
+        // Step 11.a (implicit).
 
         if (!CheckForInterrupt(cx))
             return false;
 
-        /* Steps 15.b-c, 15.d.i-ii. */
+        // Steps 11.b, 11.c.i.
         bool hole;
         if (!GetElement(cx, obj, actualStart + k, &hole, &fromValue))
             return false;
 
-        /* Step 15.d. */
+        // Step 11.c.
         if (!hole) {
-            /* Step 15.d.iii-iv. */
+            // Step 11.c.ii.
             if (!DefineElement(cx, arr, k, fromValue))
                 return false;
         }
     }
 
-    /* Steps 16-17. */
+    // Step 12.
     return SetLengthProperty(cx, arr, actualDeleteCount);
 }
 
 bool
 js::array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
 {
     AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.splice");
     CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -46,16 +46,17 @@
 #include "jswatchpoint.h"
 
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "jit/PcScriptCache.h"
 #include "js/CharacterEncoding.h"
 #include "vm/HelperThreads.h"
 #include "vm/Shape.h"
+#include "wasm/WasmSignalHandlers.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
@@ -121,16 +122,19 @@ JSContext::init()
         return false;
 
 #ifdef JS_SIMULATOR
     simulator_ = js::jit::Simulator::Create(this);
     if (!simulator_)
         return false;
 #endif
 
+    if (!wasm::EnsureSignalHandlers(this))
+        return false;
+
     return true;
 }
 
 JSContext*
 js::NewContext(uint32_t maxBytes, uint32_t maxNurseryBytes, JSRuntime* parentRuntime)
 {
     AutoNoteSingleThreadedRegion anstr;
 
@@ -169,23 +173,16 @@ js::DestroyContext(JSContext* cx)
 
     if (cx->outstandingRequests != 0)
         MOZ_CRASH("Attempted to destroy a context while it is in a request.");
 
     cx->checkNoGCRooters();
 
     js_delete(cx->ionPcScriptCache.ref());
 
-    /*
-     * Dump remaining type inference results while we still have a context.
-     * This printing depends on atoms still existing.
-     */
-    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next())
-        PrintTypes(cx, c, false);
-
     cx->runtime()->destroyRuntime();
     js_delete(cx->runtime());
 
     js_delete_poison(cx);
 }
 
 void
 JS::RootingContext::checkNoGCRooters() {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -213,16 +213,22 @@ struct JSContext : public JS::RootingCon
 
     // Threads may freely access any data in their compartment and zone.
     JSCompartment* compartment() const {
         return compartment_;
     }
     JS::Zone* zone() const {
         MOZ_ASSERT_IF(!compartment(), !zone_);
         MOZ_ASSERT_IF(compartment(), js::GetCompartmentZone(compartment()) == zone_);
+        return zoneRaw();
+    }
+
+    // For use when the context's zone is being read by another thread and the
+    // compartment and zone pointers might not be in sync.
+    JS::Zone* zoneRaw() const {
         return zone_;
     }
 
     // For JIT use.
     static size_t offsetOfZone() {
         return offsetof(JSContext, zone_);
     }
 
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -498,17 +498,17 @@ JSContext::setCompartment(JSCompartment*
     // Both the current and the new compartment should be properly marked as
     // entered at this point.
     MOZ_ASSERT_IF(compartment_, compartment_->hasBeenEntered());
     MOZ_ASSERT_IF(comp, comp->hasBeenEntered());
 
     // This context must have exclusive access to the zone's group. There is an
     // exception, for now, for zones used by exclusive threads.
     MOZ_ASSERT_IF(comp && !comp->zone()->isAtomsZone() && !comp->zone()->usedByExclusiveThread,
-                  comp->zone()->group()->context == this);
+                  comp->zone()->group()->ownedByCurrentThread());
 
     compartment_ = comp;
     zone_ = comp ? comp->zone() : nullptr;
     arenas_ = zone_ ? &zone_->arenas : nullptr;
 }
 
 inline void
 JSContext::enterZoneGroup(js::ZoneGroup* group)
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -687,17 +687,17 @@ JSCompartment::traceRoots(JSTracer* trc,
     // the data alive for the JSScript::finalize call. Thus, we do not trace the
     // keys of the HashMap to avoid adding a strong reference to the JSScript
     // pointers.
     //
     // If the code coverage is either enabled with the --dump-bytecode command
     // line option, or with the PCCount JSFriend API functions, then we mark the
     // keys of the map to hold the JSScript alive.
     if (scriptCountsMap &&
-        zone()->group()->profilingScripts &&
+        trc->runtime()->profilingScripts &&
         !JS::CurrentThreadIsHeapMinorCollecting())
     {
         MOZ_ASSERT_IF(!trc->runtime()->isBeingDestroyed(), collectCoverage());
         for (ScriptCountsMap::Range r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
             JSScript* script = const_cast<JSScript*>(r.front().key());
             MOZ_ASSERT(script->hasScriptCounts());
             TraceRoot(trc, &script, "profilingScripts");
             MOZ_ASSERT(script == r.front().key(), "const_cast is only a work-around");
@@ -1166,17 +1166,19 @@ JSCompartment::updateDebuggerObservesCov
     bool previousState = debuggerObservesCoverage();
     updateDebuggerObservesFlag(DebuggerObservesCoverage);
     if (previousState == debuggerObservesCoverage())
         return;
 
     if (debuggerObservesCoverage()) {
         // Interrupt any running interpreter frame. The scriptCounts are
         // allocated on demand when a script resume its execution.
-        for (ActivationIterator iter(runtimeFromMainThread()); !iter.done(); ++iter) {
+        JSContext* cx = TlsContext.get();
+        MOZ_ASSERT(zone()->group()->ownedByCurrentThread());
+        for (ActivationIterator iter(cx); !iter.done(); ++iter) {
             if (iter->isInterpreter())
                 iter->asInterpreter()->enableInterruptsUnconditionally();
         }
         return;
     }
 
     // If code coverage is enabled by any other means, keep it.
     if (collectCoverage())
@@ -1197,17 +1199,17 @@ JSCompartment::collectCoverageForPGO() c
 {
     return !JitOptions.disablePgo;
 }
 
 bool
 JSCompartment::collectCoverageForDebug() const
 {
     return debuggerObservesCoverage() ||
-           zone()->group()->profilingScripts ||
+           runtimeFromAnyThread()->profilingScripts ||
            runtimeFromAnyThread()->lcovOutput().isEnabled();
 }
 
 void
 JSCompartment::clearScriptCounts()
 {
     if (!scriptCountsMap)
         return;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -238,16 +238,17 @@
 #include "vm/Time.h"
 #include "vm/TraceLogging.h"
 #include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "gc/Heap-inl.h"
+#include "vm/GeckoProfiler-inl.h"
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayLength;
 using mozilla::Get;
@@ -3791,17 +3792,17 @@ GCRuntime::beginMarkPhase(JS::gcreason::
         c->marked = false;
         c->scheduledForDestruction = false;
         c->maybeAlive = c->hasBeenEntered() || !c->zone()->isGCScheduled();
         if (shouldPreserveJITCode(c, currentTime, reason, canAllocateMoreCode))
             c->zone()->setPreservingCode(true);
     }
 
     if (!rt->gc.cleanUpEverything && canAllocateMoreCode) {
-        if (JSCompartment* comp = jit::TopmostIonActivationCompartment(rt))
+        if (JSCompartment* comp = jit::TopmostIonActivationCompartment(TlsContext.get()))
             comp->zone()->setPreservingCode(true);
     }
 
     /*
      * If keepAtoms() is true then either an instance of AutoKeepAtoms is
      * currently on the stack or parsing is currently happening on another
      * thread. In either case we don't have information about which atoms are
      * roots, so we must skip collecting atoms.
@@ -5512,17 +5513,17 @@ GCRuntime::compactPhase(JS::gcreason::Re
     MOZ_ASSERT(startedCompacting);
 
     gcstats::AutoPhase ap(stats(), gcstats::PHASE_COMPACT);
 
     // TODO: JSScripts can move. If the sampler interrupts the GC in the
     // middle of relocating an arena, invalid JSScript pointers may be
     // accessed. Suppress all sampling until a finer-grained solution can be
     // found. See bug 1295775.
-    AutoSuppressProfilerSampling suppressSampling(rt);
+    AutoSuppressProfilerSampling suppressSampling(TlsContext.get());
 
     ZoneList relocatedZones;
     Arena* relocatedArenas = nullptr;
     while (!zonesToMaybeCompact.ref().isEmpty()) {
 
         Zone* zone = zonesToMaybeCompact.ref().front();
         zonesToMaybeCompact.ref().removeFront();
 
@@ -5620,17 +5621,18 @@ HeapStateToLabel(JS::HeapState heapState
     return nullptr;
 }
 
 /* Start a new heap session. */
 AutoTraceSession::AutoTraceSession(JSRuntime* rt, JS::HeapState heapState)
   : lock(rt),
     runtime(rt),
     prevState(TlsContext.get()->heapState),
-    pseudoFrame(rt, HeapStateToLabel(heapState), ProfileEntry::Category::GC)
+    pseudoFrame(rt, HeapStateToLabel(heapState), ProfileEntry::Category::GC),
+    prohibitActiveContextChange(rt)
 {
     MOZ_ASSERT(prevState == JS::HeapState::Idle);
     MOZ_ASSERT(heapState != JS::HeapState::Idle);
     MOZ_ASSERT_IF(heapState == JS::HeapState::MajorCollecting, rt->zoneGroupFromMainThread()->nursery().isEmpty());
     TlsContext.get()->heapState = heapState;
 }
 
 AutoTraceSession::~AutoTraceSession()
@@ -6900,16 +6902,18 @@ void PreventGCDuringInteractiveDebug()
 }
 
 #endif
 
 void
 js::ReleaseAllJITCode(FreeOp* fop)
 {
     js::CancelOffThreadIonCompile(fop->runtime());
+
+    JSRuntime::AutoProhibitActiveContextChange apacc(fop->runtime());
     for (ZonesIter zone(fop->runtime(), SkipAtoms); !zone.done(); zone.next()) {
         zone->setPreservingCode(false);
         zone->discardJitCode(fop);
     }
 }
 
 void
 js::PurgeJITCaches(Zone* zone)
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1750,46 +1750,46 @@ js::IsValidBytecodeOffset(JSContext* cx,
  * StopPCCountProfiling     None      Query     Query
  * PurgePCCounts            None      None      None
  */
 
 static void
 ReleaseScriptCounts(FreeOp* fop)
 {
     JSRuntime* rt = fop->runtime();
-    MOZ_ASSERT(rt->zoneGroupFromMainThread()->scriptAndCountsVector);
+    MOZ_ASSERT(rt->scriptAndCountsVector);
 
-    fop->delete_(rt->zoneGroupFromMainThread()->scriptAndCountsVector.ref());
-    rt->zoneGroupFromMainThread()->scriptAndCountsVector = nullptr;
+    fop->delete_(rt->scriptAndCountsVector.ref());
+    rt->scriptAndCountsVector = nullptr;
 }
 
 JS_FRIEND_API(void)
 js::StartPCCountProfiling(JSContext* cx)
 {
     JSRuntime* rt = cx->runtime();
 
-    if (rt->zoneGroupFromMainThread()->profilingScripts)
+    if (rt->profilingScripts)
         return;
 
-    if (rt->zoneGroupFromMainThread()->scriptAndCountsVector)
+    if (rt->scriptAndCountsVector)
         ReleaseScriptCounts(rt->defaultFreeOp());
 
     ReleaseAllJITCode(rt->defaultFreeOp());
 
-    rt->zoneGroupFromMainThread()->profilingScripts = true;
+    rt->profilingScripts = true;
 }
 
 JS_FRIEND_API(void)
 js::StopPCCountProfiling(JSContext* cx)
 {
     JSRuntime* rt = cx->runtime();
 
-    if (!rt->zoneGroupFromMainThread()->profilingScripts)
+    if (!rt->profilingScripts)
         return;
-    MOZ_ASSERT(!rt->zoneGroupFromMainThread()->scriptAndCountsVector);
+    MOZ_ASSERT(!rt->scriptAndCountsVector);
 
     ReleaseAllJITCode(rt->defaultFreeOp());
 
     auto* vec = cx->new_<PersistentRooted<ScriptAndCountsVector>>(cx,
         ScriptAndCountsVector(SystemAllocPolicy()));
     if (!vec)
         return;
 
@@ -1797,41 +1797,41 @@ js::StopPCCountProfiling(JSContext* cx)
         for (auto script = zone->cellIter<JSScript>(); !script.done(); script.next()) {
             if (script->hasScriptCounts() && script->types()) {
                 if (!vec->append(script))
                     return;
             }
         }
     }
 
-    rt->zoneGroupFromMainThread()->profilingScripts = false;
-    rt->zoneGroupFromMainThread()->scriptAndCountsVector = vec;
+    rt->profilingScripts = false;
+    rt->scriptAndCountsVector = vec;
 }
 
 JS_FRIEND_API(void)
 js::PurgePCCounts(JSContext* cx)
 {
     JSRuntime* rt = cx->runtime();
 
-    if (!rt->zoneGroupFromMainThread()->scriptAndCountsVector)
+    if (!rt->scriptAndCountsVector)
         return;
-    MOZ_ASSERT(!rt->zoneGroupFromMainThread()->profilingScripts);
+    MOZ_ASSERT(!rt->profilingScripts);
 
     ReleaseScriptCounts(rt->defaultFreeOp());
 }
 
 JS_FRIEND_API(size_t)
 js::GetPCCountScriptCount(JSContext* cx)
 {
     JSRuntime* rt = cx->runtime();
 
-    if (!rt->zoneGroupFromMainThread()->scriptAndCountsVector)
+    if (!rt->scriptAndCountsVector)
         return 0;
 
-    return rt->zoneGroupFromMainThread()->scriptAndCountsVector->length();
+    return rt->scriptAndCountsVector->length();
 }
 
 enum MaybeComma {NO_COMMA, COMMA};
 
 static MOZ_MUST_USE bool
 AppendJSONProperty(StringBuffer& buf, const char* name, MaybeComma comma = COMMA)
 {
     if (comma && !buf.append(','))
@@ -1842,24 +1842,22 @@ AppendJSONProperty(StringBuffer& buf, co
            buf.append("\":", 2);
 }
 
 JS_FRIEND_API(JSString*)
 js::GetPCCountScriptSummary(JSContext* cx, size_t index)
 {
     JSRuntime* rt = cx->runtime();
 
-    if (!rt->zoneGroupFromMainThread()->scriptAndCountsVector ||
-        index >= rt->zoneGroupFromMainThread()->scriptAndCountsVector->length())
-    {
+    if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
         return nullptr;
     }
 
-    const ScriptAndCounts& sac = (*rt->zoneGroupFromMainThread()->scriptAndCountsVector)[index];
+    const ScriptAndCounts& sac = (*rt->scriptAndCountsVector)[index];
     RootedScript script(cx, sac.script);
 
     /*
      * OOM on buffer appends here will not be caught immediately, but since
      * StringBuffer uses a TempAllocPolicy will trigger an exception on the
      * context if they occur, which we'll catch before returning.
      */
     StringBuffer buf(cx);
@@ -2128,24 +2126,22 @@ GetPCCountJSON(JSContext* cx, const Scri
     return true;
 }
 
 JS_FRIEND_API(JSString*)
 js::GetPCCountScriptContents(JSContext* cx, size_t index)
 {
     JSRuntime* rt = cx->runtime();
 
-    if (!rt->zoneGroupFromMainThread()->scriptAndCountsVector ||
-        index >= rt->zoneGroupFromMainThread()->scriptAndCountsVector->length())
-    {
+    if (!rt->scriptAndCountsVector || index >= rt->scriptAndCountsVector->length()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BUFFER_TOO_SMALL);
         return nullptr;
     }
 
-    const ScriptAndCounts& sac = (*rt->zoneGroupFromMainThread()->scriptAndCountsVector)[index];
+    const ScriptAndCounts& sac = (*rt->scriptAndCountsVector)[index];
     JSScript* script = sac.script;
 
     StringBuffer buf(cx);
 
     {
         AutoCompartment ac(cx, &script->global());
         if (!GetPCCountJSON(cx, sac, buf))
             return nullptr;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1101,17 +1101,17 @@ JSScript::initScriptCounts(JSContext* cx
     }
 
     // safe to set this;  we can't fail after this point.
     hasScriptCounts_ = true;
     guardScriptCounts.release();
 
     // Enable interrupts in any interpreter frames running on this script. This
     // is used to let the interpreter increment the PCCounts, if present.
-    for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
+    for (ActivationIterator iter(cx); !iter.done(); ++iter) {
         if (iter->isInterpreter())
             iter->asInterpreter()->enableInterruptsIfRunning(this);
     }
 
     return true;
 }
 
 static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript* script)
@@ -3618,17 +3618,17 @@ JSScript::ensureHasDebugScript(JSContext
     }
     hasDebugScript_ = true; // safe to set this;  we can't fail after this point
 
     /*
      * Ensure that any Interpret() instances running on this script have
      * interrupts enabled. The interrupts must stay enabled until the
      * debug state is destroyed.
      */
-    for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
+    for (ActivationIterator iter(cx); !iter.done(); ++iter) {
         if (iter->isInterpreter())
             iter->asInterpreter()->enableInterruptsIfRunning(this);
     }
 
     return true;
 }
 
 void
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -587,28 +587,24 @@ elif CONFIG['JS_CODEGEN_MIPS32'] or CONF
         ]
         if CONFIG['JS_SIMULATOR_MIPS64']:
             UNIFIED_SOURCES += [
                 'jit/mips64/Simulator-mips64.cpp'
             ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     SOURCES += [
-        'threading/windows/ConditionVariable.cpp',
         'threading/windows/CpuCount.cpp',
-        'threading/windows/MutexImpl.cpp',
         'threading/windows/Thread.cpp',
     ]
     # _CRT_RAND_S must be #defined before #including stdlib.h to get rand_s()
     DEFINES['_CRT_RAND_S'] = True
 else:
     SOURCES += [
-        'threading/posix/ConditionVariable.cpp',
         'threading/posix/CpuCount.cpp',
-        'threading/posix/MutexImpl.cpp',
         'threading/posix/Thread.cpp',
     ]
 
 if CONFIG['JS_HAS_CTYPES']:
     SOURCES += [
         'ctypes/CTypes.cpp',
         'ctypes/Library.cpp',
     ]
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -7675,17 +7675,17 @@ SetContextOptions(JSContext* cx, const O
         jit::Simulator::StopSimAt = stopAt;
 #endif
 
     reportWarnings = op.getBoolOption('w');
     compileOnly = op.getBoolOption('c');
     printTiming = op.getBoolOption('b');
     enableCodeCoverage = op.getBoolOption("code-coverage");
     enableDisassemblyDumps = op.getBoolOption('D');
-    cx->runtime()->zoneGroupFromMainThread()->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
+    cx->runtime()->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
 
     jsCacheDir = op.getStringOption("js-cache");
     if (jsCacheDir) {
         if (!op.getBoolOption("no-js-cache-per-process"))
             jsCacheDir = JS_smprintf("%s/%u", jsCacheDir, (unsigned)getpid());
         else
             jsCacheDir = JS_strdup(cx, jsCacheDir);
         if (!jsCacheDir)
@@ -7717,17 +7717,17 @@ SetWorkerContextOptions(JSContext* cx)
     JS::ContextOptionsRef(cx).setBaseline(enableBaseline)
                              .setIon(enableIon)
                              .setAsmJS(enableAsmJS)
                              .setWasm(enableWasm)
                              .setWasmAlwaysBaseline(enableWasmAlwaysBaseline)
                              .setNativeRegExp(enableNativeRegExp)
                              .setUnboxedArrays(enableUnboxedArrays);
     cx->runtime()->setOffthreadIonCompilationEnabled(offthreadCompilation);
-    cx->runtime()->zoneGroupFromMainThread()->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
+    cx->runtime()->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
 
 #ifdef JS_GC_ZEAL
     if (gZealBits && gZealFrequency) {
 #define ZEAL_MODE(_, value)                        \
         if (gZealBits & (1 << value))              \
             cx->runtime()->gc.setZeal(value, gZealFrequency);
         JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
 #undef ZEAL_MODE
@@ -7789,16 +7789,23 @@ Shell(JSContext* cx, OptionParser* op, c
             JS_free(cx, const_cast<char*>(jsCacheAsmJSPath));
         }
         if (jsCacheDir) {
             rmdir(jsCacheDir);
             JS_free(cx, const_cast<char*>(jsCacheDir));
         }
     }
 
+    /*
+     * Dump remaining type inference results while we still have a context.
+     * This printing depends on atoms still existing.
+     */
+    for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next())
+        PrintTypes(cx, c, false);
+
     return result;
 }
 
 static void
 SetOutputFile(const char* const envVar,
               RCFile* defaultOut,
               RCFile** outFileP)
 {
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_2017/AsyncFunctions/await-newline.js
@@ -0,0 +1,15 @@
+// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
+
+var BUGNUMBER = 1331009;
+var summary = "Newline is allowed between await and operand";
+
+print(BUGNUMBER + ": " + summary);
+
+var expr = async function foo() {
+    return await
+    10;
+};
+assertEventuallyEq(expr(), 10);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/ecma_2017/AsyncFunctions/syntax.js
+++ b/js/src/tests/ecma_2017/AsyncFunctions/syntax.js
@@ -28,19 +28,16 @@ if (typeof Reflect !== "undefined" && Re
     assertThrows(() => Reflect.parse("(async function await() {})"), SyntaxError);
 
     // Awaiting not directly inside an async function is not allowed
     assertThrows(() => Reflect.parse("await 4;"), SyntaxError);
     assertThrows(() => Reflect.parse("function a() { await 4; }"), SyntaxError);
     assertThrows(() => Reflect.parse("function* a() { await 4; }"), SyntaxError);
     assertThrows(() => Reflect.parse("async function k() { function a() { await 4; } }"), SyntaxError);
 
-    // No line terminator after await is allowed
-    assertThrows(() => Reflect.parse("async function a() { await\n4; }"), SyntaxError);
-
     // Await is not allowed as a default expr.
     assertThrows(() => Reflect.parse("async function a(k = await 3) {}"), SyntaxError);
     assertThrows(() => Reflect.parse("async function a() { async function b(k = await 3) {} }"), SyntaxError);
     assertThrows(() => Reflect.parse("async function a() { async function b(k = [await 3]) {} }"), SyntaxError);
 
     assertThrows(() => Reflect.parse("async function a() { async function b([k = await 3]) {} }"), SyntaxError);
     assertThrows(() => Reflect.parse("async function a() { async function b([k = [await 3]]) {} }"), SyntaxError);
     assertThrows(() => Reflect.parse("async function a() { async function b({k = await 3}) {} }"), SyntaxError);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/join-no-has-trap.js
@@ -0,0 +1,36 @@
+// Test that Array.prototype.join doesn't call the [[HasProperty]] internal
+// method of objects.
+
+var log = [];
+var array = [];
+var proxy = new Proxy(array, new Proxy({}, {
+    get(t, trap, r) {
+      return (t, pk, ...more) => {
+        log.push(`${trap}:${String(pk)}`);
+        return Reflect[trap](t, pk, ...more);
+      };
+    }
+}));
+
+var result;
+
+result = Array.prototype.join.call(proxy);
+assertEqArray(log, [ "get:length" ]);
+assertEq(result, "");
+
+log.length = 0;
+array.push(1);
+
+result = Array.prototype.join.call(proxy);
+assertEqArray(log, [ "get:length", "get:0" ]);
+assertEq(result, "1");
+
+log.length = 0;
+array.push(2);
+
+result = Array.prototype.join.call(proxy);
+assertEqArray(log, [ "get:length", "get:0", "get:1" ]);
+assertEq(result, "1,2");
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/pop-no-has-trap.js
@@ -0,0 +1,58 @@
+// Test that Array.prototype.pop doesn't call the [[HasProperty]] internal
+// method of objects when retrieving the element at the last index.
+
+var log = [];
+var array = [];
+var proxy = new Proxy(array, new Proxy({}, {
+    get(t, trap, r) {
+      return (t, pk, ...more) => {
+        log.push(`${trap}:${String(pk)}`);
+        return Reflect[trap](t, pk, ...more);
+      };
+    }
+}));
+
+var result;
+
+result = Array.prototype.pop.call(proxy);
+assertEqArray(log, [
+  "get:length",
+  "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length"
+]);
+assertEq(result, undefined);
+
+log.length = 0;
+array.push(1);
+
+result = Array.prototype.pop.call(proxy);
+assertEqArray(log, [
+  "get:length",
+  "get:0", "deleteProperty:0",
+  "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length"
+]);
+assertEq(result, 1);
+
+log.length = 0;
+array.push(2, 3);
+
+result = Array.prototype.pop.call(proxy);
+assertEqArray(log, [
+  "get:length",
+  "get:1", "deleteProperty:1",
+  "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length"
+]);
+assertEq(result, 3);
+
+log.length = 0;
+array.push(4, 5);
+
+result = Array.prototype.pop.call(proxy);
+assertEqArray(log, [
+  "get:length",
+  "get:2", "deleteProperty:2",
+  "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length"
+]);
+assertEq(result, 5);
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/shift-no-has-trap.js
@@ -0,0 +1,64 @@
+// Test that Array.prototype.shift doesn't call the [[HasProperty]] internal
+// method of objects when retrieving the element at index 0.
+
+var log = [];
+var array = [];
+var proxy = new Proxy(array, new Proxy({}, {
+    get(t, trap, r) {
+      return (t, pk, ...more) => {
+        log.push(`${trap}:${String(pk)}`);
+        return Reflect[trap](t, pk, ...more);
+      };
+    }
+}));
+
+var result;
+
+result = Array.prototype.shift.call(proxy);
+assertEqArray(log, [
+  "get:length",
+  "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length"
+]);
+assertEq(result, undefined);
+
+log.length = 0;
+array.push(1);
+
+result = Array.prototype.shift.call(proxy);
+assertEqArray(log, [
+  "get:length",
+  "get:0",
+  "deleteProperty:0",
+  "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length"
+]);
+assertEq(result, 1);
+
+log.length = 0;
+array.push(2, 3);
+
+result = Array.prototype.shift.call(proxy);
+assertEqArray(log, [
+  "get:length",
+  "get:0",
+  "has:1", "get:1", "set:0", "getOwnPropertyDescriptor:0", "defineProperty:0",
+  "deleteProperty:1",
+  "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length"
+]);
+assertEq(result, 2);
+
+log.length = 0;
+array.push(4, 5);
+
+result = Array.prototype.shift.call(proxy);
+assertEqArray(log, [
+  "get:length",
+  "get:0",
+  "has:1", "get:1", "set:0", "getOwnPropertyDescriptor:0", "defineProperty:0",
+  "has:2", "get:2", "set:1", "getOwnPropertyDescriptor:1", "defineProperty:1",
+  "deleteProperty:2",
+  "set:length", "getOwnPropertyDescriptor:length", "defineProperty:length"
+]);
+assertEq(result, 3);
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
--- a/js/src/threading/ConditionVariable.h
+++ b/js/src/threading/ConditionVariable.h
@@ -4,53 +4,60 @@
  * 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 threading_ConditionVariable_h
 #define threading_ConditionVariable_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
+#include "mozilla/PlatformConditionVariable.h"
 #include "mozilla/TimeStamp.h"
 
 #include <stdint.h>
 #ifndef XP_WIN
 # include <pthread.h>
 #endif
 
 #include "threading/LockGuard.h"
 #include "threading/Mutex.h"
 
 namespace js {
 
-template <typename T> using UniqueLock = LockGuard<T>;
-
 enum class CVStatus {
   NoTimeout,
   Timeout
 };
 
+template <typename T> using UniqueLock = LockGuard<T>;
+
 // A poly-fill for std::condition_variable.
 class ConditionVariable
 {
 public:
   struct PlatformData;
 
-  ConditionVariable();
-  ~ConditionVariable();
+  ConditionVariable() = default;
+  ~ConditionVariable() = default;
 
   // Wake one thread that is waiting on this condition.
-  void notify_one();
+  void notify_one() {
+    impl_.notify_one();
+  }
 
   // Wake all threads that are waiting on this condition.
-  void notify_all();
+  void notify_all() {
+    impl_.notify_all();
+  }
 
   // Block the current thread of execution until this condition variable is
   // woken from another thread via notify_one or notify_all.
-  void wait(UniqueLock<Mutex>& lock);
+  void wait(UniqueLock<Mutex>& lock) {
+    impl_.wait(lock.lock);
+  }
 
   // As with |wait|, block the current thread of execution until woken from
   // another thread. This method will resume waiting once woken until the given
   // Predicate |pred| evaluates to true.
   template <typename Predicate>
   void wait(UniqueLock<Mutex>& lock, Predicate pred) {
     while (!pred()) {
       wait(lock);
@@ -58,17 +65,19 @@ public:
   }
 
   // Block the current thread of execution until woken from another thread, or
   // the given absolute time is reached. The given absolute time is evaluated
   // when this method is called, so will wake up after (abs_time - now),
   // independent of system clock changes. While insulated from clock changes,
   // this API is succeptible to the issues discussed above wait_for.
   CVStatus wait_until(UniqueLock<Mutex>& lock,
-                      const mozilla::TimeStamp& abs_time);
+                      const mozilla::TimeStamp& abs_time) {
+    return wait_for(lock, abs_time - mozilla::TimeStamp::Now());
+  }
 
   // As with |wait_until|, block the current thread of execution until woken
   // from another thread, or the given absolute time is reached. This method
   // will resume waiting once woken until the given Predicate |pred| evaluates
   // to true.
   template <typename Predicate>
   bool wait_until(UniqueLock<Mutex>& lock, const mozilla::TimeStamp& abs_time,
                   Predicate pred) {
@@ -81,17 +90,20 @@ public:
   }
 
   // Block the current thread of execution until woken from another thread, or
   // the given time duration has elapsed. Given that the system may be
   // interrupted between the callee and the actual wait beginning, this call
   // has a minimum granularity of the system's scheduling interval, and may
   // encounter substantially longer delays, depending on system load.
   CVStatus wait_for(UniqueLock<Mutex>& lock,
-                    const mozilla::TimeDuration& rel_time);
+                    const mozilla::TimeDuration& rel_time) {
+    return impl_.wait_for(lock.lock, rel_time) == mozilla::detail::CVStatus::Timeout
+      ? CVStatus::Timeout : CVStatus::NoTimeout;
+  }
 
   // As with |wait_for|, block the current thread of execution until woken from
   // another thread or the given time duration has elapsed. This method will
   // resume waiting once woken until the given Predicate |pred| evaluates to
   // true.
   template <typename Predicate>
   bool wait_for(UniqueLock<Mutex>& lock, const mozilla::TimeDuration& rel_time,
                 Predicate pred) {
@@ -99,23 +111,14 @@ public:
                       mozilla::Move(pred));
   }
 
 
 private:
   ConditionVariable(const ConditionVariable&) = delete;
   ConditionVariable& operator=(const ConditionVariable&) = delete;
 
-  PlatformData* platformData();
-
-#ifndef XP_WIN
-  void* platformData_[sizeof(pthread_cond_t) / sizeof(void*)];
-  static_assert(sizeof(pthread_cond_t) / sizeof(void*) != 0 &&
-                sizeof(pthread_cond_t) % sizeof(void*) == 0,
-                "pthread_cond_t must have pointer alignment");
-#else
-  void* platformData_[4];
-#endif
+  mozilla::detail::ConditionVariableImpl impl_;
 };
 
 } // namespace js
 
 #endif // threading_ConditionVariable_h
--- a/js/src/threading/Mutex.h
+++ b/js/src/threading/Mutex.h
@@ -4,83 +4,36 @@
  * 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 threading_Mutex_h
 #define threading_Mutex_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Move.h"
+#include "mozilla/PlatformMutex.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Vector.h"
 
 namespace js {
 
-class ConditionVariable;
-
-namespace detail {
-
-class MutexImpl
-{
-public:
-  struct PlatformData;
-
-  MutexImpl();
-  ~MutexImpl();
-
-  bool operator==(const MutexImpl& rhs) {
-    return platformData_ == rhs.platformData_;
-  }
-
-protected:
-  void lock();
-  void unlock();
-
-private:
-  MutexImpl(const MutexImpl&) = delete;
-  void operator=(const MutexImpl&) = delete;
-  MutexImpl(MutexImpl&&) = delete;
-  void operator=(MutexImpl&&) = delete;
-
-  PlatformData* platformData();
-
-// Linux and maybe other platforms define the storage size of pthread_mutex_t in
-// bytes. However, we must define it as an array of void pointers to ensure
-// proper alignment.
-#if defined(__APPLE__) && defined(__MACH__) && defined(__i386__)
-  void* platformData_[11];
-#elif defined(__APPLE__) && defined(__MACH__) && defined(__amd64__)
-  void* platformData_[8];
-#elif defined(__linux__)
-  void* platformData_[40 / sizeof(void*)];
-#elif defined(_WIN32)
-  void* platformData_[6];
-#else
-  void* platformData_[64 / sizeof(void*)];
-#endif
-
-  friend class js::ConditionVariable;
-};
-
-} // namespace detail
-
 // A MutexId secifies the name and mutex order for a mutex.
 //
 // The mutex order defines the allowed order of mutex acqusition on a single
 // thread. Mutexes must be acquired in strictly increasing order. Mutexes with
 // the same order may not be held at the same time by that thread.
 struct MutexId
 {
   const char* name;
   uint32_t order;
 };
 
 #ifndef DEBUG
 
-class Mutex : public detail::MutexImpl
+class Mutex : public mozilla::detail::MutexImpl
 {
 public:
   static bool Init() { return true; }
   static void ShutDown() {}
 
   explicit Mutex(const MutexId& id) {}
 
   using MutexImpl::lock;
@@ -89,17 +42,17 @@ public:
 
 #else
 
 // In debug builds, js::Mutex is a wrapper over MutexImpl that checks correct
 // locking order is observed.
 //
 // The class maintains a per-thread stack of currently-held mutexes to enable it
 // to check this.
-class Mutex : public detail::MutexImpl
+class Mutex : public mozilla::detail::MutexImpl
 {
 public:
   static bool Init();
   static void ShutDown();
 
   explicit Mutex(const MutexId& id)
    : id_(id)
   {
--- a/js/src/threading/ProtectedData.cpp
+++ b/js/src/threading/ProtectedData.cpp
@@ -42,17 +42,17 @@ CheckActiveThread<Background>::check() c
     // and active thread's state from another thread while the active thread is
     // suspended. We need a way to mark these accesses as being tantamount to
     // accesses by the active thread. See bug 1323066.
 #ifndef XP_WIN
     if (OnBackgroundThread<Background>())
         return;
 
     JSContext* cx = TlsContext.get();
-    MOZ_ASSERT(cx == cx->runtime()->activeContext);
+    MOZ_ASSERT(cx == cx->runtime()->activeContext());
 #endif // XP_WIN
 }
 
 template class CheckActiveThread<AllowedBackgroundThread::None>;
 template class CheckActiveThread<AllowedBackgroundThread::GCTask>;
 template class CheckActiveThread<AllowedBackgroundThread::IonCompile>;
 
 template <AllowedBackgroundThread Background>
deleted file mode 100644
--- a/js/src/threading/posix/ConditionVariable.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/Assertions.h"
-#include "mozilla/CheckedInt.h"
-
-#include <errno.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "threading/ConditionVariable.h"
-#include "threading/Mutex.h"
-#include "threading/posix/MutexPlatformData.h"
-
-using mozilla::CheckedInt;
-using mozilla::TimeDuration;
-using mozilla::TimeStamp;
-
-static const long NanoSecPerSec = 1000000000;
-
-// Android 32-bit & macOS 10.12 has the clock functions, but not pthread_condattr_setclock.
-#if defined(HAVE_CLOCK_MONOTONIC) && \
-    !(defined(__ANDROID__) && !defined(__LP64__)) && !defined(__APPLE__)
-# define USE_CLOCK_API
-#endif
-
-#ifdef USE_CLOCK_API
-// The C++ specification defines std::condition_variable::wait_for in terms of
-// std::chrono::steady_clock, which is closest to CLOCK_MONOTONIC.
-static const clockid_t WhichClock = CLOCK_MONOTONIC;
-
-// While timevaladd is widely available to work with timevals, the newer
-// timespec structure is largely lacking such conveniences. Thankfully, the
-// utilities available in MFBT make implementing our own quite easy.
-static void
-moz_timespecadd(struct timespec* lhs, struct timespec* rhs, struct timespec* result)
-{
-  // Add nanoseconds. This may wrap, but not above 2 billion.
-  MOZ_RELEASE_ASSERT(lhs->tv_nsec < NanoSecPerSec);
-  MOZ_RELEASE_ASSERT(rhs->tv_nsec < NanoSecPerSec);
-  result->tv_nsec = lhs->tv_nsec + rhs->tv_nsec;
-
-  // Add seconds, checking for overflow in the platform specific time_t type.
-  CheckedInt<time_t> sec = CheckedInt<time_t>(lhs->tv_sec) + rhs->tv_sec;
-
-  // If nanoseconds overflowed, carry the result over into seconds.
-  if (result->tv_nsec >= NanoSecPerSec) {
-    MOZ_RELEASE_ASSERT(result->tv_nsec < 2 * NanoSecPerSec);
-    result->tv_nsec -= NanoSecPerSec;
-    sec += 1;
-  }
-
-  // Extracting the value asserts that there was no overflow.
-  MOZ_RELEASE_ASSERT(sec.isValid());
-  result->tv_sec = sec.value();
-}
-#endif
-
-struct js::ConditionVariable::PlatformData
-{
-  pthread_cond_t ptCond;
-};
-
-js::ConditionVariable::ConditionVariable()
-{
-  pthread_cond_t* ptCond = &platformData()->ptCond;
-
-#ifdef USE_CLOCK_API
-  pthread_condattr_t attr;
-  int r0 = pthread_condattr_init(&attr);
-  MOZ_RELEASE_ASSERT(!r0);
-
-  int r1 = pthread_condattr_setclock(&attr, WhichClock);
-  MOZ_RELEASE_ASSERT(!r1);
-
-  int r2 = pthread_cond_init(ptCond, &attr);
-  MOZ_RELEASE_ASSERT(!r2);
-
-  int r3 = pthread_condattr_destroy(&attr);
-  MOZ_RELEASE_ASSERT(!r3);
-#else
-  int r = pthread_cond_init(ptCond, NULL);
-  MOZ_RELEASE_ASSERT(!r);
-#endif
-}
-
-js::ConditionVariable::~ConditionVariable()
-{
-  int r = pthread_cond_destroy(&platformData()->ptCond);
-  MOZ_RELEASE_ASSERT(r == 0);
-}
-
-void
-js::ConditionVariable::notify_one()
-{
-  int r = pthread_cond_signal(&platformData()->ptCond);
-  MOZ_RELEASE_ASSERT(r == 0);
-}
-
-void
-js::ConditionVariable::notify_all()
-{
-  int r = pthread_cond_broadcast(&platformData()->ptCond);
-  MOZ_RELEASE_ASSERT(r == 0);
-}
-
-void
-js::ConditionVariable::wait(UniqueLock<Mutex>& lock)
-{
-  pthread_cond_t* ptCond = &platformData()->ptCond;
-  pthread_mutex_t* ptMutex = &lock.lock.platformData()->ptMutex;
-
-  int r = pthread_cond_wait(ptCond, ptMutex);
-  MOZ_RELEASE_ASSERT(r == 0);
-}
-
-js::CVStatus
-js::ConditionVariable::wait_until(UniqueLock<Mutex>& lock,
-                                  const TimeStamp& abs_time)
-{
-  return wait_for(lock, abs_time - TimeStamp::Now());
-}
-
-js::CVStatus
-js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
-                                const TimeDuration& a_rel_time)
-{
-  if (a_rel_time == TimeDuration::Forever()) {
-    wait(lock);
-    return CVStatus::NoTimeout;
-  }
-
-  pthread_cond_t* ptCond = &platformData()->ptCond;
-  pthread_mutex_t* ptMutex = &lock.lock.platformData()->ptMutex;
-  int r;
-
-  // Clamp to 0, as time_t is unsigned.
-  TimeDuration rel_time = a_rel_time < TimeDuration::FromSeconds(0)
-                          ? TimeDuration::FromSeconds(0)
-                          : a_rel_time;
-
-  // Convert the duration to a timespec.
-  struct timespec rel_ts;
-  rel_ts.tv_sec = static_cast<time_t>(rel_time.ToSeconds());
-  rel_ts.tv_nsec = static_cast<uint64_t>(rel_time.ToMicroseconds() * 1000.0) % NanoSecPerSec;
-
-#ifdef USE_CLOCK_API
-  struct timespec now_ts;
-  r = clock_gettime(WhichClock, &now_ts);
-  MOZ_RELEASE_ASSERT(!r);
-
-  struct timespec abs_ts;
-  moz_timespecadd(&now_ts, &rel_ts, &abs_ts);
-
-  r = pthread_cond_timedwait(ptCond, ptMutex, &abs_ts);
-#else
-  // Our non-clock-supporting platforms, OS X and Android, do support waiting
-  // on a condition variable with a relative timeout.
-  r = pthread_cond_timedwait_relative_np(ptCond, ptMutex, &rel_ts);
-#endif
-
-  if (r == 0) {
-    return CVStatus::NoTimeout;
-  }
-  MOZ_RELEASE_ASSERT(r == ETIMEDOUT);
-  return CVStatus::Timeout;
-}
-
-js::ConditionVariable::PlatformData*
-js::ConditionVariable::platformData()
-{
-  static_assert(sizeof platformData_ >= sizeof(PlatformData),
-                "platformData_ is too small");
-  return reinterpret_cast<PlatformData*>(platformData_);
-}
deleted file mode 100644
--- a/js/src/threading/posix/MutexImpl.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/Assertions.h"
-
-#include <errno.h>
-#include <pthread.h>
-#include <stdio.h>
-
-#include "threading/Mutex.h"
-#include "threading/posix/MutexPlatformData.h"
-
-#define TRY_CALL_PTHREADS(call, msg)            \
-  {                                             \
-    int result = (call);                        \
-    if (result != 0) {                          \
-      errno = result;                           \
-      perror(msg);                              \
-      MOZ_CRASH(msg);                           \
-    }                                           \
-  }
-
-js::detail::MutexImpl::MutexImpl()
-{
-  pthread_mutexattr_t* attrp = nullptr;
-
-#ifdef DEBUG
-  pthread_mutexattr_t attr;
-
-  TRY_CALL_PTHREADS(pthread_mutexattr_init(&attr),
-                    "js::detail::MutexImpl::MutexImpl: pthread_mutexattr_init failed");
-
-  TRY_CALL_PTHREADS(pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK),
-                    "js::detail::MutexImpl::MutexImpl: pthread_mutexattr_settype failed");
-
-  attrp = &attr;
-#endif
-
-  TRY_CALL_PTHREADS(pthread_mutex_init(&platformData()->ptMutex, attrp),
-                    "js::detail::MutexImpl::MutexImpl: pthread_mutex_init failed");
-
-#ifdef DEBUG
-  TRY_CALL_PTHREADS(pthread_mutexattr_destroy(&attr),
-                    "js::detail::MutexImpl::MutexImpl: pthread_mutexattr_destroy failed");
-#endif
-}
-
-js::detail::MutexImpl::~MutexImpl()
-{
-  TRY_CALL_PTHREADS(pthread_mutex_destroy(&platformData()->ptMutex),
-                    "js::detail::MutexImpl::~MutexImpl: pthread_mutex_destroy failed");
-}
-
-void
-js::detail::MutexImpl::lock()
-{
-  TRY_CALL_PTHREADS(pthread_mutex_lock(&platformData()->ptMutex),
-                    "js::detail::MutexImpl::lock: pthread_mutex_lock failed");
-}
-
-void
-js::detail::MutexImpl::unlock()
-{
-  TRY_CALL_PTHREADS(pthread_mutex_unlock(&platformData()->ptMutex),
-                    "js::detail::MutexImpl::unlock: pthread_mutex_unlock failed");
-}
-
-#undef TRY_CALL_PTHREADS
-
-js::detail::MutexImpl::PlatformData*
-js::detail::MutexImpl::platformData()
-{
-  static_assert(sizeof(platformData_) >= sizeof(PlatformData),
-                "platformData_ is too small");
-  return reinterpret_cast<PlatformData*>(platformData_);
-}
deleted file mode 100644
--- a/js/src/threading/posix/MutexPlatformData.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- 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 platform_win_MutexPlatformData_h
-#define platform_win_MutexPlatformData_h
-
-#include <pthread.h>
-
-#include "threading/Mutex.h"
-
-struct js::detail::MutexImpl::PlatformData
-{
-  pthread_mutex_t ptMutex;
-};
-
-#endif // platform_win_MutexPlatformData_h
deleted file mode 100644
--- a/js/src/threading/windows/ConditionVariable.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/Assertions.h"
-
-#include <float.h>
-#include <intrin.h>
-#include <stdlib.h>
-#include <windows.h>
-
-#include "threading/ConditionVariable.h"
-#include "threading/Mutex.h"
-#include "threading/windows/MutexPlatformData.h"
-
-// Some versions of the Windows SDK have a bug where some interlocked functions
-// are not redefined as compiler intrinsics. Fix that for the interlocked
-// functions that are used in this file.
-#if defined(_MSC_VER) && !defined(InterlockedExchangeAdd)
-#define InterlockedExchangeAdd(addend, value)                                  \
-  _InterlockedExchangeAdd((volatile long*)(addend), (long)(value))
-#endif
-
-#if defined(_MSC_VER) && !defined(InterlockedIncrement)
-#define InterlockedIncrement(addend)                                           \
-  _InterlockedIncrement((volatile long*)(addend))
-#endif
-
-// Wrapper for native condition variable APIs.
-struct js::ConditionVariable::PlatformData
-{
-  CONDITION_VARIABLE cv_;
-};
-
-js::ConditionVariable::ConditionVariable()
-{
-  InitializeConditionVariable(&platformData()->cv_);
-}
-
-void
-js::ConditionVariable::notify_one()
-{
-  WakeConditionVariable(&platformData()->cv_);
-}
-
-void
-js::ConditionVariable::notify_all()
-{
-  WakeAllConditionVariable(&platformData()->cv_);
-}
-
-void
-js::ConditionVariable::wait(UniqueLock<Mutex>& lock)
-{
-  CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection;
-  bool r = SleepConditionVariableCS(&platformData()->cv_, cs, INFINITE);
-  MOZ_RELEASE_ASSERT(r);
-}
-
-js::CVStatus
-js::ConditionVariable::wait_until(UniqueLock<Mutex>& lock,
-                                  const mozilla::TimeStamp& abs_time)
-{
-  return wait_for(lock, abs_time - mozilla::TimeStamp::Now());
-}
-
-js::CVStatus
-js::ConditionVariable::wait_for(UniqueLock<Mutex>& lock,
-                                const mozilla::TimeDuration& rel_time)
-{
-  CRITICAL_SECTION* cs = &lock.lock.platformData()->criticalSection;
-
-  // Note that DWORD is unsigned, so we have to be careful to clamp at 0.
-  // If rel_time is Forever, then ToMilliseconds is +inf, which evaluates as
-  // greater than UINT32_MAX, resulting in the correct INFINITE wait.
-  double msecd = rel_time.ToMilliseconds();
-  DWORD msec = msecd < 0.0
-               ? 0
-               : msecd > UINT32_MAX
-                 ? INFINITE
-                 : static_cast<DWORD>(msecd);
-
-  BOOL r = SleepConditionVariableCS(&platformData()->cv_, cs, msec);
-  if (r)
-    return CVStatus::NoTimeout;
-  MOZ_RELEASE_ASSERT(GetLastError() == ERROR_TIMEOUT);
-  return CVStatus::Timeout;
-}
-
-js::ConditionVariable::~ConditionVariable()
-{
-  // Native condition variables don't require cleanup.
-}
-
-inline js::ConditionVariable::PlatformData*
-js::ConditionVariable::platformData()
-{
-  static_assert(sizeof platformData_ >= sizeof(PlatformData),
-                "platformData_ is too small");
-  return reinterpret_cast<PlatformData*>(platformData_);
-}
deleted file mode 100644
--- a/js/src/threading/windows/MutexImpl.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/Assertions.h"
-#include "mozilla/DebugOnly.h"
-
-#include "jswin.h"
-
-#include "threading/Mutex.h"
-#include "threading/windows/MutexPlatformData.h"
-
-js::detail::MutexImpl::MutexImpl()
-{
-  // This number was adopted from NSPR.
-  const static DWORD LockSpinCount = 1500;
-  // Vista and later automatically allocate and subsequently leak a debug info
-  // object for each critical section that we allocate unless we tell the
-  // system not to do that.
-  BOOL r = InitializeCriticalSectionEx(&platformData()->criticalSection,
-                                       LockSpinCount,
-                                       CRITICAL_SECTION_NO_DEBUG_INFO);
-  MOZ_RELEASE_ASSERT(r);
-}
-
-js::detail::MutexImpl::~MutexImpl()
-{
-  DeleteCriticalSection(&platformData()->criticalSection);
-}
-
-void
-js::detail::MutexImpl::lock()
-{
-  EnterCriticalSection(&platformData()->criticalSection);
-}
-
-void
-js::detail::MutexImpl::unlock()
-{
-  LeaveCriticalSection(&platformData()->criticalSection);
-}
-
-js::detail::MutexImpl::PlatformData*
-js::detail::MutexImpl::platformData()
-{
-  static_assert(sizeof(platformData_) >= sizeof(PlatformData),
-                "platformData_ is too small");
-  return reinterpret_cast<PlatformData*>(platformData_);
-}
deleted file mode 100644
--- a/js/src/threading/windows/MutexPlatformData.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- 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 platform_win_MutexPlatformData_h
-#define platform_win_MutexPlatformData_h
-
-#include "jswin.h"
-
-#include "threading/Mutex.h"
-
-struct js::detail::MutexImpl::PlatformData
-{
-  CRITICAL_SECTION criticalSection;
-};
-
-#endif // platform_win_MutexPlatformData_h
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -40,16 +40,17 @@
 #include "vm/WrapperObject.h"
 #include "wasm/WasmInstance.h"
 
 #include "jsgcinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
 #include "jsscriptinlines.h"
 
+#include "vm/GeckoProfiler-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 
 using JS::dbg::AutoEntryMonitor;
 using JS::dbg::Builder;
 using js::frontend::IsIdentifier;
@@ -2398,17 +2399,22 @@ class MOZ_RAII ExecutionObservableCompar
                                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : compartments_(cx),
         zones_(cx)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     bool init() { return compartments_.init() && zones_.init(); }
-    bool add(JSCompartment* comp) { return compartments_.put(comp) && zones_.put(comp->zone()); }
+    bool add(JSCompartment* comp) {
+        // The current cx should have exclusive access to observed content,
+        // since debuggees must be in the same zone group as ther debugger.
+        MOZ_ASSERT(comp->zone()->group() == TlsContext.get()->zone()->group());
+        return compartments_.put(comp) && zones_.put(comp->zone());
+    }
 
     typedef HashSet<JSCompartment*>::Range CompartmentRange;
     const HashSet<JSCompartment*>* compartments() const { return &compartments_; }
 
     const HashSet<Zone*>* zones() const { return &zones_; }
     bool shouldRecompileOrInvalidate(JSScript* script) const {
         return script->hasBaselineScript() && compartments_.has(script->compartment());
     }
@@ -2492,16 +2498,19 @@ class MOZ_RAII ExecutionObservableScript
 {
     RootedScript script_;
 
   public:
     ExecutionObservableScript(JSContext* cx, JSScript* script
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : script_(cx, script)
     {
+        // The current cx should have exclusive access to observed content,
+        // since debuggees must be in the same zone group as ther debugger.
+        MOZ_ASSERT(singleZone()->group() == cx->zone()->group());
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     Zone* singleZone() const { return script_->compartment()->zone(); }
     JSScript* singleScriptForZoneInvalidation() const { return script_; }
     bool shouldRecompileOrInvalidate(JSScript* script) const {
         return script->hasBaselineScript() && script == script_;
     }
@@ -2596,17 +2605,16 @@ static bool
 UpdateExecutionObservabilityOfScriptsInZone(JSContext* cx, Zone* zone,
                                             const Debugger::ExecutionObservableSet& obs,
                                             Debugger::IsObserving observing)
 {
     using namespace js::jit;
 
     AutoSuppressProfilerSampling suppressProfilerSampling(cx);
 
-    JSRuntime* rt = cx->runtime();
     FreeOp* fop = cx->runtime()->defaultFreeOp();
 
     Vector<JSScript*> scripts(cx);
 
     // Iterate through observable scripts, invalidating their Ion scripts and
     // appending them to a vector for discarding their baseline scripts later.
     {
         AutoEnterAnalysis enter(fop, zone);
@@ -2628,28 +2636,28 @@ UpdateExecutionObservabilityOfScriptsInZ
         }
     }
 
     // Code below this point must be infallible to ensure the active bit of
     // BaselineScripts is in a consistent state.
     //
     // Mark active baseline scripts in the observable set so that they don't
     // get discarded. They will be recompiled.
-    for (JitActivationIterator actIter(rt); !actIter.done(); ++actIter) {
+    for (JitActivationIterator actIter(cx, zone->group()->ownerContext()); !actIter.done(); ++actIter) {
         if (actIter->compartment()->zone() != zone)
             continue;
 
         for (JitFrameIterator iter(actIter); !iter.done(); ++iter) {
             switch (iter.type()) {
               case JitFrame_BaselineJS:
                 MarkBaselineScriptActiveIfObservable(iter.script(), obs);
                 break;
               case JitFrame_IonJS:
                 MarkBaselineScriptActiveIfObservable(iter.script(), obs);
-                for (InlineFrameIterator inlineIter(rt, &iter); inlineIter.more(); ++inlineIter)
+                for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter)
                     MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs);
                 break;
               default:;
             }
         }
     }
 
     // Iterate through the scripts again and finish discarding
@@ -3935,16 +3943,23 @@ Debugger::construct(JSContext* cx, unsig
 
     args.rval().setObject(*obj);
     return true;
 }
 
 bool
 Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global)
 {
+    // Debuggers are required to be in the same zone group as their debuggees.
+    // The debugger must be able to observe all activity in the debuggee
+    // compartment, which requires that its thread have exclusive access to
+    // that compartment's contents.
+    MOZ_ASSERT(cx->zone() == object->zone());
+    MOZ_RELEASE_ASSERT(global->zone()->group() == cx->zone()->group());
+
     if (debuggees.has(global))
         return true;
 
     // Callers should generally be unable to get a reference to a debugger-
     // invisible global in order to pass it to addDebuggee. But this is possible
     // with certain testing aides we expose in the shell, so just make addDebuggee
     // throw in that case.
     JSCompartment* debuggeeCompartment = global->compartment();
@@ -7706,25 +7721,28 @@ UpdateFrameIterPc(FrameIter& iter)
         // because when returning to debuggee code, we would have bailed out
         // to baseline.
         //
         // We walk the stack to assert that it doesn't need updating.
         jit::RematerializedFrame* frame = iter.abstractFramePtr().asRematerializedFrame();
         jit::JitFrameLayout* jsFrame = (jit::JitFrameLayout*)frame->top();
         jit::JitActivation* activation = iter.activation()->asJit();
 
-        ActivationIterator activationIter(activation->cx()->runtime());
+        JSContext* cx = TlsContext.get();
+        MOZ_ASSERT(cx == activation->cx());
+
+        ActivationIterator activationIter(cx);
         while (activationIter.activation() != activation)
             ++activationIter;
 
         jit::JitFrameIterator jitIter(activationIter);
         while (!jitIter.isIonJS() || jitIter.jsFrame() != jsFrame)
             ++jitIter;
 
-        jit::InlineFrameIterator ionInlineIter(activation->cx(), &jitIter);
+        jit::InlineFrameIterator ionInlineIter(cx, &jitIter);
         while (ionInlineIter.frameNo() != frame->frameNo())
             ++ionInlineIter;
 
         MOZ_ASSERT(ionInlineIter.pc() == iter.pc());
 #endif
         return;
     }
 
new file mode 100644
--- /dev/null
+++ b/js/src/vm/GeckoProfiler-inl.h
@@ -0,0 +1,36 @@
+/* -*- 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_GeckoProfiler_inl_h
+#define vm_GeckoProfiler_inl_h
+
+#include "vm/GeckoProfiler.h"
+
+#include "vm/Runtime.h"
+
+namespace js {
+
+/*
+ * This class is used to suppress profiler sampling during
+ * critical sections where stack state is not valid.
+ */
+class MOZ_RAII AutoSuppressProfilerSampling
+{
+  public:
+    explicit AutoSuppressProfilerSampling(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+
+    ~AutoSuppressProfilerSampling();
+
+  private:
+    JSContext* cx_;
+    bool previouslyEnabled_;
+    JSRuntime::AutoProhibitActiveContextChange prohibitContextChange_;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+} // namespace js
+
+#endif // vm_GeckoProfiler_inl_h
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -1,15 +1,15 @@
 /* -*- 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/. */
 
-#include "vm/GeckoProfiler.h"
+#include "vm/GeckoProfiler-inl.h"
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsnum.h"
 #include "jsprf.h"
 #include "jsscript.h"
 
 #include "jit/BaselineFrame.h"
@@ -81,52 +81,56 @@ GeckoProfiler::enable(bool enabled)
     // (and thus, a new circular buffer). Set all current entries in the
     // JitcodeGlobalTable as expired and reset the buffer generation and lap
     // count.
     if (rt->hasJitRuntime() && rt->jitRuntime()->hasJitcodeGlobalTable())
         rt->jitRuntime()->getJitcodeGlobalTable()->setAllEntriesAsExpired(rt);
     rt->resetProfilerSampleBufferGen();
     rt->resetProfilerSampleBufferLapCount();
 
-    // Ensure that lastProfilingFrame is null before 'enabled' becomes true.
-    if (rt->contextFromMainThread()->jitActivation) {
-        rt->contextFromMainThread()->jitActivation->setLastProfilingFrame(nullptr);
-        rt->contextFromMainThread()->jitActivation->setLastProfilingCallSite(nullptr);
+    // Ensure that lastProfilingFrame is null for all threads before 'enabled' becomes true.
+    for (const CooperatingContext& target : rt->cooperatingContexts()) {
+        if (target.context()->jitActivation) {
+            target.context()->jitActivation->setLastProfilingFrame(nullptr);
+            target.context()->jitActivation->setLastProfilingCallSite(nullptr);
+        }
     }
 
     enabled_ = enabled;
 
     /* Toggle Gecko Profiler-related jumps on baseline jitcode.
      * The call to |ReleaseAllJITCode| above will release most baseline jitcode, but not
      * jitcode for scripts with active frames on the stack.  These scripts need to have
      * their profiler state toggled so they behave properly.
      */
     jit::ToggleBaselineProfiling(rt, enabled);
 
     /* Update lastProfilingFrame to point to the top-most JS jit-frame currently on
      * stack.
      */
-    if (rt->contextFromMainThread()->jitActivation) {
-        // Walk through all activations, and set their lastProfilingFrame appropriately.
-        if (enabled) {
-            void* lastProfilingFrame = GetTopProfilingJitFrame(rt->contextFromMainThread()->jitTop);
-            jit::JitActivation* jitActivation = rt->contextFromMainThread()->jitActivation;
-            while (jitActivation) {
-                jitActivation->setLastProfilingFrame(lastProfilingFrame);
-                jitActivation->setLastProfilingCallSite(nullptr);
+    for (const CooperatingContext& target : rt->cooperatingContexts()) {
+        if (target.context()->jitActivation) {
+            // Walk through all activations, and set their lastProfilingFrame appropriately.
+            if (enabled) {
+                void* lastProfilingFrame = GetTopProfilingJitFrame(target.context()->jitTop);
+                jit::JitActivation* jitActivation = target.context()->jitActivation;
+                while (jitActivation) {
+                    jitActivation->setLastProfilingFrame(lastProfilingFrame);
+                    jitActivation->setLastProfilingCallSite(nullptr);
 
-                lastProfilingFrame = GetTopProfilingJitFrame(jitActivation->prevJitTop());
-                jitActivation = jitActivation->prevJitActivation();
-            }
-        } else {
-            jit::JitActivation* jitActivation = rt->contextFromMainThread()->jitActivation;
-            while (jitActivation) {
-                jitActivation->setLastProfilingFrame(nullptr);
-                jitActivation->setLastProfilingCallSite(nullptr);
-                jitActivation = jitActivation->prevJitActivation();
+                    lastProfilingFrame = GetTopProfilingJitFrame(jitActivation->prevJitTop());
+                    jitActivation = jitActivation->prevJitActivation();
+                }
+            } else {
+                jit::JitActivation* jitActivation = target.context()->jitActivation;
+                while (jitActivation) {
+                    jitActivation->setLastProfilingFrame(nullptr);
+                    jitActivation->setLastProfilingCallSite(nullptr);
+                    jitActivation = jitActivation->prevJitActivation();
+                }
             }
         }
     }
 }
 
 /* Lookup the string for the function/script, creating one if necessary */
 const char*
 GeckoProfiler::profileString(JSScript* script, JSFunction* maybeFun)
@@ -490,18 +494,22 @@ ProfileEntry::script() const volatile
     MOZ_ASSERT(isJs());
     auto script = reinterpret_cast<JSScript*>(spOrScript);
     if (!script)
         return nullptr;
 
     // If profiling is supressed then we can't trust the script pointers to be
     // valid as they could be in the process of being moved by a compacting GC
     // (although it's still OK to get the runtime from them).
-    JSRuntime* rt = script->zoneFromAnyThread()->runtimeFromAnyThread();
-    if (!rt->unsafeContextFromAnyThread()->isProfilerSamplingEnabled())
+    //
+    // We only need to check the active context here, as
+    // AutoSuppressProfilerSampling prohibits the runtime's active context from
+    // being changed while it exists.
+    JSContext* cx = script->runtimeFromAnyThread()->activeContext();
+    if (!cx->isProfilerSamplingEnabled())
         return nullptr;
 
     MOZ_ASSERT(!IsForwarded(script));
     return script;
 }
 
 JS_FRIEND_API(jsbytecode*)
 ProfileEntry::pc() const volatile
@@ -545,38 +553,29 @@ js::RegisterContextProfilingEventMarker(
 JS_FRIEND_API(jsbytecode*)
 js::ProfilingGetPC(JSContext* cx, JSScript* script, void* ip)
 {
     return cx->runtime()->geckoProfiler().ipToPC(script, size_t(ip));
 }
 
 AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext* cx
                                                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-  : rt_(cx->runtime()),
-    previouslyEnabled_(cx->isProfilerSamplingEnabled())
+  : cx_(cx),
+    previouslyEnabled_(cx->isProfilerSamplingEnabled()),
+    prohibitContextChange_(cx->runtime())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (previouslyEnabled_)
-        rt_->contextFromMainThread()->disableProfilerSampling();
-}
-
-AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSRuntime* rt
-                                                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-  : rt_(rt),
-    previouslyEnabled_(rt_->contextFromMainThread()->isProfilerSamplingEnabled())
-{
-    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    if (previouslyEnabled_)
-        rt_->contextFromMainThread()->disableProfilerSampling();
+        cx_->disableProfilerSampling();
 }
 
 AutoSuppressProfilerSampling::~AutoSuppressProfilerSampling()
 {
     if (previouslyEnabled_)
-        rt_->contextFromMainThread()->enableProfilerSampling();
+        cx_->enableProfilerSampling();
 }
 
 void*
 js::GetTopProfilingJitFrame(uint8_t* exitFramePtr)
 {
     // For null exitFrame, there is no previous exit frame, just return.
     if (!exitFramePtr)
         return nullptr;
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -212,34 +212,16 @@ class GeckoProfiler
 
     void trace(JSTracer* trc);
     void fixupStringsMapAfterMovingGC();
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkStringsMapAfterMovingGC();
 #endif
 };
 
-/*
- * This class is used to suppress profiler sampling during
- * critical sections where stack state is not valid.
- */
-class MOZ_RAII AutoSuppressProfilerSampling
-{
-  public:
-    explicit AutoSuppressProfilerSampling(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
-    explicit AutoSuppressProfilerSampling(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
-
-    ~AutoSuppressProfilerSampling();
-
-  private:
-    JSRuntime* rt_;
-    bool previouslyEnabled_;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 inline size_t
 GeckoProfiler::stringsCount()
 {
     return strings.lock()->count();
 }
 
 inline void
 GeckoProfiler::stringsReset()
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -90,17 +90,18 @@ ReturnZeroSize(const void* p)
     return 0;
 }
 
 JSRuntime::JSRuntime(JSRuntime* parentRuntime)
   : parentRuntime(parentRuntime),
 #ifdef DEBUG
     updateChildRuntimeCount(parentRuntime),
 #endif
-    activeContext(nullptr),
+    activeContext_(nullptr),
+    activeContextChangeProhibited_(0),
     profilerSampleBufferGen_(0),
     profilerSampleBufferLapCount_(1),
     telemetryCallback(nullptr),
     getIncumbentGlobalCallback(nullptr),
     enqueuePromiseJobCallback(nullptr),
     enqueuePromiseJobCallbackData(nullptr),
     promiseRejectionTrackerCallback(nullptr),
     promiseRejectionTrackerCallbackData(nullptr),
@@ -132,16 +133,18 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
 #ifdef DEBUG
     mainThreadHasExclusiveAccess(false),
 #endif
     numExclusiveThreads(0),
     numCompartments(0),
     localeCallbacks(nullptr),
     defaultLocale(nullptr),
     defaultVersion_(JSVERSION_DEFAULT),
+    profilingScripts(false),
+    scriptAndCountsVector(nullptr),
     lcovOutput_(),
     jitRuntime_(nullptr),
     selfHostingGlobal_(nullptr),
     singletonContext(nullptr),
     singletonZoneGroup(nullptr),
     gc(thisFromCtor()),
     gcInitialized(false),
     NaNValue(DoubleNaNValue()),
@@ -187,33 +190,35 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
 }
 
 bool
 JSRuntime::init(JSContext* cx, uint32_t maxbytes, uint32_t maxNurseryBytes)
 {
     if (CanUseExtraThreads() && !EnsureHelperThreadsInitialized())
         return false;
 
-    activeContext = cx;
+    activeContext_ = cx;
+    if (!cooperatingContexts().append(cx))
+        return false;
 
     singletonContext = cx;
 
     defaultFreeOp_ = js_new<js::FreeOp>(this);
     if (!defaultFreeOp_)
         return false;
 
     ScopedJSDeletePtr<ZoneGroup> zoneGroup(js_new<ZoneGroup>(this));
     if (!zoneGroup)
         return false;
     singletonZoneGroup = zoneGroup;
 
     if (!gc.init(maxbytes, maxNurseryBytes))
         return false;
 
-    if (!zoneGroup->init(maxNurseryBytes))
+    if (!zoneGroup->init(maxNurseryBytes) || !gc.groups.ref().append(zoneGroup))
         return false;
     zoneGroup.forget();
 
     ScopedJSDeletePtr<Zone> atomsZone(new_<Zone>(this, nullptr));
     if (!atomsZone || !atomsZone->init(true))
         return false;
 
     JS::CompartmentOptions options;
@@ -244,19 +249,16 @@ JSRuntime::init(JSContext* cx, uint32_t 
         return false;
 
     JS::ResetTimeZone();
 
     jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
     jitSupportsUnalignedAccesses = js::jit::JitSupportsUnalignedAccesses();
     jitSupportsSimd = js::jit::JitSupportsSimd();
 
-    if (!wasm::EnsureSignalHandlers(this))
-        return false;
-
     if (!geckoProfiler().init())
         return false;
 
     if (!parentRuntime) {
         sharedImmutableStrings_ = js::SharedImmutableStringsCache::Create();
         if (!sharedImmutableStrings_)
             return false;
     }
@@ -298,17 +300,17 @@ JSRuntime::destroyRuntime()
 
         /*
          * Flag us as being destroyed. This allows the GC to free things like
          * interned atoms and Ion trampolines.
          */
         beingDestroyed_ = true;
 
         /* Allow the GC to release scripts that were being profiled. */
-        zoneGroupFromMainThread()->profilingScripts = false;
+        profilingScripts = false;
 
         /* Set the profiler sampler buffer generation to invalid. */
         profilerSampleBufferGen_ = UINT32_MAX;
 
         JS::PrepareForFullGC(cx);
         gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME);
     }
 
@@ -340,16 +342,25 @@ JSRuntime::destroyRuntime()
 
     DebugOnly<size_t> oldCount = liveRuntimesCount--;
     MOZ_ASSERT(oldCount > 0);
 
     js_delete(zoneGroupFromMainThread());
 }
 
 void
+JSRuntime::setActiveContext(JSContext* cx)
+{
+    MOZ_ASSERT_IF(cx, isCooperatingContext(cx));
+    MOZ_RELEASE_ASSERT(!activeContextChangeProhibited());
+
+    activeContext_ = cx;
+}
+
+void
 JSRuntime::addTelemetry(int id, uint32_t sample, const char* key)
 {
     if (telemetryCallback)
         (*telemetryCallback)(id, sample, key);
 }
 
 void
 JSRuntime::setTelemetryCallback(JSRuntime* rt, JSAccumulateTelemetryDataCallback callback)
@@ -494,17 +505,17 @@ JSContext::requestInterrupt(InterruptMod
         // collection among others), take additional steps to
         // interrupt corner cases where the above fields are not
         // regularly polled.  Wake both ilooping JIT code and
         // Atomics.wait().
         fx.lock();
         if (fx.isWaiting())
             fx.wake(FutexThread::WakeForJSInterrupt);
         fx.unlock();
-        InterruptRunningJitCode(runtime());
+        InterruptRunningJitCode(this);
     }
 }
 
 bool
 JSContext::handleInterrupt()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
     if (interrupt_ || jitStackLimit == UINTPTR_MAX) {
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -248,16 +248,18 @@ inline bool
 CanUseExtraThreads()
 {
     extern bool gCanUseExtraThreads;
     return gCanUseExtraThreads;
 }
 
 void DisableExtraThreads();
 
+using ScriptAndCountsVector = GCVector<ScriptAndCounts, 0, SystemAllocPolicy>;
+
 class AutoLockForExclusiveAccess;
 } // namespace js
 
 struct JSRuntime : public js::MallocProvider<JSRuntime>
 {
   private:
     friend class js::Activation;
     friend class js::ActivationIterator;
@@ -292,20 +294,67 @@ struct JSRuntime : public js::MallocProv
             if (parent_)
                 parent_->childRuntimeCount--;
         }
     };
 
     AutoUpdateChildRuntimeCount updateChildRuntimeCount;
 #endif
 
+  private:
     // The context for the thread which currently has exclusive access to most
     // contents of the runtime. When execution on the runtime is cooperatively
     // scheduled, this is the thread which is currently running.
-    mozilla::Atomic<JSContext*, mozilla::ReleaseAcquire> activeContext;
+    mozilla::Atomic<JSContext*, mozilla::ReleaseAcquire> activeContext_;
+
+    // All contexts participating in cooperative scheduling. All threads other
+    // than |activeContext_| are suspended.
+    js::ActiveThreadData<js::Vector<js::CooperatingContext, 4, js::SystemAllocPolicy>> cooperatingContexts_;
+
+    // Count of AutoProhibitActiveContextChange instances on the active context.
+    js::ActiveThreadData<size_t> activeContextChangeProhibited_;
+
+  public:
+    JSContext* activeContext() { return activeContext_; }
+    const void* addressOfActiveContext() { return &activeContext_; }
+
+    void setActiveContext(JSContext* cx);
+
+    js::Vector<js::CooperatingContext, 4, js::SystemAllocPolicy>& cooperatingContexts() {
+        return cooperatingContexts_.ref();
+    }
+
+#ifdef DEBUG
+    bool isCooperatingContext(JSContext* cx) {
+        for (const js::CooperatingContext& target : cooperatingContexts()) {
+            if (target.context() == cx)
+                return true;
+        }
+        return false;
+    }
+#endif
+
+    class MOZ_RAII AutoProhibitActiveContextChange
+    {
+        JSRuntime* rt;
+
+      public:
+        explicit AutoProhibitActiveContextChange(JSRuntime* rt)
+          : rt(rt)
+        {
+            rt->activeContextChangeProhibited_++;
+        }
+
+        ~AutoProhibitActiveContextChange()
+        {
+            rt->activeContextChangeProhibited_--;
+        }
+    };
+
+    bool activeContextChangeProhibited() { return activeContextChangeProhibited_; }
 
     /*
      * The profiler sampler generation after the latest sample.
      *
      * The lapCount indicates the number of largest number of 'laps'
      * (wrapping from high to low) that occurred when writing entries
      * into the sample buffer.  All JitcodeGlobalMap entries referenced
      * from a given sample are assigned the generation of the sample buffer
@@ -519,16 +568,22 @@ struct JSRuntime : public js::MallocProv
     js::ActiveThreadData<const JSLocaleCallbacks*> localeCallbacks;
 
     /* Default locale for Internationalization API */
     js::ActiveThreadData<char*> defaultLocale;
 
     /* Default JSVersion. */
     js::ActiveThreadData<JSVersion> defaultVersion_;
 
+    /* If true, new scripts must be created with PC counter information. */
+    js::ActiveThreadOrIonCompileData<bool> profilingScripts;
+
+    /* Strong references on scripts held for PCCount profiling API. */
+    js::ActiveThreadData<JS::PersistentRooted<js::ScriptAndCountsVector>*> scriptAndCountsVector;
+
   private:
     /* Code coverage output. */
     js::UnprotectedData<js::coverage::LCovRuntime> lcovOutput_;
   public:
     js::coverage::LCovRuntime& lcovOutput() { return lcovOutput_.ref(); }
 
   private:
     js::UnprotectedData<js::jit::JitRuntime*> jitRuntime_;
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -21,16 +21,17 @@
 
 #include "jit/JitFrameIterator-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Probes-inl.h"
 
 using namespace js;
 
+using mozilla::ArrayLength;
 using mozilla::Maybe;
 using mozilla::PodCopy;
 
 /*****************************************************************************/
 
 void
 InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script,
                                    AbstractFramePtr evalInFramePrev,
@@ -416,19 +417,19 @@ TraceInterpreterActivation(JSTracer* trc
 {
     for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
         InterpreterFrame* fp = frames.frame();
         fp->trace(trc, frames.sp(), frames.pc());
     }
 }
 
 void
-js::TraceInterpreterActivations(JSRuntime* rt, JSTracer* trc)
+js::TraceInterpreterActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc)
 {
-    for (ActivationIterator iter(rt); !iter.done(); ++iter) {
+    for (ActivationIterator iter(cx, target); !iter.done(); ++iter) {
         Activation* act = iter.activation();
         if (act->isInterpreter())
             TraceInterpreterActivation(trc, act->asInterpreter());
     }
 }
 
 /*****************************************************************************/
 
@@ -582,17 +583,17 @@ FrameIter::settleOnActivation()
 FrameIter::Data::Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption,
                       JSPrincipals* principals)
   : cx_(cx),
     debuggerEvalOption_(debuggerEvalOption),
     principals_(principals),
     state_(DONE),
     pc_(nullptr),
     interpFrames_(nullptr),
-    activations_(cx->runtime()),
+    activations_(cx),
     jitFrames_(),
     ionInlineFrameNo_(0),
     wasmFrames_()
 {
 }
 
 FrameIter::Data::Data(const FrameIter::Data& other)
   : cx_(other.cx_),
@@ -1013,17 +1014,17 @@ FrameIter::updatePcQuadratic()
       }
       case JIT:
         if (data_.jitFrames_.isBaselineJS()) {
             jit::BaselineFrame* frame = data_.jitFrames_.baselineFrame();
             jit::JitActivation* activation = data_.activations_->asJit();
 
             // ActivationIterator::jitTop_ may be invalid, so create a new
             // activation iterator.
-            data_.activations_ = ActivationIterator(data_.cx_->runtime());
+            data_.activations_ = ActivationIterator(data_.cx_);
             while (data_.activations_.activation() != activation)
                 ++data_.activations_;
 
             // Look for the current frame.
             data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
             while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame)
                 ++data_.jitFrames_;
 
@@ -1705,20 +1706,39 @@ Activation::unregisterProfiling()
     // There may be a non-active jit activation in the linked list.  Skip past it.
     Activation* prevProfiling = prevProfiling_;
     while (prevProfiling && prevProfiling->isJit() && !prevProfiling->asJit()->isActive())
         prevProfiling = prevProfiling->prevProfiling_;
 
     cx_->profilingActivation_ = prevProfiling;
 }
 
-ActivationIterator::ActivationIterator(JSRuntime* rt)
-  : jitTop_(TlsContext.get()->jitTop),
-    activation_(TlsContext.get()->activation_)
+ActivationIterator::ActivationIterator(JSContext* cx)
+    : jitTop_(cx->jitTop)
+    , activation_(cx->activation_)
+{
+    MOZ_ASSERT(cx == TlsContext.get());
+    settle();
+}
+
+ActivationIterator::ActivationIterator(JSContext* cx, const CooperatingContext& target)
 {
+    MOZ_ASSERT(cx == TlsContext.get());
+
+    // If target was specified --- even if it is the same as cx itself --- then
+    // we must be in a scope where changes of the active context are prohibited.
+    // Otherwise our state would be corrupted if the target thread resumed
+    // execution while we are iterating over its state.
+    MOZ_ASSERT(cx->runtime()->activeContextChangeProhibited());
+
+    // Tolerate a null target context, in case we are iterating over the
+    // activations for a zone group that is not in use by any thread.
+    jitTop_ = target.context() ? target.context()->jitTop.ref() : nullptr;
+    activation_ = target.context() ? target.context()->activation_.ref() : nullptr;
+
     settle();
 }
 
 ActivationIterator&
 ActivationIterator::operator++()
 {
     MOZ_ASSERT(activation_);
     if (activation_->isJit() && activation_->asJit()->isActive())
@@ -1734,17 +1754,17 @@ ActivationIterator::settle()
     // Stop at the next active activation. No need to update jitTop_, since
     // we don't iterate over an active jit activation.
     while (!done() && activation_->isJit() && !activation_->asJit()->isActive())
         activation_ = activation_->prev();
 }
 
 JS::ProfilingFrameIterator::ProfilingFrameIterator(JSContext* cx, const RegisterState& state,
                                                    uint32_t sampleBufferGen)
-  : rt_(cx->runtime()),
+  : cx_(cx),
     sampleBufferGen_(sampleBufferGen),
     activation_(nullptr),
     savedPrevJitTop_(nullptr)
 {
     if (!cx->runtime()->geckoProfiler().enabled())
         MOZ_CRASH("ProfilingFrameIterator called when geckoProfiler not enabled for runtime.");
 
     if (!cx->profilingActivation())
@@ -1816,17 +1836,17 @@ JS::ProfilingFrameIterator::iteratorCons
     if (activation_->isWasm()) {
         new (storage_.addr()) wasm::ProfilingFrameIterator(*activation_->asWasm(), state);
         // Set savedPrevJitTop_ to the actual jitTop_ from the runtime.
         savedPrevJitTop_ = activation_->cx()->jitTop;
         return;
     }
 
     MOZ_ASSERT(activation_->asJit()->isActive());
-    new (storage_.addr()) jit::JitProfilingFrameIterator(rt_, state);
+    new (storage_.addr()) jit::JitProfilingFrameIterator(cx_, state);
 }
 
 void
 JS::ProfilingFrameIterator::iteratorConstruct()
 {
     MOZ_ASSERT(!done());
     MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
 
@@ -1894,19 +1914,19 @@ JS::ProfilingFrameIterator::getPhysicalF
         frame.label = nullptr;
         return mozilla::Some(frame);
     }
 
     MOZ_ASSERT(isJit());
 
     // Look up an entry for the return address.
     void* returnAddr = jitIter().returnAddressToFp();
-    jit::JitcodeGlobalTable* table = rt_->jitRuntime()->getJitcodeGlobalTable();
+    jit::JitcodeGlobalTable* table = cx_->runtime()->jitRuntime()->getJitcodeGlobalTable();
     if (hasSampleBufferGen())
-        *entry = table->lookupForSamplerInfallible(returnAddr, rt_, sampleBufferGen_);
+        *entry = table->lookupForSamplerInfallible(returnAddr, cx_->runtime(), sampleBufferGen_);
     else
         *entry = table->lookupInfallible(returnAddr);
 
     MOZ_ASSERT(entry->isIon() || entry->isIonCache() || entry->isBaseline() || entry->isDummy());
 
     // Dummy frames produce no stack frames.
     if (entry->isDummy())
         return mozilla::Nothing();
@@ -1936,18 +1956,19 @@ JS::ProfilingFrameIterator::extractStack
     if (isWasm()) {
         frames[offset] = physicalFrame.value();
         frames[offset].label = wasmIter().label();
         return 1;
     }
 
     // Extract the stack for the entry.  Assume maximum inlining depth is <64
     const char* labels[64];
-    uint32_t depth = entry.callStackAtAddr(rt_, jitIter().returnAddressToFp(), labels, 64);
-    MOZ_ASSERT(depth < 64);
+    uint32_t depth = entry.callStackAtAddr(cx_->runtime(), jitIter().returnAddressToFp(),
+                                           labels, ArrayLength(labels));
+    MOZ_ASSERT(depth < ArrayLength(labels));
     for (uint32_t i = 0; i < depth; i++) {
         if (offset + i >= end)
             return i;
         frames[offset + i] = physicalFrame.value();
         frames[offset + i].label = labels[i];
     }
 
     return depth;
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -931,17 +931,34 @@ class InterpreterStack
 
     inline void purge(JSRuntime* rt);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return allocator_.sizeOfExcludingThis(mallocSizeOf);
     }
 };
 
-void TraceInterpreterActivations(JSRuntime* rt, JSTracer* trc);
+// CooperatingContext is a wrapper for a JSContext that is participating in
+// cooperative scheduling and may be different from the current thread. It is
+// in place to make it clearer when we might be operating on another thread,
+// and harder to accidentally pass in another thread's context to an API that
+// expects the current thread's context.
+class CooperatingContext
+{
+    JSContext* cx;
+
+  public:
+    explicit CooperatingContext(JSContext* cx) : cx(cx) {}
+    JSContext* context() const { return cx; }
+
+    // For &cx. The address should not be taken for other CooperatingContexts.
+    friend class ZoneGroup;
+};
+
+void TraceInterpreterActivations(JSContext* cx, const CooperatingContext& target, JSTracer* trc);
 
 /*****************************************************************************/
 
 /** Base class for all function call args. */
 class AnyInvokeArgs : public JS::CallArgs
 {
 };
 
@@ -1404,30 +1421,34 @@ class InterpreterActivation : public Act
     void enableInterruptsUnconditionally() {
         opMask_ = EnableInterruptsPseudoOpcode;
     }
     void clearInterruptsMask() {
         opMask_ = 0;
     }
 };
 
-// Iterates over a thread's activation list. If given a runtime, iterate over
-// the runtime's main thread's activation list.
+// Iterates over a thread's activation list.
 class ActivationIterator
 {
     uint8_t* jitTop_;
 
   protected:
     Activation* activation_;
 
   private:
     void settle();
 
   public:
-    explicit ActivationIterator(JSRuntime* rt);
+    explicit ActivationIterator(JSContext* cx);
+
+    // ActivationIterator can be used to iterate over a different thread's
+    // activations, for use by the GC, invalidation, and other operations that
+    // don't have a user-visible effect on the target thread's JS behavior.
+    ActivationIterator(JSContext* cx, const CooperatingContext& target);
 
     ActivationIterator& operator++();
 
     Activation* operator->() const {
         return activation_;
     }
     Activation* activation() const {
         return activation_;
@@ -1609,18 +1630,24 @@ class JitActivation : public Activation
 class JitActivationIterator : public ActivationIterator
 {
     void settle() {
         while (!done() && !activation_->isJit())
             ActivationIterator::operator++();
     }
 
   public:
-    explicit JitActivationIterator(JSRuntime* rt)
-      : ActivationIterator(rt)
+    explicit JitActivationIterator(JSContext* cx)
+      : ActivationIterator(cx)
+    {
+        settle();
+    }
+
+    JitActivationIterator(JSContext* cx, const CooperatingContext& target)
+      : ActivationIterator(cx, target)
     {
         settle();
     }
 
     JitActivationIterator& operator++() {
         ActivationIterator::operator++();
         settle();
         return *this;
@@ -1717,17 +1744,17 @@ class WasmActivation : public Activation
     // Read/written from SIGSEGV handler:
     void setResumePC(void* pc) { resumePC_ = pc; }
     void* resumePC() const { return resumePC_; }
 
     // Used by wasm::FrameIterator during stack unwinding.
     void unwindFP(uint8_t* fp) { fp_ = fp; }
 };
 
-// A FrameIter walks over the runtime's stack of JS script activations,
+// A FrameIter walks over a context's stack of JS script activations,
 // abstracting over whether the JS scripts were running in the interpreter or
 // different modes of compiled code.
 //
 // FrameIter is parameterized by what it includes in the stack iteration:
 //  - When provided, the optional JSPrincipal argument will cause FrameIter to
 //    only show frames in globals whose JSPrincipals are subsumed (via
 //    JSSecurityCallbacks::subsume) by the given JSPrincipal.
 //
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -219,17 +219,17 @@ TraceLoggerThread::silentFail(const char
 bool
 TraceLoggerThread::enable(JSContext* cx)
 {
     if (!enable())
         return fail(cx, "internal error");
 
     if (enabled_ == 1) {
         // Get the top Activation to log the top script/pc (No inlined frames).
-        ActivationIterator iter(cx->runtime());
+        ActivationIterator iter(cx);
         Activation* act = iter.activation();
 
         if (!act)
             return fail(cx, "internal error");
 
         JSScript* script = nullptr;
         int32_t engine = 0;
 
@@ -412,16 +412,18 @@ TraceLoggerThreadState::getOrCreateEvent
 
     payload->use();
 
     nextTextId++;
 
     if (!pointerMap.add(p, text, payload))
         return nullptr;
 
+    payload->incPointerCount();
+
     return payload;
 }
 
 TraceLoggerEventPayload*
 TraceLoggerThreadState::getOrCreateEventPayload(TraceLoggerTextId type, const char* filename,
                                                 size_t lineno, size_t colno, const void* ptr)
 {
     MOZ_ASSERT(type == TraceLogger_Scripts || type == TraceLogger_AnnotateScripts ||
@@ -479,16 +481,18 @@ TraceLoggerThreadState::getOrCreateEvent
 
     payload->use();
 
     nextTextId++;
 
     if (ptr) {
         if (!pointerMap.add(p, ptr, payload))
             return nullptr;
+
+        payload->incPointerCount();
     }
 
     return payload;
 }
 
 TraceLoggerEventPayload*
 TraceLoggerThreadState::getOrCreateEventPayload(TraceLoggerTextId type, JSScript* script)
 {
@@ -506,31 +510,28 @@ TraceLoggerThreadState::getOrCreateEvent
 void
 TraceLoggerThreadState::purgeUnusedPayloads()
 {
     // Care needs to be taken to maintain a coherent state in this function,
     // as payloads can have their use count change at any time from non-zero to
     // zero (but not the other way around; see TraceLoggerEventPayload::use()).
     LockGuard<Mutex> guard(lock);
 
-    // Remove the item in the pointerMap for which the payloads
-    // have no uses anymore
+    // Remove all the pointers to payloads that have no uses anymore
+    // and decrease the pointer count of that payload.
     for (PointerHashMap::Enum e(pointerMap); !e.empty(); e.popFront()) {
         if (e.front().value()->uses() == 0) {
-            TextIdHashMap::Ptr p = textIdPayloads.lookup(e.front().value()->textId());
-            MOZ_ASSERT(p);
-            textIdPayloads.remove(p);
-            js_delete(e.front().value());
+            e.front().value()->decPointerCount();
             e.removeFront();
         }
     }
 
     // Free all other payloads that have no uses anymore.
     for (TextIdHashMap::Enum e(textIdPayloads); !e.empty(); e.popFront()) {
-        if (e.front().value()->uses() == 0) {
+        if (e.front().value()->uses() == 0 && e.front().value()->pointerCount() == 0) {
             js_delete(e.front().value());
             e.removeFront();
         }
     }
 }
 
 void
 TraceLoggerThread::startEvent(TraceLoggerTextId id) {
@@ -563,17 +564,17 @@ TraceLoggerThread::startEvent(uint32_t i
     if (enabled_ > 0) {
         AutoEnterOOMUnsafeRegion oomUnsafe;
         if (!graphStack.append(id))
             oomUnsafe.crash("Could not add item to debug stack.");
     }
 #endif
 
     if (graph.get()) {
-        for (uint32_t otherId = graph->nextTextId(); otherId <= id; id++)
+        for (uint32_t otherId = graph->nextTextId(); otherId <= id; otherId++)
             graph->addTextId(otherId, maybeEventText(id));
     }
 
     log(id);
 }
 
 void
 TraceLoggerThread::stopEvent(TraceLoggerTextId id) {
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -115,26 +115,28 @@ class TraceLoggerEvent {
 };
 
 #ifdef DEBUG
 bool CurrentThreadOwnsTraceLoggerThreadStateLock();
 #endif
 
 /**
  * An internal class holding the string information to report, together with an
- * unique id and a useCount. Whenever this useCount reaches 0, this event
+ * unique id, a useCount and a pointerCount. Whenever this useCount reaches 0, this event
  * cannot get started/stopped anymore. Consumers may still request the
  * string information through maybeEventText below, but this may not succeed:
  * when the use count becomes zero, a payload may be deleted by any thread
- * holding the TraceLoggerThreadState lock.
+ * holding the TraceLoggerThreadState lock, after that the pointers have been
+ * cleared out of the pointerMap. That means pointerCount needs to be zero.
  */
 class TraceLoggerEventPayload {
     uint32_t textId_;
     UniqueChars string_;
     mozilla::Atomic<uint32_t> uses_;
+    mozilla::Atomic<uint32_t> pointerCount_;
 
   public:
     TraceLoggerEventPayload(uint32_t textId, char* string)
       : textId_(textId),
         string_(string),
         uses_(0)
     { }
 
@@ -146,28 +148,39 @@ class TraceLoggerEventPayload {
         return textId_;
     }
     const char* string() {
         return string_.get();
     }
     uint32_t uses() {
         return uses_;
     }
+    uint32_t pointerCount() {
+        return pointerCount_;
+    }
 
     // Payloads may have their use count change at any time, *except* the count
     // can only go from zero to non-zero while the thread state lock is held.
     // This should only happen under getOrCreateEventPayload below, and avoids
     // races with purgeUnusedPayloads.
     void use() {
         MOZ_ASSERT_IF(!uses_, CurrentThreadOwnsTraceLoggerThreadStateLock());
         uses_++;
     }
     void release() {
         uses_--;
     }
+    void incPointerCount() {
+        MOZ_ASSERT(CurrentThreadOwnsTraceLoggerThreadStateLock());
+        pointerCount_++;
+    }
+    void decPointerCount() {
+        MOZ_ASSERT(CurrentThreadOwnsTraceLoggerThreadStateLock());
+        pointerCount_--;
+    }
 };
 
 // Per thread trace logger state.
 class TraceLoggerThread : public mozilla::LinkedListElement<TraceLoggerThread>
 {
 #ifdef JS_TRACE_LOGGING
   private:
     uint32_t enabled_;
--- a/js/src/wasm/WasmSignalHandlers.cpp
+++ b/js/src/wasm/WasmSignalHandlers.cpp
@@ -37,49 +37,37 @@ using mozilla::PodArrayZero;
 
 #if defined(ANDROID)
 # include <sys/system_properties.h>
 # if defined(MOZ_LINKER)
 extern "C" MFBT_API bool IsSignalHandlingBroken();
 # endif
 #endif
 
-// For platforms where the signal/exception handler runs on the same
-// thread/stack as the victim (Unix and Windows), we can use TLS to find any
-// currently executing wasm code.
-static JSRuntime*
-RuntimeForCurrentThread()
-{
-    JSContext* cx = TlsContext.get();
-    if (cx && cx->runtime() && CurrentThreadCanAccessRuntime(cx->runtime()))
-        return cx->runtime();
-    return nullptr;
-}
-
 // Crashing inside the signal handler can cause the handler to be recursively
 // invoked, eventually blowing the stack without actually showing a crash
 // report dialog via Breakpad. To guard against this we watch for such
 // recursion and fall through to the next handler immediately rather than
 // trying to handle it.
 class AutoSetHandlingSegFault
 {
-    JSRuntime* rt;
+    JSContext* cx;
 
   public:
-    explicit AutoSetHandlingSegFault(JSRuntime* rt)
-      : rt(rt)
+    explicit AutoSetHandlingSegFault(JSContext* cx)
+      : cx(cx)
     {
-        MOZ_ASSERT(!rt->unsafeContextFromAnyThread()->handlingSegFault);
-        rt->unsafeContextFromAnyThread()->handlingSegFault = true;
+        MOZ_ASSERT(!cx->handlingSegFault);
+        cx->handlingSegFault = true;
     }
 
     ~AutoSetHandlingSegFault()
     {
-        MOZ_ASSERT(rt->unsafeContextFromAnyThread()->handlingSegFault);
-        rt->unsafeContextFromAnyThread()->handlingSegFault = false;
+        MOZ_ASSERT(cx->handlingSegFault);
+        cx->handlingSegFault = false;
     }
 };
 
 #if defined(XP_WIN)
 # define XMM_sig(p,i) ((p)->Xmm##i)
 # define EIP_sig(p) ((p)->Eip)
 # define RIP_sig(p) ((p)->Rip)
 # define RAX_sig(p) ((p)->Rax)
@@ -788,22 +776,22 @@ HandleFault(PEXCEPTION_POINTERS exceptio
 
     uint8_t** ppc = ContextToPC(context);
     uint8_t* pc = *ppc;
 
     if (record->NumberParameters < 2)
         return false;
 
     // Don't allow recursive handling of signals, see AutoSetHandlingSegFault.
-    JSRuntime* rt = RuntimeForCurrentThread();
-    if (!rt || rt->unsafeContextFromAnyThread()->handlingSegFault)
+    JSContext* cx = TlsContext.get();
+    if (!cx || cx->handlingSegFault)
         return false;
-    AutoSetHandlingSegFault handling(rt);
+    AutoSetHandlingSegFault handling(cx);
 
-    WasmActivation* activation = rt->unsafeContextFromAnyThread()->wasmActivationStack();
+    WasmActivation* activation = cx->wasmActivationStack();
     if (!activation)
         return false;
 
     const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
     if (!instance)
         return false;
 
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(record->ExceptionInformation[1]);
@@ -884,25 +872,25 @@ typedef struct {
 // The full Mach message also includes a trailer.
 struct ExceptionRequest
 {
     Request__mach_exception_raise_t body;
     mach_msg_trailer_t trailer;
 };
 
 static bool
-HandleMachException(JSRuntime* rt, const ExceptionRequest& request)
+HandleMachException(JSContext* cx, const ExceptionRequest& request)
 {
     // Don't allow recursive handling of signals, see AutoSetHandlingSegFault.
-    if (rt->unsafeContextFromAnyThread()->handlingSegFault)
+    if (cx->handlingSegFault)
         return false;
-    AutoSetHandlingSegFault handling(rt);
+    AutoSetHandlingSegFault handling(cx);
 
-    // Get the port of the JSRuntime's thread from the message.
-    mach_port_t rtThread = request.body.thread.name;
+    // Get the port of the JSContext's thread from the message.
+    mach_port_t cxThread = request.body.thread.name;
 
     // Read out the JSRuntime thread's register state.
     EMULATOR_CONTEXT context;
 # if defined(__x86_64__)
     unsigned int thread_state_count = x86_THREAD_STATE64_COUNT;
     unsigned int float_state_count = x86_FLOAT_STATE64_COUNT;
     int thread_state = x86_THREAD_STATE64;
     int float_state = x86_FLOAT_STATE64;
@@ -915,32 +903,32 @@ HandleMachException(JSRuntime* rt, const
     unsigned int thread_state_count = ARM_THREAD_STATE_COUNT;
     unsigned int float_state_count = ARM_NEON_STATE_COUNT;
     int thread_state = ARM_THREAD_STATE;
     int float_state = ARM_NEON_STATE;
 # else
 #  error Unsupported architecture
 # endif
     kern_return_t kret;
-    kret = thread_get_state(rtThread, thread_state,
+    kret = thread_get_state(cxThread, thread_state,
                             (thread_state_t)&context.thread, &thread_state_count);
     if (kret != KERN_SUCCESS)
         return false;
-    kret = thread_get_state(rtThread, float_state,
+    kret = thread_get_state(cxThread, float_state,
                             (thread_state_t)&context.float_, &float_state_count);
     if (kret != KERN_SUCCESS)
         return false;
 
     uint8_t** ppc = ContextToPC(&context);
     uint8_t* pc = *ppc;
 
     if (request.body.exception != EXC_BAD_ACCESS || request.body.codeCnt != 2)
         return false;
 
-    WasmActivation* activation = rt->unsafeContextFromAnyThread()->wasmActivationStack();
+    WasmActivation* activation = cx->wasmActivationStack();
     if (!activation)
         return false;
 
     const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
     if (!instance || !instance->codeSegment().containsFunctionPC(pc))
         return false;
 
     uint8_t* faultingAddress = reinterpret_cast<uint8_t*>(request.body.code[1]);
@@ -948,36 +936,36 @@ HandleMachException(JSRuntime* rt, const
     // This check isn't necessary, but, since we can, check anyway to make
     // sure we aren't covering up a real bug.
     if (!IsHeapAccessAddress(*instance, faultingAddress))
         return false;
 
     HandleMemoryAccess(&context, pc, faultingAddress, *instance, ppc);
 
     // Update the thread state with the new pc and register values.
-    kret = thread_set_state(rtThread, float_state, (thread_state_t)&context.float_, float_state_count);
+    kret = thread_set_state(cxThread, float_state, (thread_state_t)&context.float_, float_state_count);
     if (kret != KERN_SUCCESS)
         return false;
-    kret = thread_set_state(rtThread, thread_state, (thread_state_t)&context.thread, thread_state_count);
+    kret = thread_set_state(cxThread, thread_state, (thread_state_t)&context.thread, thread_state_count);
     if (kret != KERN_SUCCESS)
         return false;
 
     return true;