merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 26 Jul 2017 11:11:40 +0200
changeset 422144 e8400551c2e39f24c75a009ebed496c7acd7bf47
parent 422086 4f821dab4306fd0aff947ce55a1304acd7e4cf95 (current diff)
parent 422143 5697f69b1426ffc358a8b9f7f1b209057a9a7782 (diff)
child 422145 0d4a575f389cbc978679ef9001a079c6d9944eb6
child 422180 360af4185a25d3bb4d7bea43f61a24814ec3c7c4
child 422386 e9224528ba04e008087766d6bc2d0e10eea4c195
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone56.0a1
first release with
nightly linux32
e8400551c2e3 / 56.0a1 / 20170726100241 / files
nightly linux64
e8400551c2e3 / 56.0a1 / 20170726100241 / files
nightly mac
e8400551c2e3 / 56.0a1 / 20170726100322 / files
nightly win32
e8400551c2e3 / 56.0a1 / 20170726030207 / files
nightly win64
e8400551c2e3 / 56.0a1 / 20170726030207 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
ipc/chromium/src/base/message_loop.cc
js/src/jit-test/tests/ion/bailoutStaticObject.js
js/src/jit/BaselineCompiler.cpp
js/src/vm/Interpreter.cpp
layout/style/crashtests/crashtests.list
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewFragment.java
toolkit/components/extensions/Schemas.jsm
toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
toolkit/components/telemetry/Histograms.json
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -2102,21 +2102,41 @@ DocAccessible::DoARIAOwnsRelocation(Acce
     }
 
 #ifdef A11Y_LOG
   logging::TreeInfo("aria owns traversal", logging::eVerbose,
                     "candidate", child, nullptr);
 #endif
 
     // Same child on same position, no change.
-    if (child->Parent() == aOwner &&
-        child->IndexInParent() == static_cast<int32_t>(insertIdx)) {
-      MOZ_ASSERT(owned->ElementAt(idx) == child, "Not in sync!");
-      idx++;
-      continue;
+    if (child->Parent() == aOwner) {
+      int32_t indexInParent = child->IndexInParent();
+
+      // The child is being placed in its current index,
+      // eg. aria-owns='id1 id2 id3' is changed to aria-owns='id3 id2 id1'.
+      if (indexInParent == static_cast<int32_t>(insertIdx)) {
+        MOZ_ASSERT(child->IsRelocated(),
+                   "A child, having an index in parent from aria ownded indices range, has to be aria owned");
+        MOZ_ASSERT(owned->ElementAt(idx) == child,
+                   "Unexpected child in ARIA owned array");
+        idx++;
+        continue;
+      }
+
+      // The child is being inserted directly after its current index,
+      // resulting in a no-move case. This will happen when a parent aria-owns
+      // its last ordinal child:
+      // <ul aria-owns='id2'><li id='id1'></li><li id='id2'></li></ul>
+      if (indexInParent == static_cast<int32_t>(insertIdx) - 1) {
+        MOZ_ASSERT(!child->IsRelocated(), "Child should be in its ordinal position");
+        child->SetRelocated(true);
+        owned->InsertElementAt(idx, child);
+        idx++;
+        continue;
+      }
     }
 
     MOZ_ASSERT(owned->SafeElementAt(idx) != child, "Already in place!");
 
     if (owned->IndexOf(child) < idx) {
       continue; // ignore second entry of same ID
     }
 
@@ -2185,17 +2205,33 @@ DocAccessible::PutChildrenBack(nsTArray<
           MOZ_DIAGNOSTIC_ASSERT(origContainer == prevChild->Parent(), "Broken tree");
           origContainer = prevChild->Parent();
         }
         else {
           idxInParent = 0;
         }
       }
     }
-    MoveChild(child, origContainer, idxInParent);
+
+    // The child may have already be in its ordinal place for 2 reasons:
+    // 1. It was the last ordinal child, and the first aria-owned child.
+    //    given:      <ul id="list" aria-owns="b"><li id="a"></li><li id="b"></li></ul>
+    //    after load: $("list").setAttribute("aria-owns", "");
+    // 2. The preceding adopted children were just reclaimed, eg:
+    //    given:      <ul id="list"><li id="b"></li></ul>
+    //    after load: $("list").setAttribute("aria-owns", "a b");
+    //    later:      $("list").setAttribute("aria-owns", "");
+    if (origContainer != owner || child->IndexInParent() != idxInParent) {
+      MoveChild(child, origContainer, idxInParent);
+    } else {
+      MOZ_ASSERT(!child->PrevSibling() || !child->PrevSibling()->IsRelocated(),
+                 "No relocated child should appear before this one");
+      MOZ_ASSERT(!child->NextSibling() || child->NextSibling()->IsRelocated(),
+                 "No ordinal child should appear after this one");
+    }
   }
 
   aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
 }
 
 bool
 DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
                          int32_t aIdxInParent)
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -31,13 +31,14 @@ if CONFIG['MOZ_XUL']:
 TEST_DIRS += ['tests/mochitest']
 
 BROWSER_CHROME_MANIFESTS += [
   'tests/browser/bounds/browser.ini',
   'tests/browser/browser.ini',
   'tests/browser/e10s/browser.ini',
   'tests/browser/events/browser.ini',
   'tests/browser/scroll/browser.ini',
-  'tests/browser/states/browser.ini'
+  'tests/browser/states/browser.ini',
+  'tests/browser/tree/browser.ini'
 ]
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "Disability Access APIs")
--- a/accessible/tests/browser/events.js
+++ b/accessible/tests/browser/events.js
@@ -155,32 +155,34 @@ class UnexpectedEvents {
 }
 
 /**
  * A helper function that waits for a sequence of accessible events in
  * specified order.
  * @param {Array} events        a list of events to wait (same format as
  *                              waitForEvent arguments)
  */
-function waitForEvents(events, unexpected = [], ordered = false) {
+function waitForEvents(events, ordered = false) {
+  let expected = events.expected || events;
+  let unexpected = events.unexpected || [];
   // Next expected event index.
   let currentIdx = 0;
 
   let unexpectedListener = new UnexpectedEvents(unexpected);
 
-  return Promise.all(events.map((evt, idx) => {
+  return Promise.all(expected.map((evt, idx) => {
     let promise = evt instanceof Array ? waitForEvent(...evt) : evt;
     return promise.then(result => {
       if (ordered) {
         is(idx, currentIdx++,
           `Unexpected event order: ${result}`);
       }
       return result;
     });
   })).then(results => {
     unexpectedListener.stop();
     return results;
   });
 }
 
-function waitForOrderedEvents(events, unexpected = []) {
-  return waitForEvents(events, unexpected, true);
+function waitForOrderedEvents(events) {
+  return waitForEvents(events, true);
 }
--- a/accessible/tests/browser/shared-head.js
+++ b/accessible/tests/browser/shared-head.js
@@ -6,17 +6,17 @@
 
 /* import-globals-from ../mochitest/common.js */
 /* import-globals-from events.js */
 
 /* exported Logger, MOCHITESTS_DIR, invokeSetAttribute, invokeFocus,
             invokeSetStyle, getAccessibleDOMNodeID, getAccessibleTagName,
             addAccessibleTask, findAccessibleChildByID, isDefunct,
             CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, snippetToURL,
-            Cc, Cu */
+            Cc, Cu, arrayFromChildren */
 
 const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
 
 /**
  * Current browser test directory path used to load subscripts.
  */
 const CURRENT_DIR =
   'chrome://mochitests/content/browser/accessible/tests/browser/';
@@ -353,8 +353,13 @@ function queryInterfaces(accessible, int
       accessible.QueryInterface(iface);
     } catch (e) {
       ok(false, "Can't query " + iface);
     }
   }
 
   return accessible;
 }
+
+function arrayFromChildren(accessible) {
+  return Array.from({ length: accessible.childCount }, (c, i) =>
+    accessible.getChildAt(i));
+}
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/tree/browser.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+skip-if = e10s && os == 'win' && release_or_beta
+support-files =
+  head.js
+  !/accessible/tests/browser/events.js
+  !/accessible/tests/browser/shared-head.js
+  !/accessible/tests/mochitest/*.js
+
+[browser_test_aria_owns.js]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/tree/browser_test_aria_owns.js
@@ -0,0 +1,96 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+function testChildrenIds(acc, expectedIds) {
+  let ids = arrayFromChildren(acc).map(child => getAccessibleDOMNodeID(child));
+  Assert.deepEqual(ids, expectedIds,
+    `Children for ${getAccessibleDOMNodeID(acc)} are wrong.`);
+}
+
+async function runTests(browser, accDoc) {
+  let one = findAccessibleChildByID(accDoc, "one");
+  let two = findAccessibleChildByID(accDoc, "two");
+  let three = findAccessibleChildByID(accDoc, "three");
+  let four = findAccessibleChildByID(accDoc, "four");
+
+  testChildrenIds(one, ["a"]);
+  testChildrenIds(two, ["b", "c", "d"]);
+  testChildrenIds(three, []);
+
+  let onReorders = waitForEvents({
+    expected: [
+      [EVENT_REORDER, "two"]], // children will be reordered via aria-owns
+    unexpected: [
+      [EVENT_REORDER, "one"],  // child will remain in place
+      [EVENT_REORDER, "three"], // none of its children will be reclaimed
+      [EVENT_REORDER, "four"]] // child will remain in place
+  });
+
+  await ContentTask.spawn(browser, null, async function() {
+    // aria-own ordinal child in place, should be a no-op.
+    document.getElementById("one").setAttribute("aria-owns", "a");
+    // remove aria-owned child that is already ordinal, should be no-op.
+    document.getElementById("four").removeAttribute("aria-owns");
+    // shuffle aria-owns with markup child.
+    document.getElementById("two").setAttribute("aria-owns", "d c");
+  });
+
+  await onReorders;
+
+  testChildrenIds(one, ["a"]);
+  testChildrenIds(two, ["b", "d", "c"]);
+  testChildrenIds(three, []);
+  testChildrenIds(four, ["e"]);
+
+  onReorders = waitForEvent(EVENT_REORDER, "one");
+
+  await ContentTask.spawn(browser, null, async function() {
+    let aa = document.createElement("li");
+    aa.id = "aa";
+    document.getElementById("one").appendChild(aa);
+  });
+
+  await onReorders;
+
+  testChildrenIds(one, ["aa", "a"]);
+
+  onReorders = waitForEvents([
+      [EVENT_REORDER, "two"],    // "b" will go to "three"
+      [EVENT_REORDER, "three"], // some children will be reclaimed and acquired
+      [EVENT_REORDER, "one"]]); // removing aria-owns will reorder native children
+
+  await ContentTask.spawn(browser, null, async function() {
+    // removing aria-owns should reorder the children
+    document.getElementById("one").removeAttribute("aria-owns");
+    // child order will be overridden by aria-owns
+    document.getElementById("three").setAttribute("aria-owns", "b d");
+  });
+
+  await onReorders;
+
+  testChildrenIds(one, ["a", "aa"]);
+  testChildrenIds(two, ["c"]);
+  testChildrenIds(three, ["b", "d"]);
+}
+
+/**
+ * Test caching of accessible object states
+ */
+addAccessibleTask(`
+    <ul id="one">
+      <li id="a">Test</li>
+    </ul>
+    <ul id="two" aria-owns="d">
+      <li id="b">Test 2</li>
+      <li id="c">Test 3</li>
+    </ul>
+    <ul id="three">
+      <li id="d">Test 4</li>
+    </ul>
+    <ul id="four" aria-owns="e">
+      <li id="e">Test 5</li>
+    </ul>
+    `, runTests);
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/tree/head.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+// Load the shared-head file first.
+/* import-globals-from ../shared-head.js */
+Services.scriptloader.loadSubScript(
+  'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+  this);
+
+// Loading and common.js from accessible/tests/mochitest/ for all tests, as
+// well as events.js.
+loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'events.js', 'layout.js');
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -1440,18 +1440,16 @@ AccessibleWrap::GetIAccessibleFor(const 
   // If the MSAA ID is not a chrome id then we already know that we won't
   // find it here and should look remotely instead. This handles the case when
   // accessible is part of the chrome process and is part of the xul browser
   // window and the child id points in the content documents. Thus we need to
   // make sure that it is never called on proxies.
   if (XRE_IsParentProcess() && !IsProxy() && !sIDGen.IsChromeID(varChild.lVal)) {
     return GetRemoteIAccessibleFor(varChild);
   }
-  MOZ_ASSERT(XRE_IsParentProcess() ||
-             sIDGen.IsIDForThisContentProcess(varChild.lVal));
 
   if (varChild.lVal > 0) {
     // Gecko child indices are 0-based in contrast to indices used in MSAA.
     MOZ_ASSERT(!IsProxy());
     Accessible* xpAcc = GetChildAt(varChild.lVal - 1);
     if (!xpAcc) {
       return nullptr;
     }
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -906,16 +906,17 @@ var RefreshBlocker = {
       }
     }
   },
 
   enable() {
     this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
                      .createInstance(Ci.nsIWebProgress);
     this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
+    this._filter.target = tabEventTarget;
 
     let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebProgress);
     webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
 
     addMessageListener("RefreshBlocker:Refresh", this);
   },
 
--- a/browser/extensions/onboarding/content/onboarding.css
+++ b/browser/extensions/onboarding/content/onboarding.css
@@ -308,16 +308,28 @@
   border: 1px solid transparent;
   border-radius: 0;
   color: #fff;
   float: inline-end;
   margin-inline-end: 26px;
   margin-top: -32px;
 }
 
+/* Remove default dotted outline around buttons' text */
+.onboarding-tour-action-button::-moz-focus-inner {
+  border: 0;
+}
+
+/* Keyboard focus specific outline */
+.onboarding-tour-action-button:-moz-focusring {
+  outline: 2px solid rgba(0,149,221,0.5);
+  outline-offset: 1px;
+  -moz-outline-radius: 2px;
+}
+
 .onboarding-tour-action-button:hover:not([disabled]) ,
 #onboarding-notification-action-btn:hover {
   background: #0060df;
   cursor: pointer;
 }
 
 .onboarding-tour-action-button:active:not([disabled]),
 #onboarding-notification-action-btn:active  {
--- a/devtools/client/inspector/boxmodel/components/BoxModelInfo.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelInfo.js
@@ -30,20 +30,20 @@ module.exports = createClass({
   onToggleGeometryEditor(e) {
     this.props.onToggleGeometryEditor();
   },
 
   render() {
     let { boxModel } = this.props;
     let { geometryEditorEnabled, layout } = boxModel;
     let {
-      height,
+      height = "-",
       isPositionEditable,
       position,
-      width,
+      width = "-",
     } = layout;
 
     let buttonClass = "layout-geometry-editor devtools-button";
     if (geometryEditorEnabled) {
       buttonClass += " checked";
     }
 
     return dom.div(
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js
+++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js
@@ -115,38 +115,38 @@ module.exports = createClass({
    * Returns true if the position is displayed and false otherwise.
    */
   getDisplayPosition() {
     let { layout } = this.props.boxModel;
     return layout.position && layout.position != "static";
   },
 
   getHeightValue(property) {
-    let { layout } = this.props.boxModel;
-
     if (property == undefined) {
       return "-";
     }
 
+    let { layout } = this.props.boxModel;
+
     property -= parseFloat(layout["border-top-width"]) +
                 parseFloat(layout["border-bottom-width"]) +
                 parseFloat(layout["padding-top"]) +
                 parseFloat(layout["padding-bottom"]);
     property = parseFloat(property.toPrecision(6));
 
     return property;
   },
 
   getWidthValue(property) {
-    let { layout } = this.props.boxModel;
-
     if (property == undefined) {
       return "-";
     }
 
+    let { layout } = this.props.boxModel;
+
     property -= parseFloat(layout["border-left-width"]) +
                 parseFloat(layout["border-right-width"]) +
                 parseFloat(layout["padding-left"]) +
                 parseFloat(layout["padding-right"]);
     property = parseFloat(property.toPrecision(6));
 
     return property;
   },
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -2985,18 +2985,18 @@ Element::List(FILE* out, int32_t aIndent
   fprintf(out, "@%p", (void *)this);
 
   ListAttributes(out);
 
   fprintf(out, " state=[%llx]",
           static_cast<unsigned long long>(State().GetInternalValue()));
   fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
   if (IsCommonAncestorForRangeInSelection()) {
-    nsRange::RangeHashTable* ranges =
-      static_cast<nsRange::RangeHashTable*>(GetProperty(nsGkAtoms::range));
+    const nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
+      GetExistingCommonAncestorRanges();
     fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0);
   }
   fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
   fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
 
   nsIContent* child = GetFirstChild();
   if (child) {
     fputs("\n", out);
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -653,16 +653,29 @@ nsNodeSupportsWeakRefTearoff::GetWeakRef
   }
 
   NS_ADDREF(*aInstancePtr = slots->mWeakReference);
 
   return NS_OK;
 }
 
 //----------------------------------------------------------------------
+
+static const size_t MaxDOMSlotSizeAllowed =
+#ifdef HAVE_64BIT_BUILD
+128;
+#else
+64;
+#endif
+
+static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed,
+              "DOM slots cannot be grown without consideration");
+static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed,
+              "DOM slots cannot be grown without consideration");
+
 FragmentOrElement::nsDOMSlots::nsDOMSlots()
   : nsINode::nsSlots(),
     mDataset(nullptr)
 {
 }
 
 FragmentOrElement::nsDOMSlots::~nsDOMSlots()
 {
--- a/dom/base/StructuredCloneBlob.cpp
+++ b/dom/base/StructuredCloneBlob.cpp
@@ -17,27 +17,32 @@
 #include "mozilla/dom/StructuredCloneTags.h"
 
 namespace mozilla {
 namespace dom {
 
 StructuredCloneBlob::StructuredCloneBlob()
     : StructuredCloneHolder(CloningSupported, TransferringNotSupported,
                             StructuredCloneScope::DifferentProcess)
-{};
+{}
+
+StructuredCloneBlob::~StructuredCloneBlob()
+{
+  UnregisterWeakMemoryReporter(this);
+}
 
 
 /* static */ already_AddRefed<StructuredCloneBlob>
 StructuredCloneBlob::Constructor(GlobalObject& aGlobal, JS::HandleValue aValue,
                                  JS::HandleObject aTargetGlobal,
                                  ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.Context();
 
-  RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob();
+  RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create();
 
   Maybe<JSAutoCompartment> ac;
   JS::RootedValue value(cx, aValue);
 
   if (aTargetGlobal) {
     JS::RootedObject targetGlobal(cx, js::CheckedUnwrap(aTargetGlobal));
     if (!targetGlobal) {
       js::ReportAccessDenied(cx);
@@ -99,17 +104,17 @@ StructuredCloneBlob::Deserialize(JSConte
 
 
 /* static */ JSObject*
 StructuredCloneBlob::ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader,
                                          StructuredCloneHolder* aHolder)
 {
   JS::RootedObject obj(aCx);
   {
-    RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob();
+    RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create();
 
     if (!holder->ReadStructuredCloneInternal(aCx, aReader, aHolder) ||
         !holder->WrapObject(aCx, nullptr, &obj)) {
       return nullptr;
     }
   }
   return obj.get();
 }
@@ -176,10 +181,25 @@ StructuredCloneBlob::WriteStructuredClon
 }
 
 bool
 StructuredCloneBlob::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto, JS::MutableHandleObject aResult)
 {
     return StructuredCloneHolderBinding::Wrap(aCx, this, aGivenProto, aResult);
 }
 
+
+NS_IMETHODIMP
+StructuredCloneBlob::CollectReports(nsIHandleReportCallback* aHandleReport,
+                                    nsISupports* aData, bool aAnonymize)
+{
+  MOZ_COLLECT_REPORT(
+    "explicit/dom/structured-clone-holder", KIND_HEAP, UNITS_BYTES,
+    MallocSizeOf(this) + SizeOfExcludingThis(MallocSizeOf),
+    "Memory used by StructuredCloneHolder DOM objects.");
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(StructuredCloneBlob, nsIMemoryReporter)
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/StructuredCloneBlob.h
+++ b/dom/base/StructuredCloneBlob.h
@@ -4,32 +4,33 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_StructuredCloneBlob_h
 #define mozilla_dom_StructuredCloneBlob_h
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/StructuredCloneHolderBinding.h"
-#include "mozilla/RefCounted.h"
 
 #include "jsapi.h"
 
+#include "nsIMemoryReporter.h"
 #include "nsISupports.h"
 
 namespace mozilla {
 namespace dom {
 
-class StructuredCloneBlob : public StructuredCloneHolder
-                          , public RefCounted<StructuredCloneBlob>
+class StructuredCloneBlob final : public nsIMemoryReporter
+                                , public StructuredCloneHolder
 {
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
 public:
-  MOZ_DECLARE_REFCOUNTED_TYPENAME(StructuredCloneBlob)
-
-  explicit StructuredCloneBlob();
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMEMORYREPORTER
 
   static JSObject* ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader,
                                        StructuredCloneHolder* aHolder);
   bool WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter,
                             StructuredCloneHolder* aHolder);
 
   static already_AddRefed<StructuredCloneBlob>
   Constructor(GlobalObject& aGlobal, JS::HandleValue aValue, JS::HandleObject aTargetGlobal, ErrorResult& aRv);
@@ -38,22 +39,28 @@ public:
                    JS::MutableHandleValue aResult, ErrorResult& aRv);
 
   nsISupports* GetParentObject() const { return nullptr; }
   JSObject* GetWrapper() const { return nullptr; }
 
   bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandleObject aResult);
 
 protected:
-  template <typename T, detail::RefCountAtomicity>
-  friend class detail::RefCounted;
-
-  ~StructuredCloneBlob() = default;
+  virtual ~StructuredCloneBlob();
 
 private:
+  explicit StructuredCloneBlob();
+
+  static already_AddRefed<StructuredCloneBlob> Create()
+  {
+    RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob();
+    RegisterWeakMemoryReporter(holder);
+    return holder.forget();
+  }
+
   bool ReadStructuredCloneInternal(JSContext* aCx, JSStructuredCloneReader* aReader,
                                    StructuredCloneHolder* aHolder);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_StructuredCloneBlob_h
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_StructuredCloneHolder_h
 #define mozilla_dom_StructuredCloneHolder_h
 
 #include "jsapi.h"
 #include "js/StructuredClone.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsISupports.h"
 #include "nsTArray.h"
 
 #ifdef DEBUG
 #include "nsIThread.h"
@@ -111,16 +112,25 @@ public:
   }
 
   JSStructuredCloneData& BufferData() const
   {
     MOZ_ASSERT(mBuffer, "Write() has never been called.");
     return mBuffer->data();
   }
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+  {
+    size_t size = 0;
+    if (HasData()) {
+      size += mBuffer->sizeOfIncludingThis(aMallocSizeOf);
+    }
+    return size;
+  }
+
 protected:
   UniquePtr<JSAutoStructuredCloneBuffer> mBuffer;
 
   StructuredCloneScope mStructuredCloneScope;
 
 #ifdef DEBUG
   bool mClearCalled;
 #endif
@@ -301,16 +311,22 @@ protected:
                       JSStructuredCloneData& aBuffer,
                       uint32_t aAlgorithmVersion,
                       JS::MutableHandle<JS::Value> aValue,
                       ErrorResult &aRv);
 
   bool mSupportsCloning;
   bool mSupportsTransferring;
 
+  // SizeOfExcludingThis is inherited from StructuredCloneHolderBase. It doesn't
+  // account for objects in the following arrays because a) they're not expected
+  // to be stored in long-lived StructuredCloneHolder objects, and b) in the
+  // case of BlobImpl objects, MemoryBlobImpls have their own memory reporters,
+  // and the other types do not hold significant amounts of memory alive.
+
   // Used for cloning blobs in the structured cloning algorithm.
   nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
 
   // Used for cloning JS::WasmModules in the structured cloning algorithm.
   nsTArray<RefPtr<JS::WasmModule>> mWasmModuleArray;
 
   // Used for cloning InputStream in the structured cloning algorithm.
   nsTArray<nsCOMPtr<nsIInputStream>> mInputStreamArray;
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -2,16 +2,17 @@
 /* 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 TabGroup_h
 #define TabGroup_h
 
+#include "nsHashKeys.h"
 #include "nsISupportsImpl.h"
 #include "nsIPrincipal.h"
 #include "nsTHashtable.h"
 #include "nsString.h"
 
 #include "mozilla/Atomics.h"
 #include "mozilla/SchedulerGroup.h"
 #include "mozilla/RefPtr.h"
@@ -38,16 +39,17 @@ class TabChild;
 // A TabGroup is a set of browsing contexts which are all "related". Within a
 // TabGroup, browsing contexts are broken into "similar-origin" DocGroups. In
 // more detail, a DocGroup is actually a collection of documents, and a
 // TabGroup is a collection of DocGroups. A TabGroup typically will contain
 // (through its DocGroups) the documents from one or more tabs related by
 // window.opener. A DocGroup is a member of exactly one TabGroup.
 
 class DocGroup;
+class TabChild;
 
 class TabGroup final : public SchedulerGroup
 {
 private:
   class HashEntry : public nsCStringHashKey
   {
   public:
     // NOTE: Weak reference. The DocGroup destructor removes itself from its
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -1286,16 +1286,18 @@ TimeoutManager::MaybeStartThrottleTimeou
     do_CreateInstance("@mozilla.org/timer;1");
   if (!mThrottleTimeoutsTimer) {
     return;
   }
 
   nsCOMPtr<nsITimerCallback> callback =
     new ThrottleTimeoutsCallback(&mWindow);
 
+  mThrottleTimeoutsTimer->SetTarget(EventTarget());
+
   mThrottleTimeoutsTimer->InitWithCallback(
     callback, gTimeoutThrottlingDelay, nsITimer::TYPE_ONE_SHOT);
 }
 
 void
 TimeoutManager::BeginSyncOperation()
 {
   // If we're beginning a sync operation, the currently running
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -845,16 +845,23 @@ nsFrameMessageManager::GetContent(mozIDO
 NS_IMETHODIMP
 nsFrameMessageManager::GetDocShell(nsIDocShell** aDocShell)
 {
   *aDocShell = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsFrameMessageManager::GetTabEventTarget(nsIEventTarget** aTarget)
+{
+  *aTarget = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsFrameMessageManager::Btoa(const nsAString& aBinaryData,
                             nsAString& aAsciiBase64String)
 {
   return nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::Atob(const nsAString& aAsciiString,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1028,17 +1028,17 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMW
   mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
   mDesktopModeViewport(false), mIsRootOuterWindow(false), mInnerWindow(nullptr),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false),
   mLargeAllocStatus(LargeAllocStatus::NONE),
   mHasTriedToCacheTopInnerWindow(false),
-  mNumOfIndexedDBDatabases(0), mCleanedUp(false)
+  mNumOfIndexedDBDatabases(0)
 {
   if (aOuterWindow) {
     mTimeoutManager =
       MakeUnique<mozilla::dom::TimeoutManager>(*nsGlobalWindow::Cast(AsInner()));
   }
 }
 
 template<class T>
@@ -1615,16 +1615,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mIdleRequestExecutor(nullptr),
 #ifdef DEBUG
     mSetOpenerWindowCalled(false),
 #endif
 #ifdef MOZ_B2G
     mNetworkUploadObserverEnabled(false),
     mNetworkDownloadObserverEnabled(false),
 #endif
+    mCleanedUp(false),
     mDialogAbuseCount(0),
     mAreDialogsEnabled(true),
 #ifdef DEBUG
     mIsValidatingTabGroup(false),
 #endif
     mCanSkipCCGeneration(0),
     mAutoActivateVRDisplayID(0),
     mBeforeUnloadListenerCount(0)
@@ -2000,20 +2001,17 @@ nsGlobalWindow::CleanUp()
 
   mAudioWorklet = nullptr;
   mPaintWorklet = nullptr;
 
   mExternal = nullptr;
 
   mMozSelfSupport = nullptr;
 
-  if (mPerformance) {
-    mPerformance->Shutdown();
-    mPerformance = nullptr;
-  }
+  mPerformance = nullptr;
 
 #ifdef MOZ_WEBSPEECH
   mSpeechSynthesis = nullptr;
 #endif
 
 #if defined(MOZ_WIDGET_ANDROID)
   mOrientationChangeObserver = nullptr;
 #endif
@@ -4358,20 +4356,19 @@ nsGlobalWindow::GetPerformance()
   return AsInner()->GetPerformance();
 }
 
 void
 nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded()
 {
   MOZ_ASSERT(IsInnerWindow());
 
-  if (mPerformance || !mDoc || mCleanedUp) {
-    return;
-  }
-
+  if (mPerformance || !mDoc) {
+    return;
+  }
   RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
   nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel()));
   bool timingEnabled = false;
   if (!timedChannel ||
       !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) ||
       !timingEnabled) {
     timedChannel = nullptr;
   }
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -2005,16 +2005,18 @@ protected:
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
 #ifdef MOZ_B2G
   bool mNetworkUploadObserverEnabled;
   bool mNetworkDownloadObserverEnabled;
 #endif // MOZ_B2G
 
+  bool mCleanedUp;
+
   nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
 
   using XBLPrototypeHandlerTable = nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>;
   nsAutoPtr<XBLPrototypeHandlerTable> mCachedXBLPrototypeHandlers;
 
   // mSuspendedDoc is only set on outer windows. It's useful when we get matched
   // EnterModalState/LeaveModalState calls, in which case the outer window is
   // responsible for unsuspending events on the document. If we don't (for
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -3,16 +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/. */
 
 #include "nsISupports.idl"
 
 interface mozIDOMWindowProxy;
 interface nsIDocShell;
 interface nsIContent;
+interface nsIEventTarget;
 interface nsIFrameLoader;
 interface nsIPrincipal;
 
 /**
  * Message managers provide a way for chrome-privileged JS code to
  * communicate with each other, even across process boundaries.
  *
  * Message managers are separated into "parent side" and "child side".
@@ -393,16 +394,22 @@ interface nsIContentFrameMessageManager 
    * The current top level window in the frame or null.
    */
   readonly attribute mozIDOMWindowProxy content;
 
   /**
    * The top level docshell or null.
    */
   readonly attribute nsIDocShell docShell;
+
+  /**
+   * Returns the SchedulerEventTarget corresponding to the TabGroup
+   * for this frame.
+   */
+  readonly attribute nsIEventTarget tabEventTarget;
 };
 
 [uuid(b39a3324-b574-4f85-8cdb-274d04f807ef)]
 interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager
 {
   [notxpcom] nsIContent getOwnerContent();
   [notxpcom] void cacheFrameLoader(in nsIFrameLoader aFrameLoader);
 };
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -18,16 +18,17 @@
 #include "nsPropertyTable.h"        // for typedefs
 #include "nsTObserverArray.h"       // for member
 #include "mozilla/ErrorResult.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/EventTarget.h" // for base class
 #include "js/TypeDecls.h"     // for Handle, Value, JSObject, JSContext
 #include "mozilla/dom/DOMString.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "nsTHashtable.h"
 #include <iosfwd>
 
 // Including 'windows.h' will #define GetClassInfo to something else.
 #ifdef XP_WIN
 #ifdef GetClassInfo
 #undef GetClassInfo
 #endif
 #endif
@@ -46,16 +47,17 @@ class nsIMutationObserver;
 class nsINode;
 class nsINodeList;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIURI;
 class nsNodeSupportsWeakRefTearoff;
 class nsNodeWeakReference;
 class nsDOMMutationObserver;
+class nsRange;
 
 namespace mozilla {
 class EventListenerManager;
 class TextEditor;
 namespace dom {
 /**
  * @return true if aChar is what the WHATWG defines as a 'ascii whitespace'.
  * https://infra.spec.whatwg.org/#ascii-whitespace
@@ -1104,16 +1106,22 @@ public:
 
     /**
      * Weak reference to this node.  This is cleared by the destructor of
      * nsNodeWeakReference.
      */
     nsNodeWeakReference* MOZ_NON_OWNING_REF mWeakReference;
 
     /**
+     * A set of ranges in the common ancestor for the selection to which
+     * this node belongs to.
+     */
+    mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>> mCommonAncestorRanges;
+
+    /**
      * Number of descendant nodes in the uncomposed document that have been
      * explicitly set as editable.
      */
     uint32_t mEditableDescendantCount;
   };
 
   /**
    * Functions for managing flags and slots
@@ -1893,16 +1901,37 @@ public:
                                                 CallerType aCallerType,
                                                 ErrorResult& aRv);
   already_AddRefed<DOMPoint> ConvertPointFromNode(const DOMPointInit& aPoint,
                                                   const TextOrElementOrDocument& aFrom,
                                                   const ConvertCoordinateOptions& aOptions,
                                                   CallerType aCallerType,
                                                   ErrorResult& aRv);
 
+  const nsTHashtable<nsPtrHashKey<nsRange>>* GetExistingCommonAncestorRanges() const
+  {
+    if (!HasSlots()) {
+      return nullptr;
+    }
+    mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& ranges =
+      GetExistingSlots()->mCommonAncestorRanges;
+    return ranges.get();
+  }
+
+  nsTHashtable<nsPtrHashKey<nsRange>>* GetExistingCommonAncestorRanges()
+  {
+    nsINode::nsSlots* slots = GetExistingSlots();
+    return slots ? slots->mCommonAncestorRanges.get() : nullptr;
+  }
+
+  mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& GetCommonAncestorRangesPtr()
+  {
+    return Slots()->mCommonAncestorRanges;
+  }
+
 protected:
 
   // Override this function to create a custom slots class.
   // Must not return null.
   virtual nsINode::nsSlots* CreateSlots();
 
   bool HasSlots() const
   {
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -194,16 +194,24 @@ nsInProcessTabChildGlobal::GetContent(mo
 
 NS_IMETHODIMP
 nsInProcessTabChildGlobal::GetDocShell(nsIDocShell** aDocShell)
 {
   NS_IF_ADDREF(*aDocShell = mDocShell);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsInProcessTabChildGlobal::GetTabEventTarget(nsIEventTarget** aTarget)
+{
+  nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget();
+  target.forget(aTarget);
+  return NS_OK;
+}
+
 void
 nsInProcessTabChildGlobal::FireUnloadEvent()
 {
   // We're called from nsDocument::MaybeInitializeFinalizeFrameLoaders, so it
   // should be safe to run script.
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   // Don't let the unload event propagate to chrome event handlers.
--- a/dom/base/nsInProcessTabChildGlobal.h
+++ b/dom/base/nsInProcessTabChildGlobal.h
@@ -72,16 +72,17 @@ public:
   {
     return mMessageManager
       ? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote,
                                         aPrincipal, aCx, aArgc, aRetval)
       : NS_ERROR_NULL_POINTER;
   }
   NS_IMETHOD GetContent(mozIDOMWindowProxy** aContent) override;
   NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) override;
+  NS_IMETHOD GetTabEventTarget(nsIEventTarget** aTarget) override;
 
   NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER
 
   /**
    * MessageManagerCallback methods that we override.
    */
   virtual bool DoSendBlockingMessage(JSContext* aCx,
                                       const nsAString& aMessage,
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2085,20 +2085,25 @@ CCRunnerFired(TimeStamp aDeadline, void*
   if (isLateTimerFire && ShouldTriggerCC(suspected)) {
     if (sCCRunnerFireCount == numEarlyTimerFires + 1) {
       FireForgetSkippable(suspected, true, aDeadline);
       didDoWork = true;
       if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
         // Our efforts to avoid a CC have failed, so we return to let the
         // timer fire once more to trigger a CC.
 
-        // Clear content unbinder before the first CC slice.
-        Element::ClearContentUnbinder();
-        // And trigger deferred deletion too.
-        nsCycleCollector_doDeferredDeletion();
+        if (!aDeadline.IsNull() && TimeStamp::Now() < aDeadline) {
+          // Clear content unbinder before the first CC slice.
+          Element::ClearContentUnbinder();
+
+          if (TimeStamp::Now() < aDeadline) {
+            // And trigger deferred deletion too.
+            nsCycleCollector_doDeferredDeletion();
+          }
+        }
         return didDoWork;
       }
     } else {
       // We are in the final timer fire and still meet the conditions for
       // triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if
       // any because that will allow us to include the GC time in the CC pause.
       nsJSContext::RunCycleCollectorSlice(aDeadline);
       didDoWork = true;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -767,18 +767,16 @@ protected:
 
   // The evidence that we have tried to cache mTopInnerWindow only once from
   // SetNewDocument(). Note: We need this extra flag because mTopInnerWindow
   // could be null and we don't want it to be set multiple times.
   bool mHasTriedToCacheTopInnerWindow;
 
   // The number of active IndexedDB databases. Inner window only.
   uint32_t mNumOfIndexedDBDatabases;
-
-  bool mCleanedUp;
 };
 
 #define NS_PIDOMWINDOWINNER_IID \
 { 0x775dabc9, 0x8f43, 0x4277, \
   { 0x9a, 0xdb, 0xf1, 0x99, 0x0d, 0x77, 0xcf, 0xfb } }
 
 #define NS_PIDOMWINDOWOUTER_IID \
   { 0x769693d4, 0xb009, 0x4fe2, \
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -200,18 +200,21 @@ nsRange::IsNodeSelected(nsINode* aNode, 
   NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
                "orphan selection descendant");
 
   // Collect the potential ranges and their selection objects.
   RangeHashTable ancestorSelectionRanges;
   nsTHashtable<nsPtrHashKey<Selection>> ancestorSelections;
   uint32_t maxRangeCount = 0;
   for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) {
-    RangeHashTable* ranges =
-      static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range));
+    nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
+      n->GetExistingCommonAncestorRanges();
+    if (!ranges) {
+      continue;
+    }
     for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) {
       nsRange* range = iter.Get()->GetKey();
       if (range->IsInSelection() && !range->Collapsed()) {
         ancestorSelectionRanges.PutEntry(range);
         Selection* selection = range->mSelection;
         ancestorSelections.PutEntry(selection);
         maxRangeCount = std::max(maxRangeCount, selection->RangeCount());
       }
@@ -403,39 +406,38 @@ static void UnmarkDescendants(nsINode* a
 void
 nsRange::RegisterCommonAncestor(nsINode* aNode)
 {
   NS_PRECONDITION(aNode, "bad arg");
   NS_ASSERTION(IsInSelection(), "registering range not in selection");
 
   MarkDescendants(aNode);
 
-  RangeHashTable* ranges =
-    static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
+  UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& ranges =
+    aNode->GetCommonAncestorRangesPtr();
   if (!ranges) {
-    ranges = new RangeHashTable;
-    aNode->SetProperty(nsGkAtoms::range, ranges,
-                       nsINode::DeleteProperty<nsRange::RangeHashTable>, true);
+    ranges = MakeUnique<nsRange::RangeHashTable>();
   }
   ranges->PutEntry(this);
   aNode->SetCommonAncestorForRangeInSelection();
 }
 
 void
 nsRange::UnregisterCommonAncestor(nsINode* aNode)
 {
   NS_PRECONDITION(aNode, "bad arg");
   NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
-  RangeHashTable* ranges =
-    static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
+  nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
+    aNode->GetExistingCommonAncestorRanges();
+  MOZ_ASSERT(ranges);
   NS_ASSERTION(ranges->GetEntry(this), "unknown range");
 
   if (ranges->Count() == 1) {
     aNode->ClearCommonAncestorForRangeInSelection();
-    aNode->DeleteProperty(nsGkAtoms::range);
+    aNode->GetCommonAncestorRangesPtr().reset();
     UnmarkDescendants(aNode);
   } else {
     ranges->RemoveEntry(this);
   }
 }
 
 /******************************************************
  * nsIMutationObserver implementation
@@ -3391,18 +3393,18 @@ nsRange::GetUsedFontFaces(nsIDOMFontFace
 
 nsINode*
 nsRange::GetRegisteredCommonAncestor()
 {
   NS_ASSERTION(IsInSelection(),
                "GetRegisteredCommonAncestor only valid for range in selection");
   nsINode* ancestor = GetNextRangeCommonAncestor(mStartContainer);
   while (ancestor) {
-    RangeHashTable* ranges =
-      static_cast<RangeHashTable*>(ancestor->GetProperty(nsGkAtoms::range));
+    nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
+      ancestor->GetExistingCommonAncestorRanges();
     if (ranges->GetEntry(this)) {
       break;
     }
     ancestor = GetNextRangeCommonAncestor(ancestor->GetParentNode());
   }
   NS_ASSERTION(ancestor, "can't find common ancestor for selected range");
   return ancestor;
 }
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -388,20 +388,20 @@ protected:
   // CharacterDataChanged does the re-registering when needed.
   void DoSetRange(nsINode* aStartN, uint32_t aStartOffset,
                   nsINode* aEndN, uint32_t aEndOffset,
                   nsINode* aRoot, bool aNotInsertedYet = false);
 
   /**
    * For a range for which IsInSelection() is true, return the common
    * ancestor for the range.  This method uses the selection bits and
-   * nsGkAtoms::range property on the nodes to quickly find the ancestor.
-   * That is, it's a faster version of GetCommonAncestor that only works
-   * for ranges in a Selection.  The method will assert and the behavior
-   * is undefined if called on a range where IsInSelection() is false.
+   * node slots to quickly find the ancestor.  That is, it's a faster
+   * version of GetCommonAncestor that only works for ranges in a
+   * Selection.  The method will assert and the behavior is undefined if
+   * called on a range where IsInSelection() is false.
    */
   nsINode* GetRegisteredCommonAncestor();
 
   // Helper to IsNodeSelected.
   static bool IsNodeInSortedRanges(nsINode* aNode,
                                    uint32_t aStartOffset,
                                    uint32_t aEndOffset,
                                    const nsTArray<const nsRange*>& aRanges,
--- a/dom/base/nsTextNode.cpp
+++ b/dom/base/nsTextNode.cpp
@@ -160,19 +160,18 @@ void
 nsTextNode::List(FILE* out, int32_t aIndent) const
 {
   int32_t index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
   fprintf(out, "Text@%p", static_cast<const void*>(this));
   fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
   if (IsCommonAncestorForRangeInSelection()) {
-    typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
-    RangeHashTable* ranges =
-      static_cast<RangeHashTable*>(GetProperty(nsGkAtoms::range));
+    const nsTHashtable<nsPtrHashKey<nsRange>>* ranges =
+      GetExistingCommonAncestorRanges();
     fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0);
   }
   fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
   fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
 
   nsAutoString tmp;
   ToCString(tmp, 0, mText.GetLength());
   fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -4103,19 +4103,19 @@ public:
   WidgetMouseEvent* mMouseEvent;
   EventMessage mEventMessage;
 };
 
 void
 EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent,
                                   nsIContent* aMovingInto)
 {
-  OverOutElementsWrapper* wrapper = GetWrapperByEventID(aMouseEvent);
-
-  if (!wrapper->mLastOverElement)
+  RefPtr<OverOutElementsWrapper> wrapper = GetWrapperByEventID(aMouseEvent);
+
+  if (!wrapper || !wrapper->mLastOverElement)
     return;
   // Before firing mouseout, check for recursion
   if (wrapper->mLastOverElement == wrapper->mFirstOutEventElement)
     return;
 
   if (wrapper->mLastOverFrame) {
     // if the frame is associated with a subdocument,
     // tell the subdocument that we're moving out of it
@@ -4171,19 +4171,19 @@ EventStateManager::NotifyMouseOut(Widget
 }
 
 void
 EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent,
                                    nsIContent* aContent)
 {
   NS_ASSERTION(aContent, "Mouse must be over something");
 
-  OverOutElementsWrapper* wrapper = GetWrapperByEventID(aMouseEvent);
-
-  if (wrapper->mLastOverElement == aContent)
+  RefPtr<OverOutElementsWrapper> wrapper = GetWrapperByEventID(aMouseEvent);
+
+  if (!wrapper || wrapper->mLastOverElement == aContent)
     return;
 
   // Before firing mouseover, check for recursion
   if (aContent == wrapper->mFirstOverEventElement)
     return;
 
   // Check to see if we're a subdocument and if so update the parent
   // document's ESM state to indicate that the mouse is over the
@@ -4343,33 +4343,33 @@ EventStateManager::GenerateMouseEnterExi
       nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent);
       if (!targetElement) {
         // We're always over the document root, even if we're only
         // over dead space in a page (whose frame is not associated with
         // any content) or in print preview dead space
         targetElement = mDocument->GetRootElement();
       }
       if (targetElement) {
-        OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent);
+        RefPtr<OverOutElementsWrapper> helper = GetWrapperByEventID(aMouseEvent);
         if (helper) {
           helper->mLastOverElement = targetElement;
         }
         NotifyMouseOut(aMouseEvent, nullptr);
       }
     }
     break;
   case ePointerLeave:
   case ePointerCancel:
   case eMouseExitFromWidget:
     {
       // This is actually the window mouse exit or pointer leave event. We're not moving
       // into any new element.
 
-      OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent);
-      if (helper->mLastOverFrame &&
+      RefPtr<OverOutElementsWrapper> helper = GetWrapperByEventID(aMouseEvent);
+      if (helper && helper->mLastOverFrame &&
           nsContentUtils::GetTopLevelWidget(aMouseEvent->mWidget) !=
           nsContentUtils::GetTopLevelWidget(helper->mLastOverFrame->GetNearestWidget())) {
         // the Mouse/PointerOut event widget doesn't have same top widget with
         // mLastOverFrame, it's a spurious event for mLastOverFrame
         break;
       }
 
       // Reset sLastRefPoint, so that we'll know not to report any
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3515,16 +3515,24 @@ TabChildGlobal::GetDocShell(nsIDocShell*
   *aDocShell = nullptr;
   if (!mTabChild)
     return NS_ERROR_NULL_POINTER;
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mTabChild->WebNavigation());
   docShell.swap(*aDocShell);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+TabChildGlobal::GetTabEventTarget(nsIEventTarget** aTarget)
+{
+  nsCOMPtr<nsIEventTarget> target = EventTargetFor(TaskCategory::Other);
+  target.forget(aTarget);
+  return NS_OK;
+}
+
 nsIPrincipal*
 TabChildGlobal::GetPrincipal()
 {
   if (!mTabChild)
     return nullptr;
   return mTabChild->GetPrincipal();
 }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -109,16 +109,17 @@ public:
   {
     return mMessageManager
       ? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote,
                                         aPrincipal, aCx, aArgc, aRetval)
       : NS_ERROR_NULL_POINTER;
   }
   NS_IMETHOD GetContent(mozIDOMWindowProxy** aContent) override;
   NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) override;
+  NS_IMETHOD GetTabEventTarget(nsIEventTarget** aTarget) override;
 
   nsresult AddEventListener(const nsAString& aType,
                             nsIDOMEventListener* aListener,
                             bool aUseCapture)
   {
     // By default add listeners only for trusted events!
     return DOMEventTargetHelper::AddEventListener(aType, aListener,
                                                   aUseCapture, false, 2);
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -379,17 +379,21 @@ PaymentRequest::Show(ErrorResult& aRv)
   RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
   if (NS_WARN_IF(!manager)) {
     mState = eClosed;
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
   nsresult rv = manager->ShowPayment(mInternalId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    promise->MaybeReject(NS_ERROR_FAILURE);
+    if (rv == NS_ERROR_ABORT) {
+      promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    } else {
+      promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
+    }
     mState = eClosed;
     return promise.forget();
   }
 
   mAcceptPromise = promise;
   mState = eInteractive;
   return promise.forget();
 }
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -485,25 +485,29 @@ PaymentRequestManager::CanMakePayment(co
   IPCPaymentCanMakeActionRequest action(requestId);
 
   return SendRequestPayment(request, action);
 }
 
 nsresult
 PaymentRequestManager::ShowPayment(const nsAString& aRequestId)
 {
+  if (mShowingRequest) {
+    return NS_ERROR_ABORT;
+  }
   RefPtr<PaymentRequest> request = GetPaymentRequestById(aRequestId);
   if (!request) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoString requestId(aRequestId);
   IPCPaymentShowActionRequest action(requestId);
-
-  return SendRequestPayment(request, action);
+  nsresult rv = SendRequestPayment(request, action);
+  mShowingRequest = request;
+  return rv;
 }
 
 nsresult
 PaymentRequestManager::AbortPayment(const nsAString& aRequestId)
 {
   RefPtr<PaymentRequest> request = GetPaymentRequestById(aRequestId);
   if (!request) {
     return NS_ERROR_FAILURE;
@@ -588,47 +592,53 @@ PaymentRequestManager::RespondPayment(co
       }
       request->RespondShowPayment(response.isAccepted(),
                                   response.methodName(),
                                   response.data(),
                                   response.payerName(),
                                   response.payerEmail(),
                                   response.payerPhone());
       if (!response.isAccepted()) {
+        MOZ_ASSERT(mShowingRequest == request);
+        mShowingRequest = nullptr;
         mRequestQueue.RemoveElement(request);
         nsresult rv = ReleasePaymentChild(request);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       break;
     }
     case IPCPaymentActionResponse::TIPCPaymentAbortActionResponse: {
       const IPCPaymentAbortActionResponse& response = aResponse;
       RefPtr<PaymentRequest> request = GetPaymentRequestById(response.requestId());
       if (NS_WARN_IF(!request)) {
         return NS_ERROR_FAILURE;
       }
       request->RespondAbortPayment(response.isSucceeded());
       if (response.isSucceeded()) {
+        MOZ_ASSERT(mShowingRequest == request);
+        mShowingRequest = nullptr;
         mRequestQueue.RemoveElement(request);
         nsresult rv = ReleasePaymentChild(request);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       break;
     }
     case IPCPaymentActionResponse::TIPCPaymentCompleteActionResponse: {
       const IPCPaymentCompleteActionResponse& response = aResponse;
       RefPtr<PaymentRequest> request = GetPaymentRequestById(response.requestId());
       if (NS_WARN_IF(!request)) {
         return NS_ERROR_FAILURE;
       }
       request->RespondComplete();
+      MOZ_ASSERT(mShowingRequest == request);
+      mShowingRequest = nullptr;
       mRequestQueue.RemoveElement(request);
       nsresult rv = ReleasePaymentChild(request);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       break;
     }
     default: {
--- a/dom/payments/PaymentRequestManager.h
+++ b/dom/payments/PaymentRequestManager.h
@@ -74,14 +74,15 @@ private:
 
   nsresult SendRequestPayment(PaymentRequest* aRequest,
                               const IPCPaymentActionRequest& action,
                               bool aReleaseAfterSend = false);
 
   // The container for the created PaymentRequests
   nsTArray<RefPtr<PaymentRequest>> mRequestQueue;
   nsRefPtrHashtable<nsRefPtrHashKey<PaymentRequest>, PaymentRequestChild> mPaymentChildHash;
+  RefPtr<PaymentRequest> mShowingRequest;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
 #endif
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -301,21 +301,56 @@ PaymentRequestService::RequestPayment(ns
     }
     /*
      *  TODO: 1. Check basic card support once the Basic Card Payment spec is
      *           implemented.
      *           https://www.w3.org/TR/payment-method-basic-card/
      *        2. Check third party payment app support by traversing all
      *           registered third party payment apps.
      */
-    case nsIPaymentActionRequest::CANMAKE_ACTION:
+    case nsIPaymentActionRequest::CANMAKE_ACTION: {
+      rv = CallTestingUIAction(requestId, type);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return NS_ERROR_FAILURE;
+      }
+      break;
+    }
     /*
      *  TODO: Launch/inform payment UI here once the UI module is implemented.
      */
-    case nsIPaymentActionRequest::SHOW_ACTION:
+    case nsIPaymentActionRequest::SHOW_ACTION: {
+      if (mShowingRequest) {
+        nsCOMPtr<nsIPaymentShowActionResponse> showResponse =
+          do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID);
+        MOZ_ASSERT(showResponse);
+        rv = showResponse->Init(requestId,
+                                nsIPaymentActionResponse::PAYMENT_REJECTED,
+                                EmptyString(),
+                                EmptyString(),
+                                EmptyString(),
+                                EmptyString(),
+                                EmptyString());
+        nsCOMPtr<nsIPaymentActionResponse> response = do_QueryInterface(showResponse);
+        MOZ_ASSERT(response);
+        rv = RespondPayment(response);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+      } else {
+        rv = GetPaymentRequestById(requestId, getter_AddRefs(mShowingRequest));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NS_ERROR_FAILURE;
+        }
+        rv = CallTestingUIAction(requestId, type);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NS_ERROR_FAILURE;
+        }
+      }
+      break;
+    }
     case nsIPaymentActionRequest::ABORT_ACTION:
     case nsIPaymentActionRequest::COMPLETE_ACTION: {
       rv = CallTestingUIAction(requestId, type);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
@@ -386,21 +421,36 @@ PaymentRequestService::RespondPayment(ns
     case nsIPaymentActionResponse::ABORT_ACTION: {
       nsCOMPtr<nsIPaymentAbortActionResponse> response =
         do_QueryInterface(aResponse);
       MOZ_ASSERT(response);
       bool isSucceeded;
       rv = response->IsSucceeded(&isSucceeded);
       NS_ENSURE_SUCCESS(rv, rv);
       if (isSucceeded) {
+        mShowingRequest = nullptr;
+        mRequestQueue.RemoveElement(request);
+      }
+      break;
+    }
+    case nsIPaymentActionResponse::SHOW_ACTION: {
+      nsCOMPtr<nsIPaymentShowActionResponse> response =
+        do_QueryInterface(aResponse);
+      MOZ_ASSERT(response);
+      bool isAccepted;
+      rv = response->IsAccepted(&isAccepted);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (!isAccepted) {
+        mShowingRequest = nullptr;
         mRequestQueue.RemoveElement(request);
       }
       break;
     }
     case nsIPaymentActionResponse::COMPLETE_ACTION: {
+      mShowingRequest = nullptr;
       mRequestQueue.RemoveElement(request);
       break;
     }
     default: {
       break;
     }
   }
   return NS_OK;
--- a/dom/payments/PaymentRequestService.h
+++ b/dom/payments/PaymentRequestService.h
@@ -46,14 +46,16 @@ private:
   nsresult
   CallTestingUIAction(const nsAString& aRequestId, uint32_t aActionType);
 
   FallibleTArray<nsCOMPtr<nsIPaymentRequest>> mRequestQueue;
 
   nsInterfaceHashtable<nsStringHashKey, nsIPaymentActionCallback> mCallbackHashtable;
 
   nsCOMPtr<nsIPaymentUIService> mTestingUIService;
+
+  nsCOMPtr<nsIPaymentRequest> mShowingRequest;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
 #endif
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -101,17 +101,17 @@ NS_IMPL_RELEASE_INHERITED(Performance, D
 /* static */ already_AddRefed<Performance>
 Performance::CreateForMainThread(nsPIDOMWindowInner* aWindow,
                                  nsDOMNavigationTiming* aDOMTiming,
                                  nsITimedChannel* aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<Performance> performance =
-    PerformanceMainThread::Create(aWindow, aDOMTiming, aChannel);
+    new PerformanceMainThread(aWindow, aDOMTiming, aChannel);
   return performance.forget();
 }
 
 /* static */ already_AddRefed<Performance>
 Performance::CreateForWorker(workers::WorkerPrivate* aWorkerPrivate)
 {
   MOZ_ASSERT(aWorkerPrivate);
   aWorkerPrivate->AssertIsOnWorkerThread();
@@ -272,23 +272,28 @@ Performance::RoundTime(double aTime) con
 void
 Performance::Mark(const nsAString& aName, ErrorResult& aRv)
 {
   // We add nothing when 'privacy.resistFingerprinting' is on.
   if (nsContentUtils::ShouldResistFingerprinting()) {
     return;
   }
 
+  // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003.
+  if (mUserEntries.Length() >= mResourceTimingBufferSize) {
+    return;
+  }
+
   if (IsPerformanceTimingAttribute(aName)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   RefPtr<PerformanceMark> performanceMark =
-    new PerformanceMark(GetParentObject(), aName, Now());
+    new PerformanceMark(GetAsISupports(), aName, Now());
   InsertUserEntry(performanceMark);
 
   if (profiler_is_active()) {
     profiler_add_marker(
       "UserTiming",
       MakeUnique<UserTimingMarkerPayload>(aName, TimeStamp::Now()));
   }
 }
@@ -334,16 +339,22 @@ Performance::Measure(const nsAString& aN
                      const Optional<nsAString>& aEndMark,
                      ErrorResult& aRv)
 {
   // We add nothing when 'privacy.resistFingerprinting' is on.
   if (nsContentUtils::ShouldResistFingerprinting()) {
     return;
   }
 
+  // Don't add the entry if the buffer is full. XXX should be removed by bug
+  // 1159003.
+  if (mUserEntries.Length() >= mResourceTimingBufferSize) {
+    return;
+  }
+
   DOMHighResTimeStamp startTime;
   DOMHighResTimeStamp endTime;
 
   if (IsPerformanceTimingAttribute(aName)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
@@ -364,17 +375,17 @@ Performance::Measure(const nsAString& aN
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
   } else {
     endTime = Now();
   }
 
   RefPtr<PerformanceMeasure> performanceMeasure =
-    new PerformanceMeasure(GetParentObject(), aName, startTime, endTime);
+    new PerformanceMeasure(GetAsISupports(), aName, startTime, endTime);
   InsertUserEntry(performanceMeasure);
 
   if (profiler_is_active()) {
     TimeStamp startTimeStamp = CreationTimeStamp() +
                                TimeDuration::FromMilliseconds(startTime);
     TimeStamp endTimeStamp = CreationTimeStamp() +
                              TimeDuration::FromMilliseconds(endTime);
     profiler_add_marker(
@@ -562,16 +573,10 @@ Performance::IsObserverEnabled(JSContext
 
   RefPtr<PrefEnabledRunnable> runnable =
     new PrefEnabledRunnable(workerPrivate,
                             NS_LITERAL_CSTRING("dom.enable_performance_observer"));
 
   return runnable->Dispatch() && runnable->IsEnabled();
 }
 
-void
-Performance::MemoryPressure()
-{
-  mUserEntries.Clear();
-}
-
 } // dom namespace
 } // mozilla namespace
--- a/dom/performance/Performance.h
+++ b/dom/performance/Performance.h
@@ -96,36 +96,33 @@ public:
 
   virtual void GetMozMemory(JSContext *aCx,
                             JS::MutableHandle<JSObject*> aObj) = 0;
 
   virtual nsDOMNavigationTiming* GetDOMTiming() const = 0;
 
   virtual nsITimedChannel* GetChannel() const = 0;
 
-  void MemoryPressure();
-
-  // This method is currently called only on the main-thread.
-  virtual void Shutdown() {}
-
 protected:
   Performance();
   explicit Performance(nsPIDOMWindowInner* aWindow);
 
   virtual ~Performance();
 
   virtual void InsertUserEntry(PerformanceEntry* aEntry);
   void InsertResourceEntry(PerformanceEntry* aEntry);
 
   void ClearUserEntries(const Optional<nsAString>& aEntryName,
                         const nsAString& aEntryType);
 
   DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName,
                                                ErrorResult& aRv);
 
+  virtual nsISupports* GetAsISupports() = 0;
+
   virtual void DispatchBufferFullEvent() = 0;
 
   virtual TimeStamp CreationTimeStamp() const = 0;
 
   virtual DOMHighResTimeStamp CreationTime() const = 0;
 
   virtual bool IsPerformanceTimingAttribute(const nsAString& aName)
   {
--- a/dom/performance/PerformanceMainThread.cpp
+++ b/dom/performance/PerformanceMainThread.cpp
@@ -2,17 +2,16 @@
 /* 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 "PerformanceMainThread.h"
 #include "PerformanceNavigation.h"
 #include "nsICacheInfoChannel.h"
-#include "nsISupportsPrimitives.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
                                                 Performance)
@@ -34,50 +33,28 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INH
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_ADDREF_INHERITED(PerformanceMainThread, Performance)
 NS_IMPL_RELEASE_INHERITED(PerformanceMainThread, Performance)
 
 // QueryInterface implementation for PerformanceMainThread
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMainThread)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END_INHERITING(Performance)
 
-already_AddRefed<PerformanceMainThread>
-PerformanceMainThread::Create(nsPIDOMWindowInner* aWindow,
-                              nsDOMNavigationTiming* aDOMTiming,
-                              nsITimedChannel* aChannel)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aWindow, "Parent window object should be provided");
-
-  RefPtr<PerformanceMainThread> performance =
-    new PerformanceMainThread(aWindow, aDOMTiming, aChannel);
-
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (NS_WARN_IF(!obs)) {
-    return nullptr;
-  }
-
-  nsresult rv = obs->AddObserver(performance, "memory-pressure", false);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return nullptr;
-  }
-
-  return performance.forget();
-}
-
 PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow,
                                              nsDOMNavigationTiming* aDOMTiming,
                                              nsITimedChannel* aChannel)
   : Performance(aWindow)
   , mDOMTiming(aDOMTiming)
   , mChannel(aChannel)
-{}
+{
+  MOZ_ASSERT(aWindow, "Parent window object should be provided");
+}
 
 PerformanceMainThread::~PerformanceMainThread()
 {
   mozilla::DropJSObjects(this);
 }
 
 void
 PerformanceMainThread::GetMozMemory(JSContext *aCx,
@@ -355,33 +332,10 @@ PerformanceMainThread::CreationTimeStamp
 }
 
 DOMHighResTimeStamp
 PerformanceMainThread::CreationTime() const
 {
   return GetDOMTiming()->GetNavigationStart();
 }
 
-NS_IMETHODIMP
-PerformanceMainThread::Observe(nsISupports* aSubject, const char* aTopic,
-                               const char16_t* aData)
-{
-  AssertIsOnMainThread();
-
-  if (!strcmp(aTopic, "memory-pressure")) {
-    MemoryPressure();
-    return NS_OK;
-  }
-
-  return NS_OK;
-}
-
-void
-PerformanceMainThread::Shutdown()
-{
-  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    obs->RemoveObserver(this, "memory-pressure");
-  }
-}
-
 } // dom namespace
 } // mozilla namespace
--- a/dom/performance/PerformanceMainThread.h
+++ b/dom/performance/PerformanceMainThread.h
@@ -8,28 +8,26 @@
 #define mozilla_dom_PerformanceMainThread_h
 
 #include "Performance.h"
 
 namespace mozilla {
 namespace dom {
 
 class PerformanceMainThread final : public Performance
-                                  , public nsIObserver
 {
 public:
+  PerformanceMainThread(nsPIDOMWindowInner* aWindow,
+                        nsDOMNavigationTiming* aDOMTiming,
+                        nsITimedChannel* aChannel);
+
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIOBSERVER
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PerformanceMainThread,
                                                          Performance)
 
-  static already_AddRefed<PerformanceMainThread>
-  Create(nsPIDOMWindowInner* aWindow, nsDOMNavigationTiming* aDOMTiming,
-         nsITimedChannel* aChannel);
-
   virtual PerformanceTiming* Timing() override;
 
   virtual PerformanceNavigation* Navigation() override;
 
   virtual void AddEntry(nsIHttpChannel* channel,
                         nsITimedChannel* timedChannel) override;
 
   TimeStamp CreationTimeStamp() const override;
@@ -44,24 +42,23 @@ public:
     return mDOMTiming;
   }
 
   virtual nsITimedChannel* GetChannel() const override
   {
     return mChannel;
   }
 
-  void Shutdown() override;
+protected:
+  ~PerformanceMainThread();
 
-protected:
-  PerformanceMainThread(nsPIDOMWindowInner* aWindow,
-                        nsDOMNavigationTiming* aDOMTiming,
-                        nsITimedChannel* aChannel);
-
-  ~PerformanceMainThread();
+  nsISupports* GetAsISupports() override
+  {
+    return this;
+  }
 
   void InsertUserEntry(PerformanceEntry* aEntry) override;
 
   bool IsPerformanceTimingAttribute(const nsAString& aName) override;
 
   DOMHighResTimeStamp
   GetPerformanceTimingFromString(const nsAString& aTimingName) override;
 
--- a/dom/performance/PerformanceWorker.h
+++ b/dom/performance/PerformanceWorker.h
@@ -59,16 +59,21 @@ public:
   {
     MOZ_CRASH("This should not be called on workers.");
     return nullptr;
   }
 
 protected:
   ~PerformanceWorker();
 
+  nsISupports* GetAsISupports() override
+  {
+    return nullptr;
+  }
+
   void InsertUserEntry(PerformanceEntry* aEntry) override;
 
   void DispatchBufferFullEvent() override
   {
     MOZ_CRASH("This should not be called on workers.");
   }
 
 private:
--- a/dom/plugins/base/nsPluginsDirWin.cpp
+++ b/dom/plugins/base/nsPluginsDirWin.cpp
@@ -419,14 +419,14 @@ nsresult nsPluginFile::FreePluginInfo(ns
 
   if (info.fFullPath)
     PL_strfree(info.fFullPath);
 
   if (info.fFileName)
     PL_strfree(info.fFileName);
 
   if (info.fVersion)
-    mozilla::SmprintfFree(info.fVersion);
+    free(info.fVersion);
 
   ZeroMemory((void *)&info, sizeof(info));
 
   return NS_OK;
 }
--- a/dom/power/PowerManagerService.cpp
+++ b/dom/power/PowerManagerService.cpp
@@ -28,17 +28,17 @@
 #include <android/log.h>
 extern "C" char* PrintJSStack();
 static void LogFunctionAndJSStack(const char* funcname) {
   char *jsstack = PrintJSStack();
   __android_log_print(ANDROID_LOG_INFO, "PowerManagerService", \
                       "Call to %s. The JS stack is:\n%s\n",
                       funcname,
                       jsstack ? jsstack : "<no JS stack>");
-  JS_smprintf_free(jsstack);
+  js_free(jsstack);
 }
 // bug 839452
 #define LOG_FUNCTION_AND_JS_STACK() \
   LogFunctionAndJSStack(__PRETTY_FUNCTION__);
 #else
 #define LOG_FUNCTION_AND_JS_STACK()
 #endif
 
--- a/dom/tests/mochitest/chrome/chrome.ini
+++ b/dom/tests/mochitest/chrome/chrome.ini
@@ -17,16 +17,17 @@ support-files =
   file_bug1224790-2_nonmodal.xul
   file_subscript_bindings.js
   focus_frameset.html
   focus_window2.xul
   fullscreen.xul
   queryCaretRectUnix.html
   queryCaretRectWin.html
   selectAtPoint.html
+  selectAtPoint-innerframe.html
   sizemode_attribute.xul
   window_activation.xul
   window_callback_wrapping.xul
   window_docshell_swap.xul
   window_focus.xul
   window_focus_docnav.xul
   !/dom/tests/mochitest/general/file_clonewrapper.html
   !/dom/tests/mochitest/general/file_moving_nodeList.html
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/chrome/selectAtPoint-innerframe.html
@@ -0,0 +1,6 @@
+<html>
+<body style='margin: 0; padding: 0; font-family: monospace;' onload='window.parent.onFrameLoad();'>
+<div id='sel2'>ttestselection2 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut.</div>
+<br/><br/>
+</body>
+</html>">
--- a/dom/tests/mochitest/chrome/selectAtPoint.html
+++ b/dom/tests/mochitest/chrome/selectAtPoint.html
@@ -262,17 +262,17 @@ body {
 </style>
 </head>
 <body id="body" onload="onPageLoad();">
 
 <div id="div1">ttestselection1 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut. Ne labore incorrupte vix. Cu copiosae postulant tincidunt ius, in illud appetere contentiones eos. Ei munere officiis assentior pro, nibh decore ius at.</div>
 
 <br />
 
-<iframe id="frame1" src="data:text/html,<html><body style='margin: 0; padding: 0; font-family: monospace;' onload='window.parent.onFrameLoad();'><div id='sel2'>ttestselection2 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut.</div><br/><br/></body></html>"></iframe>
+<iframe id="frame1" src="selectAtPoint-innerframe.html"></iframe>
 
 <br/>
 
 <div id="div2">Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut. Ne labore incorrupte vix. Cu copiosae postulant tincidunt ius, in illud appetere contentiones eos.</div>
 
 <br />
 
 <span id="measure">t</span>
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -6770,33 +6770,24 @@ WorkerPrivate::CycleCollectInternal(bool
   }
 }
 
 void
 WorkerPrivate::MemoryPressureInternal()
 {
   AssertIsOnWorkerThread();
 
-  if (mScope) {
-    RefPtr<Console> console = mScope->GetConsoleIfExists();
-    if (console) {
-      console->ClearStorage();
-    }
-
-    RefPtr<Performance> performance = mScope->GetPerformanceIfExists();
-    if (performance) {
-      performance->MemoryPressure();
-    }
-  }
-
-  if (mDebuggerScope) {
-    RefPtr<Console> console = mDebuggerScope->GetConsoleIfExists();
-    if (console) {
-      console->ClearStorage();
-    }
+  RefPtr<Console> console = mScope ? mScope->GetConsoleIfExists() : nullptr;
+  if (console) {
+    console->ClearStorage();
+  }
+
+  console = mDebuggerScope ? mDebuggerScope->GetConsoleIfExists() : nullptr;
+  if (console) {
+    console->ClearStorage();
   }
 
   for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
     mChildWorkers[index]->MemoryPressure(false);
   }
 }
 
 void
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -151,21 +151,16 @@ public:
   IMPL_EVENT_HANDLER(online)
   IMPL_EVENT_HANDLER(offline)
 
   void
   Dump(const Optional<nsAString>& aString) const;
 
   Performance* GetPerformance();
 
-  Performance* GetPerformanceIfExists() const
-  {
-    return mPerformance;
-  }
-
   already_AddRefed<Promise>
   Fetch(const RequestOrUSVString& aInput, const RequestInit& aInit,
         CallerType aCallerType, ErrorResult& aRv);
 
   already_AddRefed<IDBFactory>
   GetIndexedDB(ErrorResult& aErrorResult);
 
   already_AddRefed<cache::CacheStorage>
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1100,16 +1100,23 @@ CompositorD3D11::NormalDrawingDone()
 void
 CompositorD3D11::EndFrame()
 {
   if (!mDefaultRT) {
     Compositor::EndFrame();
     return;
   }
 
+  if (XRE_IsParentProcess() && mDevice->GetDeviceRemovedReason() != S_OK) {
+    gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed.";
+    Compositor::EndFrame();
+    mCurrentRT = nullptr;
+    return;
+  }
+
   LayoutDeviceIntSize oldSize = mSize;
   EnsureSize();
   if (mSize.width <= 0 || mSize.height <= 0) {
     Compositor::EndFrame();
     return;
   }
 
   RefPtr<ID3D11Query> query;
--- a/gfx/layers/d3d11/MLGDeviceD3D11.cpp
+++ b/gfx/layers/d3d11/MLGDeviceD3D11.cpp
@@ -1646,47 +1646,47 @@ MLGDeviceD3D11::DrawInstanced(uint32_t a
                               uint32_t aVertexOffset, uint32_t aInstanceOffset)
 {
   mCtx->DrawInstanced(aVertexCountPerInstance, aInstanceCount, aVertexOffset, aInstanceOffset);
 }
 
 void
 MLGDeviceD3D11::SetPSTextures(uint32_t aSlot, uint32_t aNumTextures, TextureSource* const* aTextures)
 {
-  StackArray<ID3D11ShaderResourceView*, 2> textures(aNumTextures);
+  // TextureSource guarantees that the ID3D11ShaderResourceView will be cached,
+  // so we don't hold a RefPtr here.
+  StackArray<ID3D11ShaderResourceView*, 3> views(aNumTextures);
 
   for (size_t i = 0; i < aNumTextures; i++) {
-    if (!aTextures[i]) {
-      gfxWarning() << "Null TextureRef in SetPSTextures";
-      continue;
+    views[i] = ResolveTextureSourceForShader(aTextures[i]);
+  }
+
+  mCtx->PSSetShaderResources(aSlot, aNumTextures, views.data());
+}
+
+ID3D11ShaderResourceView*
+MLGDeviceD3D11::ResolveTextureSourceForShader(TextureSource* aTexture)
+{
+  if (!aTexture) {
+    return nullptr;
+  }
+
+  if (TextureSourceD3D11* source = aTexture->AsSourceD3D11()) {
+    ID3D11Texture2D* texture = source->GetD3D11Texture();
+    if (!texture) {
+      gfxWarning() << "No D3D11 texture present in SetPSTextures";
+      return nullptr;
     }
 
-    ID3D11ShaderResourceView* view = nullptr;
-    if (TextureSourceD3D11* source = aTextures[i]->AsSourceD3D11()) {
-      ID3D11Texture2D* texture = source->GetD3D11Texture();
-      if (!texture) {
-        gfxWarning() << "No D3D11 texture present in SetPSTextures";
-        continue;
-      }
-      MaybeLockTexture(texture);
-
-      view = source->GetShaderResourceView();
-    } else {
-      gfxWarning() << "Unknown texture type in SetPSTextures";
-      continue;
-    }
-
-    if (!view) {
-      gfxWarning() << "Failed to get shader resource view for texture";
-      continue;
-    }
-    textures[i] = view;
+    MaybeLockTexture(texture);
+    return source->GetShaderResourceView();
   }
 
-  mCtx->PSSetShaderResources(aSlot, aNumTextures, textures.data());
+  gfxWarning() << "Unknown texture type in SetPSTextures";
+  return nullptr;
 }
 
 void
 MLGDeviceD3D11::SetPSTexture(uint32_t aSlot, MLGTexture* aTexture)
 {
   RefPtr<ID3D11ShaderResourceView> view;
   if (aTexture) {
     MLGTextureD3D11* texture = aTexture->AsD3D11();
--- a/gfx/layers/d3d11/MLGDeviceD3D11.h
+++ b/gfx/layers/d3d11/MLGDeviceD3D11.h
@@ -272,16 +272,20 @@ private:
   bool InitSamplerStates();
   bool InitBlendStates();
   bool InitDepthStencilState();
   bool VerifyConstantBufferOffsetting() override;
 
   void SetInputLayout(ID3D11InputLayout* aLayout);
   void SetVertexShader(ID3D11VertexShader* aShader);
 
+  // Resolve a TextureSource to an ID3D11ShaderResourceView, locking the
+  // texture if needed. The lock is released at the end of the frame.
+  ID3D11ShaderResourceView* ResolveTextureSourceForShader(TextureSource* aSource);
+
 private:
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11DeviceContext> mCtx;
   RefPtr<ID3D11DeviceContext1> mCtx1;
   UniquePtr<DiagnosticsD3D11> mDiagnostics;
 
   typedef EnumeratedArray<PixelShaderID, PixelShaderID::MaxShaders, RefPtr<ID3D11PixelShader>> PixelShaderArray;
   typedef EnumeratedArray<VertexShaderID, VertexShaderID::MaxShaders, RefPtr<ID3D11VertexShader>> VertexShaderArray;
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1657,17 +1657,17 @@ CompositorBridgeParent::RecvAdoptChild(c
       sIndirectLayerTrees[child].mWrBridge->UpdateWebRender(mWrBridge->CompositorScheduler(),
                                                             mWrBridge->GetWebRenderAPI(),
                                                             mWrBridge->CompositableHolder(),
                                                             GetAnimationStorage());
       // Pretend we composited, since parent CompositorBridgeParent was replaced.
       CrossProcessCompositorBridgeParent* cpcp = sIndirectLayerTrees[child].mCrossProcessParent;
       if (cpcp) {
         TimeStamp now = TimeStamp::Now();
-        cpcp->DidComposite(child, now, now);
+        cpcp->DidCompositeLocked(child, now, now);
       }
     }
     parent = sIndirectLayerTrees[child].mApzcTreeManagerParent;
   }
 
   if (mApzcTreeManager && parent) {
     parent->ChildAdopted(mApzcTreeManager);
   }
@@ -2000,17 +2000,17 @@ CompositorBridgeParent::NotifyDidComposi
       Unused << ImageBridgeParent::NotifyImageComposites(notifications);
     }
   }
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   ForEachIndirectLayerTree([&] (LayerTreeState* lts, const uint64_t& aLayersId) -> void {
     if (lts->mCrossProcessParent && lts->mParent == this) {
       CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
-      cpcp->DidComposite(aLayersId, aCompositeStart, aCompositeEnd);
+      cpcp->DidCompositeLocked(aLayersId, aCompositeStart, aCompositeEnd);
     }
   });
 }
 
 void
 CompositorBridgeParent::InvalidateRemoteLayers()
 {
   MOZ_ASSERT(CompositorLoop() == MessageLoop::current());
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -357,16 +357,26 @@ CrossProcessCompositorBridgeParent::Shad
 }
 
 void
 CrossProcessCompositorBridgeParent::DidComposite(
   uint64_t aId,
   TimeStamp& aCompositeStart,
   TimeStamp& aCompositeEnd)
 {
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  DidCompositeLocked(aId, aCompositeStart, aCompositeEnd);
+}
+
+void
+CrossProcessCompositorBridgeParent::DidCompositeLocked(
+  uint64_t aId,
+  TimeStamp& aCompositeStart,
+  TimeStamp& aCompositeEnd)
+{
   sIndirectLayerTreesLock->AssertCurrentThreadOwns();
   if (LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree) {
     uint64_t transactionId = layerTree->GetPendingTransactionId();
     if (transactionId) {
       Unused << SendDidComposite(aId, transactionId, aCompositeStart, aCompositeEnd);
       layerTree->SetPendingTransactionId(0);
     }
   } else if (WebRenderBridgeParent* wrbridge = sIndirectLayerTrees[aId].mWrBridge) {
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -102,16 +102,22 @@ public:
                               APZTestData* aOutData) override;
   virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId,
                                       const uint64_t& aInputBlockId,
                                       const nsTArray<ScrollableLayerGuid>& aTargets) override;
 
   virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) override;
   virtual mozilla::ipc::IPCResult RecvRemotePluginsReady()  override { return IPC_FAIL_NO_REASON(this); }
 
+  // Use DidCompositeLocked if you already hold a lock on
+  // sIndirectLayerTreesLock; Otherwise use DidComposite, which would request
+  // the lock automatically.
+  void DidCompositeLocked(uint64_t aId,
+                                  TimeStamp& aCompositeStart,
+                                  TimeStamp& aCompositeEnd);
   virtual void DidComposite(uint64_t aId,
                             TimeStamp& aCompositeStart,
                             TimeStamp& aCompositeEnd) override;
 
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const LayersBackend& aLayersBackend,
                                               const TextureFlags& aFlags,
                                               const uint64_t& aId,
--- a/image/imgRequest.cpp
+++ b/image/imgRequest.cpp
@@ -64,17 +64,19 @@ imgRequest::imgRequest(imgLoader* aLoade
  , mMutex("imgRequest")
  , mProgressTracker(new ProgressTracker())
  , mIsMultiPartChannel(false)
  , mGotData(false)
  , mIsInCache(false)
  , mDecodeRequested(false)
  , mNewPartPending(false)
  , mHadInsecureRedirect(false)
-{ }
+{
+  LOG_FUNC(gImgLog, "imgRequest::imgRequest()");
+}
 
 imgRequest::~imgRequest()
 {
   if (mLoader) {
     mLoader->RemoveFromUncachedImages(this);
   }
   if (mURI) {
     nsAutoCString spec;
--- a/image/imgRequestProxy.cpp
+++ b/image/imgRequestProxy.cpp
@@ -117,17 +117,17 @@ imgRequestProxy::imgRequestProxy() :
   mIsInLoadGroup(false),
   mListenerIsStrongRef(false),
   mDecodeRequested(false),
   mDeferNotifications(false),
   mHadListener(false),
   mHadDispatch(false)
 {
   /* member initializers and constructor code */
-
+  LOG_FUNC(gImgLog, "imgRequestProxy::imgRequestProxy");
 }
 
 imgRequestProxy::~imgRequestProxy()
 {
   /* destructor code */
   NS_PRECONDITION(!mListener,
                   "Someone forgot to properly cancel this request!");
 
@@ -164,16 +164,18 @@ imgRequestProxy::~imgRequestProxy()
     /* Call RemoveProxy with a successful status.  This will keep the
        channel, if still downloading data, from being canceled if 'this' is
        the last observer.  This allows the image to continue to download and
        be cached even if no one is using it currently.
     */
     mCanceled = true;
     GetOwner()->RemoveProxy(this, NS_OK);
   }
+
+  LOG_FUNC(gImgLog, "imgRequestProxy::~imgRequestProxy");
 }
 
 nsresult
 imgRequestProxy::Init(imgRequest* aOwner,
                       nsILoadGroup* aLoadGroup,
                       nsIDocument* aLoadingDocument,
                       ImageURL* aURI,
                       imgINotificationObserver* aObserver)
--- a/ipc/chromium/src/base/message_loop.cc
+++ b/ipc/chromium/src/base/message_loop.cc
@@ -163,16 +163,21 @@ MessageLoop::EventTarget::DelayedDispatc
 
 //------------------------------------------------------------------------------
 
 // static
 MessageLoop* MessageLoop::current() {
   return get_tls_ptr().Get();
 }
 
+// static
+void MessageLoop::set_current(MessageLoop* loop) {
+  get_tls_ptr().Set(loop);
+}
+
 static mozilla::Atomic<int32_t> message_loop_id_seq(0);
 
 MessageLoop::MessageLoop(Type type, nsIThread* aThread)
     : type_(type),
       id_(++message_loop_id_seq),
       nestable_tasks_allowed_(true),
       exception_restoration_(false),
       state_(NULL),
--- a/ipc/chromium/src/base/message_loop.h
+++ b/ipc/chromium/src/base/message_loop.h
@@ -214,16 +214,18 @@ public:
     DCHECK(thread_name_.empty()) << "Should not rename this thread!";
     thread_name_ = aThreadName;
   }
   const std::string& thread_name() const { return thread_name_; }
 
   // Returns the MessageLoop object for the current thread, or null if none.
   static MessageLoop* current();
 
+  static void set_current(MessageLoop* loop);
+
   // Enables or disables the recursive task processing. This happens in the case
   // of recursive message loops. Some unwanted message loop may occurs when
   // using common controls or printer functions. By default, recursive task
   // processing is disabled.
   //
   // The specific case where tasks get queued is:
   // - The thread is running a message loop.
   // - It receives a task #1 and execute it.
--- a/ipc/chromium/src/base/pickle.h
+++ b/ipc/chromium/src/base/pickle.h
@@ -12,28 +12,24 @@
 #include "base/basictypes.h"
 #include "base/logging.h"
 #include "base/string16.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/BufferList.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/TimeStamp.h"
-
 #ifdef FUZZING
 #include "base/singleton.h"
 #include "mozilla/ipc/Faulty.h"
 #endif
-
-#if !defined(RELEASE_OR_BETA) || defined(DEBUG)
+#if (!defined(RELEASE_OR_BETA) && !defined(FUZZING)) || defined(DEBUG)
 #define MOZ_PICKLE_SENTINEL_CHECKING
 #endif
-
 class Pickle;
-
 class PickleIterator {
 public:
   explicit PickleIterator(const Pickle& pickle);
 
 private:
   friend class Pickle;
 
   mozilla::BufferList<InfallibleAllocPolicy>::IterImpl iter_;
--- a/ipc/glue/MessageLink.cpp
+++ b/ipc/glue/MessageLink.cpp
@@ -67,16 +67,18 @@ ProcessLink::~ProcessLink()
     mIOLoop = nullptr;
     mExistingListener = nullptr;
 #endif
 }
 
 void
 ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Side aSide)
 {
+    mChan->AssertWorkerThread();
+
     NS_PRECONDITION(aTransport, "need transport layer");
 
     // FIXME need to check for valid channel
 
     mTransport = aTransport;
 
     // FIXME figure out whether we're in parent or child, grab IO loop
     // appropriately
@@ -125,18 +127,22 @@ ProcessLink::Open(mozilla::ipc::Transpor
             // over the channel from the previous listener and process
             // any queued messages.
             mIOLoop->PostTask(NewNonOwningRunnableMethod(
               "ipc::ProcessLink::OnTakeConnectedChannel",
               this,
               &ProcessLink::OnTakeConnectedChannel));
         }
 
-        // Should not wait here if something goes wrong with the channel.
-        while (!mChan->Connected() && mChan->mChannelState != ChannelError) {
+        // Wait until one of the runnables above changes the state of the
+        // channel. Note that the state could be changed again after that (to
+        // ChannelClosing, for example, by the IO thread). We can rely on it not
+        // changing back to Closed: only the worker thread changes it to closed,
+        // and we're on the worker thread, blocked.
+        while (mChan->mChannelState == ChannelClosed) {
             mChan->mMonitor->Wait();
         }
     }
 }
 
 void
 ProcessLink::EchoMessage(Message *msg)
 {
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.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 js_StructuredClone_h
 #define js_StructuredClone_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/BufferList.h"
+#include "mozilla/MemoryReporting.h"
 
 #include <stdint.h>
 
 #include "jstypes.h"
 
 #include "js/RootingAPI.h"
 #include "js/TypeDecls.h"
 #include "js/Value.h"
@@ -334,16 +335,26 @@ class JS_PUBLIC_API(JSAutoStructuredClon
 
     bool write(JSContext* cx, JS::HandleValue v,
                const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
 
     bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable,
                JS::CloneDataPolicy cloneDataPolicy,
                const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
+    {
+        return data_.SizeOfExcludingThis(mallocSizeOf);
+    }
+
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
+    {
+        return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
+    }
+
   private:
     // Copy and assignment are not supported.
     JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& other) = delete;
     JSAutoStructuredCloneBuffer& operator=(const JSAutoStructuredCloneBuffer& other) = delete;
 };
 
 // The range of tag values the application may use for its own custom object types.
 #define JS_SCTAG_USER_MIN  ((uint32_t) 0xFFFF8000)
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -18,17 +18,16 @@ namespace JS {
     _(GetProp_Constant)                                 \
     _(GetProp_NotDefined)                               \
     _(GetProp_StaticName)                               \
     _(GetProp_SimdGetter)                               \
     _(GetProp_TypedObject)                              \
     _(GetProp_DefiniteSlot)                             \
     _(GetProp_Unboxed)                                  \
     _(GetProp_CommonGetter)                             \
-    _(GetProp_Static)                                   \
     _(GetProp_InlineAccess)                             \
     _(GetProp_Innerize)                                 \
     _(GetProp_InlineCache)                              \
     _(GetProp_SharedCache)                              \
     _(GetProp_ModuleNamespace)                          \
                                                         \
     _(SetProp_CommonSetter)                             \
     _(SetProp_TypedObject)                              \
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -9511,25 +9511,16 @@ BytecodeEmitter::emitCallOrNew(ParseNode
         if (!emitGetName(pn2, callop))
             return false;
         break;
       case PNK_DOT:
         MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
         if (pn2->as<PropertyAccess>().isSuper()) {
             if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop))
                 return false;
-        } else if ((pn->getOp() == JSOP_FUNCALL || pn->getOp() == JSOP_FUNAPPLY) &&
-                   pn2->expr()->getKind() == PNK_FUNCTION &&
-                   checkRunOnceContext()) {
-            // Top level lambdas whose .call or .apply methods are immediately
-            // invoked should be treated as run once lambdas.
-            emittingRunOnceLambda = true;
-            if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP))
-                return false;
-            emittingRunOnceLambda = false;
         } else {
             if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP))
                 return false;
         }
 
         break;
       case PNK_ELEM:
         MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -357,19 +357,16 @@ js::gc::GCRuntime::traceRuntimeCommon(JS
     for (const CooperatingContext& target : rt->cooperatingContexts())
         target.context()->trace(trc);
 
     // Trace all compartment roots, but not the compartment itself; it is
     // traced via the parent pointer if traceRoots actually traces anything.
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->traceRoots(trc, traceOrMark);
 
-    // Trace the Gecko Profiler.
-    rt->geckoProfiler().trace(trc);
-
     // Trace helper thread roots.
     HelperThreadState().trace(trc);
 
     // Trace the embedding's black and gray roots.
     if (!JS::CurrentThreadIsHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_EMBEDDING);
 
         /*
--- a/js/src/gc/ZoneGroup.cpp
+++ b/js/src/gc/ZoneGroup.cpp
@@ -6,16 +6,18 @@
 
 #include "gc/ZoneGroup.h"
 
 #include "jscntxt.h"
 
 #include "jit/IonBuilder.h"
 #include "jit/JitCompartment.h"
 
+using namespace js;
+
 namespace js {
 
 ZoneGroup::ZoneGroup(JSRuntime* runtime)
   : runtime(runtime),
     ownerContext_(TlsContext.get()),
     enterCount(1),
     zones_(this),
     usedByHelperThread(false),
@@ -142,8 +144,31 @@ ZoneGroup::deleteEmptyZone(Zone* zone)
             zone->destroy(runtime->defaultFreeOp());
             return;
         }
     }
     MOZ_CRASH("Zone not found");
 }
 
 } // namespace js
+
+JS::AutoRelinquishZoneGroups::AutoRelinquishZoneGroups(JSContext* cx)
+  : cx(cx)
+{
+    MOZ_ASSERT(cx == TlsContext.get());
+
+    AutoEnterOOMUnsafeRegion oomUnsafe;
+    for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
+        while (group->ownerContext().context() == cx) {
+            group->leave();
+            if (!enterList.append(group))
+                oomUnsafe.crash("AutoRelinquishZoneGroups");
+        }
+    }
+}
+
+JS::AutoRelinquishZoneGroups::~AutoRelinquishZoneGroups()
+{
+    for (size_t i = 0; i < enterList.length(); i++) {
+        ZoneGroup* group = static_cast<ZoneGroup*>(enterList[i]);
+        group->enter(cx);
+    }
+}
deleted file mode 100644
--- a/js/src/jit-test/tests/ion/bailoutStaticObject.js
+++ /dev/null
@@ -1,28 +0,0 @@
-// Test bailouts from loading particular non-singleton static objects.
-
-function wrap(fun) {
-    function wrapper() {
-	return fun.apply(this, arguments);
-    }
-    return wrapper;
-}
-
-var adder = wrap(function(a, b) { return a + b; });
-var subber = wrap(function(a, b) { return a - b; });
-var tmp = adder;
-adder = subber;
-adder = tmp;
-
-function foo() {
-    var i = 0;
-    var a = 0;
-    for (var i = 0; i < 10000; i++) {
-	a = adder(a, 1);
-	a = subber(a, 1);
-    }
-    return a;
-}
-
-assertEq(foo(), 0);
-adder = subber;
-assertEq(foo(), -20000);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/idempotentCache.js
@@ -0,0 +1,34 @@
+// Test that we don't attach ICs to idempotent caches that are incompatible
+// with the cache result type.
+
+var missingObjs = [{a:1},Object.create({a:2}),{}];
+function testMissing(limit)
+{
+    var res = 0;
+    for (var i = 0; i < 1000; i++) {
+	for (var j = 0; j < missingObjs.length; j++) {
+	    var obj = missingObjs[j];
+	    if (j < limit)
+		res += obj.a;
+	}
+    }
+    return res;
+}
+assertEq(testMissing(2), 3000);
+assertEq(testMissing(3), NaN);
+
+var lengthObjs = [{length:{a:1}},Object.create({length:{a:2}}),[0,1]];
+function testArrayLength(limit)
+{
+    var res = 0;
+    for (var i = 0; i < 1000; i++) {
+	for (var j = 0; j < lengthObjs.length; j++) {
+	    var obj = lengthObjs[j];
+	    if (j < limit)
+		res += obj.length.a;
+	}
+    }
+    return res;
+}
+assertEq(testArrayLength(2), 3000);
+assertEq(testArrayLength(3), NaN);
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1998,17 +1998,16 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_NonObjectInput:
       case Bailout_NonStringInput:
       case Bailout_NonSymbolInput:
       case Bailout_UnexpectedSimdInput:
       case Bailout_NonSharedTypedArrayInput:
       case Bailout_Debugger:
       case Bailout_UninitializedThis:
       case Bailout_BadDerivedConstructorReturn:
-      case Bailout_LoadStaticObject:
         // Do nothing.
         break;
 
       case Bailout_FirstExecution:
         // Do not return directly, as this was not frequent in the first place,
         // thus rely on the check for frequent bailouts to recompile the current
         // script.
         break;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -4803,17 +4803,18 @@ BaselineCompiler::emit_JSOP_RESUME()
 
     // If profiler instrumentation is on, update lastProfilingFrame on
     // current JitActivation
     {
         Register scratchReg = scratch2;
         Label skip;
         AbsoluteAddress addressOfEnabled(cx->runtime()->geckoProfiler().addressOfEnabled());
         masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skip);
-        masm.loadPtr(AbsoluteAddress(cx->addressOfProfilingActivation()), scratchReg);
+        masm.loadJSContext(scratchReg);
+        masm.loadPtr(Address(scratchReg, JSContext::offsetOfProfilingActivation()), scratchReg);
         masm.storePtr(masm.getStackPointer(),
                       Address(scratchReg, JitActivation::offsetOfLastProfilingFrame()));
         masm.bind(&skip);
     }
 
     // Construct BaselineFrame.
     masm.push(BaselineFrameReg);
     masm.moveStackPtrTo(BaselineFrameReg);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -805,18 +805,20 @@ DoGetElemFallback(JSContext* cx, Baselin
     bool attached = false;
     bool isTemporarilyUnoptimizable = false;
 
     if (stub->state().maybeTransition())
         stub->discardStubs(cx);
 
     if (stub->state().canAttachStub()) {
         ICStubEngine engine = ICStubEngine::Baseline;
-        GetPropIRGenerator gen(cx, script, pc, CacheKind::GetElem, stub->state().mode(),
-                               &isTemporarilyUnoptimizable, lhs, rhs, lhs, CanAttachGetter::Yes);
+        GetPropIRGenerator gen(cx, script, pc,
+                               CacheKind::GetElem, stub->state().mode(),
+                               &isTemporarilyUnoptimizable, lhs, rhs, lhs,
+                               GetPropertyResultFlags::All);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         engine, script, stub, &attached);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
@@ -879,17 +881,17 @@ DoGetElemSuperFallback(JSContext* cx, Ba
 
     if (stub->state().maybeTransition())
         stub->discardStubs(cx);
 
     if (stub->state().canAttachStub()) {
         ICStubEngine engine = ICStubEngine::Baseline;
         GetPropIRGenerator gen(cx, script, pc, CacheKind::GetElemSuper, stub->state().mode(),
                                &isTemporarilyUnoptimizable, lhs, rhs, receiver,
-                               CanAttachGetter::Yes);
+                               GetPropertyResultFlags::All);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         engine, script, stub, &attached);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -1035,17 +1035,17 @@ GetMegamorphicGetterSetterFunction(ICStu
     JSObject* obj = isGetter ? propShape->getterObject() : propShape->setterObject();
     return &obj->as<JSFunction>();
 }
 
 bool
 BaselineInspector::megamorphicGetterSetterFunction(jsbytecode* pc, bool isGetter,
                                                    JSFunction** getterOrSetter)
 {
-    if (!hasBaselineScript() || *pc == JSOP_SETALIASEDVAR)
+    if (!hasBaselineScript())
         return false;
 
     *getterOrSetter = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
     for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isCacheIR_Monitored()) {
             MOZ_ASSERT(isGetter);
@@ -1186,17 +1186,17 @@ AddCacheIRSetPropFunction(ICCacheIR_Upda
 }
 
 bool
 BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape,
                                          JSFunction** commonSetter, bool* isOwnProperty,
                                          ReceiverVector& receivers,
                                          ObjectGroupVector& convertUnboxedGroups)
 {
-    if (!hasBaselineScript() || *pc == JSOP_SETALIASEDVAR)
+    if (!hasBaselineScript())
         return false;
 
     MOZ_ASSERT(receivers.empty());
     MOZ_ASSERT(convertUnboxedGroups.empty());
 
     *commonSetter = nullptr;
     const ICEntry& entry = icEntryFromPC(pc);
 
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1261,13 +1261,15 @@ MarkActiveBaselineScripts(JSContext* cx,
 }
 
 void
 jit::MarkActiveBaselineScripts(Zone* zone)
 {
     if (zone->isAtomsZone())
         return;
     JSContext* cx = TlsContext.get();
-    for (JitActivationIterator iter(cx, zone->group()->ownerContext()); !iter.done(); ++iter) {
-        if (iter->compartment()->zone() == zone)
-            MarkActiveBaselineScripts(cx, iter);
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        for (JitActivationIterator iter(cx, target); !iter.done(); ++iter) {
+            if (iter->compartment()->zone() == zone)
+                MarkActiveBaselineScripts(cx, iter);
+        }
     }
 }
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -43,23 +43,23 @@ IRGenerator::IRGenerator(JSContext* cx, 
     cacheKind_(cacheKind),
     mode_(mode)
 {}
 
 GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
                                        CacheKind cacheKind, ICState::Mode mode,
                                        bool* isTemporarilyUnoptimizable, HandleValue val,
                                        HandleValue idVal, HandleValue receiver,
-                                       CanAttachGetter canAttachGetter)
+                                       GetPropertyResultFlags resultFlags)
   : IRGenerator(cx, script, pc, cacheKind, mode),
     val_(val),
     idVal_(idVal),
     receiver_(receiver),
     isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
-    canAttachGetter_(canAttachGetter),
+    resultFlags_(resultFlags),
     preliminaryObjectAction_(PreliminaryObjectAction::None)
 {}
 
 static void
 EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder,
                    Shape* shape)
 {
     if (holder->isFixedSlot(shape->slot())) {
@@ -242,72 +242,75 @@ GetPropIRGenerator::tryAttachStub()
 
     trackNotAttached();
     return false;
 }
 
 bool
 GetPropIRGenerator::tryAttachIdempotentStub()
 {
-    // For idempotent ICs, only attach stubs for plain data properties.
-    // This ensures (1) the lookup has no side-effects and (2) Ion has complete
-    // static type information and we don't have to monitor the result. Because
-    // of (2), we don't support for instance missing properties or array
-    // lengths, as TI does not account for these cases.
+    // For idempotent ICs, only attach stubs which we can be sure have no side
+    // effects and produce a result which the MIR in the calling code is able
+    // to handle, since we do not have a pc to explicitly monitor the result.
 
     MOZ_ASSERT(idempotent());
 
     RootedObject obj(cx_, &val_.toObject());
     RootedId id(cx_, NameToId(idVal_.toString()->asAtom().asPropertyName()));
 
     ValOperandId valId(writer.setInputOperandId(0));
     ObjOperandId objId = writer.guardIsObject(valId);
     if (tryAttachNative(obj, objId, id))
         return true;
 
+    // Object lengths are supported only if int32 results are allowed.
+    if ((resultFlags_ & GetPropertyResultFlags::AllowInt32) && tryAttachObjectLength(obj, objId, id))
+        return true;
+
     // Also support native data properties on DOMProxy prototypes.
     if (GetProxyStubType(cx_, obj, id) == ProxyStubType::DOMUnshadowed)
         return tryAttachDOMProxyUnshadowed(obj, objId, id);
 
     return false;
 }
 
 static bool
 IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
-                      jsbytecode* pc)
+                      jsbytecode* pc, GetPropertyResultFlags resultFlags)
 {
     if (shape)
         return false;
 
     MOZ_ASSERT(!holder);
 
-    if (!pc) {
-        // This is an idempotent IC, don't attach a missing-property stub.
-        // See tryAttachStub.
+    // Idempotent ICs may only attach missing-property stubs if undefined
+    // results are explicitly allowed, since no monitoring is done of the
+    // cache result.
+    if (!pc && !(resultFlags & GetPropertyResultFlags::AllowUndefined))
         return false;
-    }
 
     // If we're doing a name lookup, we have to throw a ReferenceError. If
     // extra warnings are enabled, we may have to report a warning.
-    if (*pc == JSOP_GETBOUNDNAME || cx->compartment()->behaviors().extraWarnings(cx))
+    // Note that Ion does not generate idempotent caches for JSOP_GETBOUNDNAME.
+    if ((pc && *pc == JSOP_GETBOUNDNAME) || cx->compartment()->behaviors().extraWarnings(cx))
         return false;
 
     return CheckHasNoSuchProperty(cx, obj, id);
 }
 
 enum NativeGetPropCacheability {
     CanAttachNone,
     CanAttachReadSlot,
     CanAttachCallGetter,
 };
 
 static NativeGetPropCacheability
 CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id,
                        MutableHandleNativeObject holder, MutableHandleShape shape,
-                       jsbytecode* pc, CanAttachGetter canAttachGetter,
+                       jsbytecode* pc, GetPropertyResultFlags resultFlags,
                        bool* isTemporarilyUnoptimizable)
 {
     MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
 
     // The lookup needs to be universally pure, otherwise we risk calling hooks out
     // of turn. We don't mind doing this even when purity isn't required, because we
     // only miss out on shape hashification, which is only a temporary perf cost.
     // The limits were arbitrarily set, anyways.
@@ -322,32 +325,27 @@ CanAttachNativeGetProp(JSContext* cx, Ha
             return CanAttachNone;
         holder.set(&baseHolder->as<NativeObject>());
     }
     shape.set(prop.maybeShape());
 
     if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop))
         return CanAttachReadSlot;
 
-    // Idempotent ICs only support plain data properties, see
-    // tryAttachIdempotentStub.
-    if (!pc)
-        return CanAttachNone;
-
-    if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc))
+    if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc, resultFlags))
         return CanAttachReadSlot;
 
-    if (canAttachGetter == CanAttachGetter::No)
-        return CanAttachNone;
-
-    if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable))
-        return CanAttachCallGetter;
-
-    if (IsCacheableGetPropCallNative(obj, holder, shape))
-        return CanAttachCallGetter;
+    // Idempotent ICs cannot call getters, see tryAttachIdempotentStub.
+    if (pc && (resultFlags & GetPropertyResultFlags::Monitored)) {
+        if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable))
+            return CanAttachCallGetter;
+
+        if (IsCacheableGetPropCallNative(obj, holder, shape))
+            return CanAttachCallGetter;
+    }
 
     return CanAttachNone;
 }
 
 static void
 GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId objId)
 {
     // The guards here protect against the effects of JSObject::swap(). If the
@@ -577,20 +575,18 @@ GetPropIRGenerator::attachMegamorphicNat
 
 bool
 GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id)
 {
     RootedShape shape(cx_);
     RootedNativeObject holder(cx_);
 
     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_,
-                                                            canAttachGetter_,
+                                                            resultFlags_,
                                                             isTemporarilyUnoptimizable_);
-    MOZ_ASSERT_IF(idempotent(),
-                  type == CanAttachNone || (type == CanAttachReadSlot && holder));
     switch (type) {
       case CanAttachNone:
         return false;
       case CanAttachReadSlot:
         if (mode_ == ICState::Mode::Megamorphic) {
             attachMegamorphicNativeSlot(objId, id, holder == nullptr);
             return true;
         }
@@ -608,16 +604,17 @@ GetPropIRGenerator::tryAttachNative(Hand
         }
         EmitReadSlotResult(writer, obj, holder, shape, objId);
         EmitReadSlotReturn(writer, obj, holder, shape);
 
         trackAttached("NativeSlot");
         return true;
       case CanAttachCallGetter: {
         // |super.prop| accesses use a |this| value that differs from lookup object
+        MOZ_ASSERT(!idempotent());
         ObjOperandId receiverId = isSuper() ? writer.guardIsObject(getSuperReceiverValueId())
                                             : objId;
         maybeEmitIdGuard(id);
         EmitCallGetterResult(writer, obj, holder, shape, objId, receiverId, mode_);
 
         trackAttached("NativeGetter");
         return true;
       }
@@ -646,17 +643,17 @@ GetPropIRGenerator::tryAttachWindowProxy
     MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
     MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
 
     // Now try to do the lookup on the Window (the current global).
     HandleObject windowObj = cx_->global();
     RootedShape shape(cx_);
     RootedNativeObject holder(cx_);
     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_,
-                                                            canAttachGetter_,
+                                                            resultFlags_,
                                                             isTemporarilyUnoptimizable_);
     switch (type) {
       case CanAttachNone:
         return false;
 
       case CanAttachReadSlot: {
         maybeEmitIdGuard(id);
         writer.guardClass(objId, GuardClassKind::WindowProxy);
@@ -733,18 +730,18 @@ GetPropIRGenerator::tryAttachCrossCompar
         MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == unwrapped->compartment()->maybeGlobal());
         unwrapped = cx_->global();
         MOZ_ASSERT(unwrapped);
     }
 
     RootedShape shape(cx_);
     RootedNativeObject holder(cx_);
     NativeGetPropCacheability canCache =
-        CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_, canAttachGetter_,
-                               isTemporarilyUnoptimizable_);
+        CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_,
+                               resultFlags_, isTemporarilyUnoptimizable_);
     if (canCache != CanAttachReadSlot)
         return false;
 
     if (holder) {
         EnsureTrackPropertyTypes(cx_, holder, id);
         if (unwrapped == holder) {
             // See the comment in StripPreliminaryObjectStubs.
             if (IsPreliminaryObject(unwrapped))
@@ -958,17 +955,17 @@ GetPropIRGenerator::tryAttachDOMProxyExp
         expandoObj = &expandoAndGeneration->expando.toObject();
     }
 
     // Try to do the lookup on the expando object.
     RootedNativeObject holder(cx_);
     RootedShape propShape(cx_);
     NativeGetPropCacheability canCache =
         CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &propShape, pc_,
-                               canAttachGetter_, isTemporarilyUnoptimizable_);
+                               resultFlags_, isTemporarilyUnoptimizable_);
     if (canCache != CanAttachReadSlot && canCache != CanAttachCallGetter)
         return false;
     if (!holder)
         return false;
 
     MOZ_ASSERT(holder == expandoObj);
 
     maybeEmitIdGuard(id);
@@ -1049,20 +1046,18 @@ GetPropIRGenerator::tryAttachDOMProxyUns
 
     RootedObject checkObj(cx_, obj->staticPrototype());
     if (!checkObj)
         return false;
 
     RootedNativeObject holder(cx_);
     RootedShape shape(cx_);
     NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape,
-                                                                pc_, canAttachGetter_,
+                                                                pc_, resultFlags_,
                                                                 isTemporarilyUnoptimizable_);
-    MOZ_ASSERT_IF(idempotent(),
-                  canCache == CanAttachNone || (canCache == CanAttachReadSlot && holder));
     if (canCache == CanAttachNone)
         return false;
 
     maybeEmitIdGuard(id);
     writer.guardShape(objId, obj->maybeShape());
 
     // Guard that our expando object hasn't started shadowing this property.
     CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
@@ -1389,17 +1384,17 @@ GetPropIRGenerator::tryAttachPrimitive(V
         return false;
     }
     if (!proto)
         return false;
 
     RootedShape shape(cx_);
     RootedNativeObject holder(cx_);
     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, proto, id, &holder, &shape, pc_,
-                                                            canAttachGetter_,
+                                                            resultFlags_,
                                                             isTemporarilyUnoptimizable_);
     if (type != CanAttachReadSlot)
         return false;
 
     if (holder) {
         // Instantiate this property, for use during Ion compilation.
         if (IsIonEnabled(cx_))
             EnsureTrackPropertyTypes(cx_, holder, id);
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -1142,26 +1142,57 @@ class MOZ_RAII IRGenerator
   public:
     explicit IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
                          ICState::Mode mode);
 
     const CacheIRWriter& writerRef() const { return writer; }
     CacheKind cacheKind() const { return cacheKind_; }
 };
 
-enum class CanAttachGetter { Yes, No };
+// Flags used to describe what values a GetProperty cache may produce.
+enum class GetPropertyResultFlags {
+    None            = 0,
+
+    // Values produced by this cache will go through a type barrier,
+    // so the cache may produce any type of value that is compatible with its
+    // result operand.
+    Monitored       = 1 << 0,
+
+    // Whether particular primitives may be produced by this cache.
+    AllowUndefined  = 1 << 1,
+    AllowInt32      = 1 << 2,
+    AllowDouble     = 1 << 3,
+
+    All             = Monitored | AllowUndefined | AllowInt32 | AllowDouble
+};
+
+static inline bool operator&(GetPropertyResultFlags a, GetPropertyResultFlags b)
+{
+    return static_cast<int>(a) & static_cast<int>(b);
+}
+
+static inline GetPropertyResultFlags operator|(GetPropertyResultFlags a, GetPropertyResultFlags b)
+{
+    return static_cast<GetPropertyResultFlags>(static_cast<int>(a) | static_cast<int>(b));
+}
+
+static inline GetPropertyResultFlags& operator|=(GetPropertyResultFlags& lhs, GetPropertyResultFlags b)
+{
+    lhs = lhs | b;
+    return lhs;
+}
 
 // GetPropIRGenerator generates CacheIR for a GetProp IC.
 class MOZ_RAII GetPropIRGenerator : public IRGenerator
 {
     HandleValue val_;
     HandleValue idVal_;
     HandleValue receiver_;
     bool* isTemporarilyUnoptimizable_;
-    CanAttachGetter canAttachGetter_;
+    GetPropertyResultFlags resultFlags_;
 
     enum class PreliminaryObjectAction { None, Unlink, NotePreliminary };
     PreliminaryObjectAction preliminaryObjectAction_;
 
     bool tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id);
     bool tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id);
@@ -1228,17 +1259,18 @@ class MOZ_RAII GetPropIRGenerator : publ
     void maybeEmitIdGuard(jsid id);
 
     void trackAttached(const char* name);
     void trackNotAttached();
 
   public:
     GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
                        ICState::Mode mode, bool* isTemporarilyUnoptimizable, HandleValue val,
-                       HandleValue idVal, HandleValue receiver, CanAttachGetter canAttachGetter);
+                       HandleValue idVal, HandleValue receiver,
+                       GetPropertyResultFlags resultFlags);
 
     bool tryAttachStub();
     bool tryAttachIdempotentStub();
 
     bool shouldUnlinkPreliminaryObjectStubs() const {
         return preliminaryObjectAction_ == PreliminaryObjectAction::Unlink;
     }
     bool shouldNotePreliminaryObjectStub() const {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -10280,28 +10280,27 @@ CodeGenerator::visitGetNameCache(LGetNam
     IonGetNameIC ic(liveRegs, envChain, output, temp);
     addIC(ins, allocateIC(ic));
 }
 
 void
 CodeGenerator::addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
                                    TypedOrValueRegister value, const ConstantOrRegister& id,
                                    TypedOrValueRegister output, Register maybeTemp,
-                                   bool monitoredResult, bool allowDoubleResult,
+                                   GetPropertyResultFlags resultFlags,
                                    jsbytecode* profilerLeavePc)
 {
     CacheKind kind = CacheKind::GetElem;
     if (id.constant() && id.value().isString()) {
         JSString* idString = id.value().toString();
         uint32_t dummy;
         if (idString->isAtom() && !idString->asAtom().isIndex(&dummy))
             kind = CacheKind::GetProp;
     }
-    IonGetPropertyIC cache(kind, liveRegs, value, id, output, maybeTemp, monitoredResult,
-                           allowDoubleResult);
+    IonGetPropertyIC cache(kind, liveRegs, value, id, output, maybeTemp, resultFlags);
     addIC(ins, allocateIC(cache));
 }
 
 void
 CodeGenerator::addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                                    Register temp, FloatRegister tempDouble,
                                    FloatRegister tempF32, const ConstantOrRegister& id,
                                    const ConstantOrRegister& value,
@@ -10328,44 +10327,71 @@ CodeGenerator::toConstantOrRegister(LIns
 
     const LAllocation* value = lir->getOperand(n);
     if (value->isConstant())
         return ConstantOrRegister(value->toConstant()->toJSValue());
 
     return TypedOrValueRegister(type, ToAnyRegister(value));
 }
 
+static GetPropertyResultFlags
+IonGetPropertyICFlags(const MGetPropertyCache* mir)
+{
+    GetPropertyResultFlags flags = GetPropertyResultFlags::None;
+    if (mir->monitoredResult())
+        flags |= GetPropertyResultFlags::Monitored;
+
+    if (mir->type() == MIRType::Value) {
+        if (TemporaryTypeSet* types = mir->resultTypeSet()) {
+            if (types->hasType(TypeSet::UndefinedType()))
+                flags |= GetPropertyResultFlags::AllowUndefined;
+            if (types->hasType(TypeSet::Int32Type()))
+                flags |= GetPropertyResultFlags::AllowInt32;
+            if (types->hasType(TypeSet::DoubleType()))
+                flags |= GetPropertyResultFlags::AllowDouble;
+        } else {
+            flags |= GetPropertyResultFlags::AllowUndefined
+                   | GetPropertyResultFlags::AllowInt32
+                   | GetPropertyResultFlags::AllowDouble;
+        }
+    } else if (mir->type() == MIRType::Int32) {
+        flags |= GetPropertyResultFlags::AllowInt32;
+    } else if (mir->type() == MIRType::Double) {
+        flags |= GetPropertyResultFlags::AllowInt32 | GetPropertyResultFlags::AllowDouble;
+    }
+
+    return flags;
+}
+
 void
 CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
     TypedOrValueRegister value =
         toConstantOrRegister(ins, LGetPropertyCacheV::Value, ins->mir()->value()->type()).reg();
     ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheV::Id, ins->mir()->idval()->type());
-    bool monitoredResult = ins->mir()->monitoredResult();
     TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins));
     Register maybeTemp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
 
-    addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp, monitoredResult,
-                        ins->mir()->allowDoubleResult(), ins->mir()->profilerLeavePc());
+    addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp,
+                        IonGetPropertyICFlags(ins->mir()), ins->mir()->profilerLeavePc());
 }
 
 void
 CodeGenerator::visitGetPropertyCacheT(LGetPropertyCacheT* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
     TypedOrValueRegister value =
         toConstantOrRegister(ins, LGetPropertyCacheV::Value, ins->mir()->value()->type()).reg();
     ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheT::Id, ins->mir()->idval()->type());
-    bool monitoredResult = ins->mir()->monitoredResult();
     TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->getDef(0)));
     Register maybeTemp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
 
-    addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp, monitoredResult,
-                        ins->mir()->allowDoubleResult(), ins->mir()->profilerLeavePc());
+    addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp,
+                        IonGetPropertyICFlags(ins->mir()), ins->mir()->profilerLeavePc());
 }
 
 void
 CodeGenerator::visitBindNameCache(LBindNameCache* ins)
 {
     LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register envChain = ToRegister(ins->environmentChain());
     Register output = ToRegister(ins->output());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -2,16 +2,17 @@
  * 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 jit_CodeGenerator_h
 #define jit_CodeGenerator_h
 
+#include "jit/CacheIR.h"
 #include "jit/IonCaches.h"
 #if defined(JS_ION_PERF)
 # include "jit/PerfSpewer.h"
 #endif
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/CodeGenerator-x86.h"
 #elif defined(JS_CODEGEN_X64)
@@ -463,18 +464,18 @@ class CodeGenerator final : public CodeG
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
         return counts;
     }
 
   private:
     void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs,
                              TypedOrValueRegister value, const ConstantOrRegister& id,
-                             TypedOrValueRegister output, Register maybeTemp, bool monitoredResult,
-                             bool allowDoubleResult, jsbytecode* profilerLeavePc);
+                             TypedOrValueRegister output, Register maybeTemp,
+                             GetPropertyResultFlags flags, jsbytecode* profilerLeavePc);
     void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg,
                              Register temp, FloatRegister tempDouble,
                              FloatRegister tempF32, const ConstantOrRegister& id,
                              const ConstantOrRegister& value,
                              bool strict, bool needsPostBarrier, bool needsTypeBarrier,
                              bool guardHoles, jsbytecode* profilerLeavePc);
 
     MOZ_MUST_USE bool generateBranchV(const ValueOperand& value, Label* ifTrue, Label* ifFalse,
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -32,17 +32,17 @@ CompileRuntime::addressOfGCZealModeBits(
 #endif
 
 const JitRuntime*
 CompileRuntime::jitRuntime()
 {
     return runtime()->jitRuntime();
 }
 
-GeckoProfiler&
+GeckoProfilerRuntime&
 CompileRuntime::geckoProfiler()
 {
     return runtime()->geckoProfiler();
 }
 
 bool
 CompileRuntime::jitSupportsFloatingPoint()
 {
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -29,17 +29,17 @@ class CompileRuntime
 
 #ifdef JS_GC_ZEAL
     const void* addressOfGCZealModeBits();
 #endif
 
     const JitRuntime* jitRuntime();
 
     // Compilation does not occur off thread when the Gecko Profiler is enabled.
-    GeckoProfiler& geckoProfiler();
+    GeckoProfilerRuntime& geckoProfiler();
 
     bool jitSupportsFloatingPoint();
     bool hadOutOfMemory();
     bool profilingScripts();
 
     const JSAtomState& names();
     const PropertyName* emptyString();
     const StaticStrings& staticStrings();
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -3120,20 +3120,22 @@ jit::InvalidateAll(FreeOp* fop, Zone* zo
     // The caller should previously have cancelled off thread compilation.
 #ifdef DEBUG
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
         MOZ_ASSERT(!HasOffThreadIonCompile(comp));
 #endif
     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);
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        for (JitActivationIterator iter(cx, target); !iter.done(); ++iter) {
+            if (iter->compartment()->zone() == zone) {
+                JitSpew(JitSpew_IonInvalidate, "Invalidating all frames for GC");
+                InvalidateActivation(fop, iter, true);
+            }
         }
     }
 }
 
 
 void
 jit::Invalidate(TypeZone& types, FreeOp* fop,
                 const RecompileInfoVector& invalid, bool resetUses,
@@ -3174,18 +3176,20 @@ jit::Invalidate(TypeZone& types, FreeOp*
     // 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);
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        for (JitActivationIterator iter(cx, target); !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)
             continue;
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -2,16 +2,17 @@
  * 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 "jit/IonBuilder.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/SizePrintfMacros.h"
 
 #include "builtin/Eval.h"
 #include "builtin/TypedObject.h"
 #include "frontend/SourceNotes.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
 #include "jit/Ion.h"
@@ -831,21 +832,23 @@ IonBuilder::build()
 #ifdef DEBUG
     // lazyArguments should never be accessed in |argsObjAliasesFormals| scripts.
     if (info().hasArguments() && !info().argsObjAliasesFormals())
         hasLazyArguments_ = true;
 #endif
 
     insertRecompileCheck();
 
+    auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] {
+        // Discard unreferenced & pre-allocated resume points.
+        replaceMaybeFallbackFunctionGetter(nullptr);
+    });
+
     MOZ_TRY(traverseBytecode());
 
-    // Discard unreferenced & pre-allocated resume points.
-    replaceMaybeFallbackFunctionGetter(nullptr);
-
     if (script_->hasBaselineScript() &&
         inlinedBytecodeLength_ > script_->baselineScript()->inlinedBytecodeLength())
     {
         script_->baselineScript()->setInlinedBytecodeLength(inlinedBytecodeLength_);
     }
 
     MOZ_TRY(maybeAddOsrTypeBarriers());
     MOZ_TRY(processIterators());
@@ -993,20 +996,23 @@ IonBuilder::buildInline(IonBuilder* call
 #endif
 
     insertRecompileCheck();
 
     // Initialize the env chain now that all resume points operands are
     // initialized.
     MOZ_TRY(initEnvironmentChain(callInfo.fun()));
 
+    auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] {
+        // Discard unreferenced & pre-allocated resume points.
+        replaceMaybeFallbackFunctionGetter(nullptr);
+    });
+
     MOZ_TRY(traverseBytecode());
 
-    // Discard unreferenced & pre-allocated resume points.
-    replaceMaybeFallbackFunctionGetter(nullptr);
 
     MOZ_ASSERT(iterators_.empty(), "Iterators should be added to outer builder");
 
     if (!info().isAnalysis() && !abortedPreliminaryGroups().empty())
         return abort(AbortReason::PreliminaryObjects);
 
     return Ok();
 }
@@ -7270,16 +7276,27 @@ IonBuilder::ensureDefiniteTypeSet(MDefin
     }
 
     // Create a NOP mir instruction to filter the typeset.
     MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), def, types);
     current->add(filter);
     return filter;
 }
 
+static size_t
+NumFixedSlots(JSObject* object)
+{
+    // Note: we can't use object->numFixedSlots() here, as this will read the
+    // shape and can race with the active thread if we are building off thread.
+    // The allocation kind and object class (which goes through the type) can
+    // be read freely, however.
+    gc::AllocKind kind = object->asTenured().getAllocKind();
+    return gc::GetGCKindSlots(kind, object->getClass());
+}
+
 static bool
 IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name)
 {
     LexicalEnvironmentObject &globalLexical = obj->as<LexicalEnvironmentObject>();
     MOZ_ASSERT(globalLexical.isGlobal());
     Shape* shape = globalLexical.lookupPure(name);
     if (!shape)
         return false;
@@ -7291,58 +7308,42 @@ IonBuilder::getStaticName(bool* emitted,
                           MDefinition* lexicalCheck)
 {
     MOZ_ASSERT(*emitted == false);
 
     jsid id = NameToId(name);
 
     bool isGlobalLexical = staticObject->is<LexicalEnvironmentObject>() &&
                            staticObject->as<LexicalEnvironmentObject>().isGlobal();
+    MOZ_ASSERT(isGlobalLexical ||
+               staticObject->is<GlobalObject>() ||
+               staticObject->is<CallObject>() ||
+               staticObject->is<ModuleEnvironmentObject>());
+    MOZ_ASSERT(staticObject->isSingleton());
 
     // Always emit the lexical check. This could be optimized, but is
     // currently not for simplicity's sake.
     if (lexicalCheck)
         return Ok();
 
-    // Only optimize accesses on native objects.
-    if (!staticObject->isNative())
-        return Ok();
-
-    // Only optimize accesses on own data properties.
-    Shape* propertyShape = staticObject->as<NativeObject>().lastProperty()->searchLinear(NameToId(name));
-    if (!propertyShape || !propertyShape->isDataDescriptor() || !propertyShape->hasSlot())
-        return Ok();
-    uint32_t slot = propertyShape->slot();
-
     TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(staticObject);
     if (analysisContext)
         staticKey->ensureTrackedProperty(analysisContext, NameToId(name));
 
-    // Make sure the property is a normal data property. This is not done for
-    // call objects, as they are not tracked by TI and their data properties
-    // cannot be dynamically reconfigured.
-    Maybe<HeapTypeSetKey> property;
-    if (!staticObject->is<CallObject>()) {
-        if (staticKey->unknownProperties())
-            return Ok();
-
-        property.emplace(staticKey->property(id));
-
-        if (property.ref().nonData(constraints()) ||
-            !property.ref().maybeTypes() ||
-            !property.ref().maybeTypes()->definiteProperty())
-        {
-            // We can't be sure the slot will match at runtime, so include a
-            // shape guard on the object.
-            MInstruction* obj = MConstant::NewConstraintlessObject(alloc(), staticObject);
-            current->add(obj);
-            addShapeGuard(obj, staticObject->as<NativeObject>().lastProperty(), Bailout_ShapeGuard);
-        } else {
-            MOZ_ASSERT(slot == property.ref().maybeTypes()->definiteSlot());
-        }
+    if (staticKey->unknownProperties())
+        return Ok();
+
+    HeapTypeSetKey property = staticKey->property(id);
+    if (!property.maybeTypes() ||
+        !property.maybeTypes()->definiteProperty() ||
+        property.nonData(constraints()))
+    {
+        // The property has been reconfigured as non-configurable, non-enumerable
+        // or non-writable.
+        return Ok();
     }
 
     // Don't optimize global lexical bindings if they aren't initialized at
     // compile time.
     if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
         return Ok();
 
     *emitted = true;
@@ -7358,47 +7359,23 @@ IonBuilder::getStaticName(bool* emitted,
             if (testSingletonProperty(staticObject, id) == singleton) {
                 pushConstant(ObjectValue(*singleton));
                 return Ok();
             }
         }
 
         // Try to inline properties that have never been overwritten.
         Value constantValue;
-        if (property.isSome() && property.ref().constant(constraints(), &constantValue)) {
+        if (property.constant(constraints(), &constantValue)) {
             pushConstant(constantValue);
             return Ok();
         }
     }
 
-    MOZ_TRY(loadStaticSlot(staticObject, barrier, types, slot));
-
-    // If the static object has a function object stored in this property,
-    // test that the result is that specific function. This is yet another
-    // technique for trying to force a property load to be a specific value,
-    // and is included because other mechanisms (property types and observed
-    // types) do not always work, especially in polymorphic framework code.
-    // We restrict this optimization to function properties, as they are less
-    // likely to change over time and are more likely to require precise
-    // information for inlining decisions.
-    if (!outermostBuilder()->script()->hadFrequentBailouts()) {
-        Value v = staticObject->as<NativeObject>().getSlot(slot);
-        if (v.isObject() &&
-            v.toObject().is<JSFunction>() &&
-            v.toObject().as<JSFunction>().isInterpreted())
-        {
-            JSObject* result = checkNurseryObject(&v.toObject().as<JSFunction>());
-            MDefinition* load = current->pop();
-            MInstruction* expected = MConstant::NewConstraintlessObject(alloc(), result);
-            expected->setResultTypeSet(MakeSingletonTypeSet(constraints(), result));
-            current->add(expected);
-            current->add(MGuardObjectIdentity::New(alloc(), load, expected, false, Bailout_LoadStaticObject));
-            current->push(expected);
-        }
-    }
+    MOZ_TRY(loadStaticSlot(staticObject, barrier, types, property.maybeTypes()->definiteSlot()));
 
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::loadStaticSlot(JSObject* staticObject, BarrierKind barrier, TemporaryTypeSet* types,
                            uint32_t slot)
 {
@@ -7416,17 +7393,17 @@ IonBuilder::loadStaticSlot(JSObject* sta
     }
 
     MInstruction* obj = constant(ObjectValue(*staticObject));
 
     MIRType rvalType = types->getKnownMIRType();
     if (barrier != BarrierKind::NoBarrier)
         rvalType = MIRType::Value;
 
-    return loadSlot(obj, slot, staticObject->as<NativeObject>().numFixedSlots(), rvalType, barrier, types);
+    return loadSlot(obj, slot, NumFixedSlots(staticObject), rvalType, barrier, types);
 }
 
 // Whether a write of the given value may need a post-write barrier for GC purposes.
 bool
 jit::NeedsPostBarrier(MDefinition* value)
 {
     if (!GetJitContext()->compartment->zone()->nurseryExists())
         return false;
@@ -7481,18 +7458,17 @@ IonBuilder::setStaticName(JSObject* stat
     // If the property has a known type, we may be able to optimize typed stores by not
     // storing the type tag.
     MIRType slotType = MIRType::None;
     MIRType knownType = property.knownMIRType(constraints());
     if (knownType != MIRType::Value)
         slotType = knownType;
 
     bool needsPreBarrier = property.needsBarrier(constraints());
-    return storeSlot(obj, property.maybeTypes()->definiteSlot(),
-                     staticObject->as<NativeObject>().numFixedSlots(),
+    return storeSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject),
                      value, needsPreBarrier, slotType);
 }
 
 JSObject*
 IonBuilder::testGlobalLexicalBinding(PropertyName* name)
 {
     MOZ_ASSERT(JSOp(*pc) == JSOP_BINDGNAME ||
                JSOp(*pc) == JSOP_GETGNAME ||
@@ -10423,22 +10399,16 @@ IonBuilder::jsop_getprop(PropertyName* n
             return Ok();
 
         // Try to inline a common property getter, or make a call.
         trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
         MOZ_TRY(getPropTryCommonGetter(&emitted, obj, name, types));
         if (emitted)
             return Ok();
 
-        // Try to optimize for loads from a specific object.
-        trackOptimizationAttempt(TrackedStrategy::GetProp_Static);
-        MOZ_TRY(getPropTryStaticAccess(&emitted, obj, name, barrier, types));
-        if (emitted)
-            return Ok();
-
         // Try to emit a monomorphic/polymorphic access based on baseline caches.
         trackOptimizationAttempt(TrackedStrategy::GetProp_InlineAccess);
         MOZ_TRY(getPropTryInlineAccess(&emitted, obj, name, barrier, types));
         if (emitted)
             return Ok();
 
         // Try to emit loads from a module namespace.
         trackOptimizationAttempt(TrackedStrategy::GetProp_ModuleNamespace);
@@ -11229,27 +11199,16 @@ PropertyShapesHaveSameSlot(const Baselin
             return nullptr;
         }
     }
 
     return firstShape;
 }
 
 AbortReasonOr<Ok>
-IonBuilder::getPropTryStaticAccess(bool* emitted, MDefinition* obj, PropertyName* name,
-                                   BarrierKind barrier, TemporaryTypeSet* types)
-{
-    if (!obj->isConstant() || obj->type() != MIRType::Object)
-        return Ok();
-
-    obj->setImplicitlyUsedUnchecked();
-    return getStaticName(emitted, &obj->toConstant()->toObject(), name);
-}
-
-AbortReasonOr<Ok>
 IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
                                    BarrierKind barrier, TemporaryTypeSet* types)
 {
     MOZ_ASSERT(*emitted == false);
 
     BaselineInspector::ReceiverVector receivers(alloc());
     BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc());
     if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups))
@@ -12672,59 +12631,21 @@ IonBuilder::walkEnvironmentChain(unsigne
         MInstruction* ins = MEnclosingEnvironment::New(alloc(), env);
         current->add(ins);
         env = ins;
     }
 
     return env;
 }
 
-static bool
-SearchEnvironmentChainForCallObject(JSObject* environment, JSScript* script, JSObject** pcall)
-{
-    while (environment && !environment->is<GlobalObject>()) {
-        if (environment->is<CallObject>() &&
-            environment->as<CallObject>().callee().nonLazyScript() == script)
-        {
-            *pcall = environment;
-            return true;
-        }
-        environment = environment->enclosingEnvironment();
-    }
-    return false;
-}
-
 bool
 IonBuilder::hasStaticEnvironmentObject(EnvironmentCoordinate ec, JSObject** pcall)
 {
     JSScript* outerScript = EnvironmentCoordinateFunctionScript(script(), pc);
-    if (!outerScript)
-        return false;
-
-    // JSOP_SETALIASEDVAR only emits a cache when the outer script is a run
-    // once script. To avoid problems with the generic jsop_setprop() paths,
-    // only use static environment objects when a baseline cache exists.
-    if (*pc == JSOP_SETALIASEDVAR && !outerScript->treatAsRunOnce())
-        return false;
-
-    // If the callee is a specific JSFunction then there is a specific
-    // environment object on its chain we can use.
-    if (inlineCallInfo_) {
-        MDefinition* calleeDef = inlineCallInfo_->fun();
-        if (calleeDef->isConstant()) {
-            JSFunction* callee = &calleeDef->toConstant()->toObject().template as<JSFunction>();
-            JSObject* environment = callee->environment();
-            if (SearchEnvironmentChainForCallObject(environment, outerScript, pcall))
-                return true;
-        }
-    }
-
-    // Otherwise, if the outer script will only run once then we can go looking
-    // for its call object.
-    if (!outerScript->treatAsRunOnce())
+    if (!outerScript || !outerScript->treatAsRunOnce())
         return false;
 
     TypeSet::ObjectKey* funKey =
         TypeSet::ObjectKey::get(outerScript->functionNonDelazifying());
     if (funKey->hasFlags(constraints(), OBJECT_FLAG_RUNONCE_INVALIDATED))
         return false;
 
     // The script this aliased var operation is accessing will run only once,
@@ -12735,28 +12656,42 @@ IonBuilder::hasStaticEnvironmentObject(E
     // Look for the call object on the current script's function's env chain.
     // If the current script is inner to the outer script and the function has
     // singleton type then it should show up here.
 
     MDefinition* envDef = current->getSlot(info().environmentChainSlot());
     envDef->setImplicitlyUsedUnchecked();
 
     JSObject* environment = script()->functionNonDelazifying()->environment();
-    if (SearchEnvironmentChainForCallObject(environment, outerScript, pcall))
-        return true;
+    while (environment && !environment->is<GlobalObject>()) {
+        if (environment->is<CallObject>() &&
+            environment->as<CallObject>().callee().nonLazyScript() == outerScript)
+        {
+            MOZ_ASSERT(environment->isSingleton());
+            *pcall = environment;
+            return true;
+        }
+        environment = environment->enclosingEnvironment();
+    }
 
     // Look for the call object on the current frame, if we are compiling the
     // outer script itself. Don't do this if we are at entry to the outer
     // script, as the call object we see will not be the real one --- after
     // entering the Ion code a different call object will be created.
 
     if (script() == outerScript && baselineFrame_ && info().osrPc()) {
         JSObject* singletonScope = baselineFrame_->singletonEnvChain;
-        if (SearchEnvironmentChainForCallObject(singletonScope, outerScript, pcall))
+        if (singletonScope &&
+            singletonScope->is<CallObject>() &&
+            singletonScope->as<CallObject>().callee().nonLazyScript() == outerScript)
+        {
+            MOZ_ASSERT(singletonScope->isSingleton());
+            *pcall = singletonScope;
             return true;
+        }
     }
 
     return true;
 }
 
 MDefinition*
 IonBuilder::getAliasedVar(EnvironmentCoordinate ec)
 {
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -241,18 +241,16 @@ class IonBuilder
     AbortReasonOr<Ok> getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name,
                                              BarrierKind barrier, TemporaryTypeSet* types);
     AbortReasonOr<Ok> getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name,
                                                 BarrierKind barrier, TemporaryTypeSet* types);
     AbortReasonOr<Ok> getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name,
                                         BarrierKind barrier, TemporaryTypeSet* types);
     AbortReasonOr<Ok> getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
                                              TemporaryTypeSet* types, bool innerized = false);
-    AbortReasonOr<Ok> getPropTryStaticAccess(bool* emitted, MDefinition* obj, PropertyName* name,
-                                             BarrierKind barrier, TemporaryTypeSet* types);
     AbortReasonOr<Ok> getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name,
                                              BarrierKind barrier, TemporaryTypeSet* types);
     AbortReasonOr<Ok> getPropTryTypedObject(bool* emitted, MDefinition* obj, PropertyName* name);
     AbortReasonOr<Ok> getPropTryScalarPropOfTypedObject(bool* emitted, MDefinition* typedObj,
                                                         int32_t fieldOffset,
                                                         TypedObjectPrediction fieldTypeReprs);
     AbortReasonOr<Ok> getPropTryReferencePropOfTypedObject(bool* emitted, MDefinition* typedObj,
                                                            int32_t fieldOffset,
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -130,22 +130,21 @@ IonGetPropertyIC::update(JSContext* cx, 
         ic->discardStubs(cx->zone());
 
     bool attached = false;
     if (ic->state().canAttachStub()) {
         // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it
         // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier
         // does not account for getters, so we should only attach a getter
         // stub if we inserted a type barrier.
-        CanAttachGetter canAttachGetter =
-            ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No;
         jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc();
         bool isTemporarilyUnoptimizable = false;
         GetPropIRGenerator gen(cx, outerScript, pc, ic->kind(), ic->state().mode(),
-                               &isTemporarilyUnoptimizable, val, idVal, val, canAttachGetter);
+                               &isTemporarilyUnoptimizable, val, idVal, val,
+                               ic->resultFlags());
         if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub())
             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
 
         if (!attached && !isTemporarilyUnoptimizable)
             ic->state().trackNotAttached();
     }
 
     if (!attached && ic->idempotent()) {
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -173,47 +173,47 @@ class IonIC
 
     void attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind,
                            IonScript* ionScript, bool* attached,
                            const PropertyTypeCheckInfo* typeCheckInfo = nullptr);
 };
 
 class IonGetPropertyIC : public IonIC
 {
+  private:
     LiveRegisterSet liveRegs_;
 
     TypedOrValueRegister value_;
     ConstantOrRegister id_;
     TypedOrValueRegister output_;
     Register maybeTemp_; // Might be InvalidReg.
 
-    bool monitoredResult_ : 1;
-    bool allowDoubleResult_ : 1;
+    GetPropertyResultFlags resultFlags_;
 
   public:
     IonGetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, TypedOrValueRegister value,
                      const ConstantOrRegister& id, TypedOrValueRegister output, Register maybeTemp,
-                     bool monitoredResult, bool allowDoubleResult)
+                     GetPropertyResultFlags resultFlags)
       : IonIC(kind),
         liveRegs_(liveRegs),
         value_(value),
         id_(id),
         output_(output),
         maybeTemp_(maybeTemp),
-        monitoredResult_(monitoredResult),
-        allowDoubleResult_(allowDoubleResult)
+        resultFlags_(resultFlags)
     { }
 
-    bool monitoredResult() const { return monitoredResult_; }
     TypedOrValueRegister value() const { return value_; }
     ConstantOrRegister id() const { return id_; }
     TypedOrValueRegister output() const { return output_; }
     Register maybeTemp() const { return maybeTemp_; }
     LiveRegisterSet liveRegs() const { return liveRegs_; }
-    bool allowDoubleResult() const { return allowDoubleResult_; }
+    GetPropertyResultFlags resultFlags() const { return resultFlags_; }
+    bool monitoredResult() const { return resultFlags_ & GetPropertyResultFlags::Monitored; }
+    bool allowDoubleResult() const { return resultFlags_ & GetPropertyResultFlags::AllowDouble; }
 
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic,
                                     HandleValue val, HandleValue idVal, MutableHandleValue res);
 };
 
 class IonSetPropertyIC : public IonIC
 {
     LiveRegisterSet liveRegs_;
--- a/js/src/jit/IonInstrumentation.h
+++ b/js/src/jit/IonInstrumentation.h
@@ -4,28 +4,28 @@
  * 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 jit_IonInstrumentatjit_h
 #define jit_IonInstrumentatjit_h
 
 namespace js {
 
-class GeckoProfiler;
+class GeckoProfilerRuntime;
 
 namespace jit {
 
 class MacroAssembler;
 
 typedef GeckoProfilerInstrumentation<MacroAssembler, Register> BaseInstrumentation;
 
 class IonInstrumentation : public BaseInstrumentation
 {
   public:
-    IonInstrumentation(GeckoProfiler* profiler, jsbytecode** pc)
+    IonInstrumentation(GeckoProfilerRuntime* profiler, jsbytecode** pc)
       : BaseInstrumentation(profiler)
     {
         MOZ_ASSERT(pc != nullptr);
     }
 };
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -137,19 +137,16 @@ enum BailoutKind
     Bailout_NonStringInputInvalidate,
 
     // Used for integer division, multiplication and modulo.
     // If there's a remainder, bails to return a double.
     // Can also signal overflow or result of -0.
     // Can also signal division by 0 (returns inf, a double).
     Bailout_DoubleOutput,
 
-    // Load of a value from a static object retrieved an unexpected value.
-    Bailout_LoadStaticObject,
-
     // END Invalid assumptions bailouts
 
 
     // A bailout at the very start of a function indicates that there may be
     // a type mismatch in the arguments that necessitates a reflow.
     Bailout_ArgumentCheck,
 
     // A bailout triggered by a bounds-check failure.
@@ -231,18 +228,16 @@ BailoutKindString(BailoutKind kind)
 
       // Bailouts caused by invalid assumptions.
       case Bailout_OverflowInvalidate:
         return "Bailout_OverflowInvalidate";
       case Bailout_NonStringInputInvalidate:
         return "Bailout_NonStringInputInvalidate";
       case Bailout_DoubleOutput:
         return "Bailout_DoubleOutput";
-      case Bailout_LoadStaticObject:
-        return "Bailout_LoadStaticObject";
 
       // Other bailouts.
       case Bailout_ArgumentCheck:
         return "Bailout_ArgumentCheck";
       case Bailout_BoundsCheck:
         return "Bailout_BoundsCheck";
       case Bailout_Detached:
         return "Bailout_Detached";
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3895,17 +3895,17 @@ LIRGenerator::visitCallBindVar(MCallBind
     define(lir, ins);
 }
 
 void
 LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity* ins)
 {
     LGuardObjectIdentity* guard = new(alloc()) LGuardObjectIdentity(useRegister(ins->object()),
                                                                     useRegister(ins->expected()));
-    assignSnapshot(guard, ins->bailoutKind());
+    assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard);
     add(guard, ins);
     redefine(ins, ins->object());
 }
 
 void
 LIRGenerator::visitGuardClass(MGuardClass* ins)
 {
     LDefinition t = temp();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -6192,28 +6192,19 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
             if (obj->unknownProperties())
                 break;
 
             HeapTypeSetKey property = obj->property(NameToId(name));
             if (property.maybeTypes()) {
                 TypeSet::TypeList types;
                 if (!property.maybeTypes()->enumerateTypes(&types))
                     break;
-                // If there is a single possible type for the property,
-                // optimistically add it to the observed set. Don't do this
-                // for the special uninitialized lexical type, which will
-                // never actually be observed here and will cause problems
-                // downstream during compilation.
-                if (types.length() == 1 &&
-                    (!types[0].isPrimitive() ||
-                     types[0].primitive() != JSVAL_TYPE_MAGIC))
-                {
+                if (types.length() == 1) {
                     // Note: the return value here is ignored.
                     observed->addType(types[0], GetJitContext()->temp->lifoAlloc());
-                    break;
                 }
                 break;
             }
 
             if (!obj->proto().isObject())
                 break;
             obj = TypeSet::ObjectKey::get(obj->proto().toObject());
         } while (obj);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11485,47 +11485,39 @@ class MGuardObjectGroup
 };
 
 // Guard on an object's identity, inclusively or exclusively.
 class MGuardObjectIdentity
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
 {
     bool bailOnEquality_;
-    BailoutKind bailoutKind_;
-
-    MGuardObjectIdentity(MDefinition* obj, MDefinition* expected, bool bailOnEquality,
-                         BailoutKind bailoutKind = Bailout_ObjectIdentityOrTypeGuard)
+
+    MGuardObjectIdentity(MDefinition* obj, MDefinition* expected, bool bailOnEquality)
       : MBinaryInstruction(obj, expected),
-        bailOnEquality_(bailOnEquality),
-        bailoutKind_(bailoutKind)
+        bailOnEquality_(bailOnEquality)
     {
         setGuard();
         setMovable();
         setResultType(MIRType::Object);
     }
 
   public:
     INSTRUCTION_HEADER(GuardObjectIdentity)
     TRIVIAL_NEW_WRAPPERS
     NAMED_OPERANDS((0, object), (1, expected))
 
     bool bailOnEquality() const {
         return bailOnEquality_;
     }
-    BailoutKind bailoutKind() const {
-        return bailoutKind_;
-    }
     bool congruentTo(const MDefinition* ins) const override {
         if (!ins->isGuardObjectIdentity())
             return false;
         if (bailOnEquality() != ins->toGuardObjectIdentity()->bailOnEquality())
             return false;
-        if (bailoutKind() != ins->toGuardObjectIdentity()->bailoutKind())
-            return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
 };
 
 // Guard on an object's class.
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -2064,17 +2064,18 @@ DoGetPropFallback(JSContext* cx, Baselin
 
     if (stub->state().maybeTransition())
         stub->discardStubs(cx);
 
     bool attached = false;
     if (stub->state().canAttachStub()) {
         RootedValue idVal(cx, StringValue(name));
         GetPropIRGenerator gen(cx, script, pc, CacheKind::GetProp, stub->state().mode(),
-                               &isTemporarilyUnoptimizable, val, idVal, val, CanAttachGetter::Yes);
+                               &isTemporarilyUnoptimizable, val, idVal, val,
+                               GetPropertyResultFlags::All);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         ICStubEngine::Baseline, script,
                                                         stub, &attached);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 if (gen.shouldNotePreliminaryObjectStub())
@@ -2135,17 +2136,17 @@ DoGetPropSuperFallback(JSContext* cx, Ba
     if (stub->state().maybeTransition())
         stub->discardStubs(cx);
 
     bool attached = false;
     if (stub->state().canAttachStub()) {
         RootedValue idVal(cx, StringValue(name));
         GetPropIRGenerator gen(cx, script, pc, CacheKind::GetPropSuper, stub->state().mode(),
                                &isTemporarilyUnoptimizable, val, idVal, receiver,
-                               CanAttachGetter::Yes);
+                               GetPropertyResultFlags::All);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         ICStubEngine::Baseline, script,
                                                         stub, &attached);
             if (newStub) {
                 JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
                 if (gen.shouldNotePreliminaryObjectStub())
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -28,17 +28,17 @@ static const FloatRegisterSet NonVolatil
                      (1ULL << FloatRegisters::d10) |
                      (1ULL << FloatRegisters::d11) |
                      (1ULL << FloatRegisters::d12) |
                      (1ULL << FloatRegisters::d13) |
                      (1ULL << FloatRegisters::d14) |
                      (1ULL << FloatRegisters::d15));
 
 static void
-GenerateReturn(MacroAssembler& masm, int returnCode, GeckoProfiler* prof)
+GenerateReturn(MacroAssembler& masm, int returnCode)
 {
     // Restore non-volatile floating point registers.
     masm.transferMultipleByRuns(NonVolatileFloatRegs, IsLoad, StackPointer, IA);
 
     // Get rid of padding word.
     masm.addPtr(Imm32(sizeof(void*)), sp);
 
     // Set up return value
@@ -372,17 +372,17 @@ JitRuntime::generateEnterJIT(JSContext* 
     // :TODO: Optimize storeValue with:
     // We're using a load-double here. In order for that to work, the data needs
     // to be stored in two consecutive registers, make sure this is the case
     //   MOZ_ASSERT(JSReturnReg_Type.code() == JSReturnReg_Data.code()+1);
     //   aasm->as_extdtr(IsStore, 64, true, Offset,
     //                   JSReturnReg_Data, EDtrAddr(r5, EDtrOffImm(0)));
 
     // Restore non-volatile registers and return.
-    GenerateReturn(masm, true, &cx->runtime()->geckoProfiler());
+    GenerateReturn(masm, true);
 
     Linker linker(masm);
     AutoFlushICache afc("EnterJIT");
     JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE);
 
 #ifdef JS_ION_PERF
     writePerfSpewerJitCodeProfile(code, "EnterJIT");
 #endif
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1025,16 +1025,33 @@ JS_ResumeCooperativeContext(JSContext* c
 
 // Create a new context on this thread for cooperative multithreading in the
 // same runtime as siblingContext. Called on a runtime (as indicated by
 // siblingContet) which has no active context, on success the new context will
 // become the runtime's active context.
 extern JS_PUBLIC_API(JSContext*)
 JS_NewCooperativeContext(JSContext* siblingContext);
 
+namespace JS {
+
+// Class to relinquish exclusive access to all zone groups in use by this
+// thread. This allows other cooperative threads to enter the zone groups
+// and modify their contents.
+struct AutoRelinquishZoneGroups
+{
+    explicit AutoRelinquishZoneGroups(JSContext* cx);
+    ~AutoRelinquishZoneGroups();
+
+  private:
+    JSContext* cx;
+    mozilla::Vector<void*> enterList;
+};
+
+} // namespace JS
+
 // Destroy a context allocated with JS_NewContext or JS_NewCooperativeContext.
 // The context must be the current active context in the runtime, and after
 // this call the runtime will have no active context.
 extern JS_PUBLIC_API(void)
 JS_DestroyContext(JSContext* cx);
 
 JS_PUBLIC_API(void*)
 JS_GetContextPrivate(JSContext* cx);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1318,17 +1318,17 @@ ArrayJoinKernel(JSContext* cx, Separator
 // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce
 // 22.1.3.13 Array.prototype.join ( separator )
 bool
 js::array_join(JSContext* cx, unsigned argc, Value* vp)
 {
     if (!CheckRecursionLimit(cx))
         return false;
 
-    AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.join");
+    AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.join");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     AutoCycleDetector detector(cx, obj);
@@ -1578,17 +1578,17 @@ ArrayReverseDenseKernel(JSContext* cx, H
 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");
+    AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.reverse");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     // Step 2.
@@ -2283,17 +2283,17 @@ js::NewbornArrayPush(JSContext* cx, Hand
     return true;
 }
 
 // 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");
+    AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.push");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     // Step 2.
@@ -2340,17 +2340,17 @@ js::array_push(JSContext* cx, unsigned a
     return SetLengthProperty(cx, obj, newlength);
 }
 
 // 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");
+    AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.pop");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     // Step 2.
@@ -2445,17 +2445,17 @@ ArrayShiftDenseKernel(JSContext* cx, Han
 DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel,
                              JSContext*, HandleObject, MutableHandleValue);
 
 // 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");
+    AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.shift");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     // Step 2.
@@ -2521,17 +2521,17 @@ js::array_shift(JSContext* cx, unsigned 
     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");
+    AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.unshift");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     // Step 2.
@@ -2764,17 +2764,17 @@ CopyArrayElements(JSContext* cx, HandleO
             return false;
     }
     return true;
 }
 
 static bool
 array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed)
 {
-    AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.splice");
+    AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.splice");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     /* Step 2. */
@@ -3272,17 +3272,17 @@ ArraySliceOrdinary(JSContext* cx, Handle
     rval.setObject(*narr);
     return true;
 }
 
 /* ES 2016 draft Mar 25, 2016 22.1.3.23. */
 bool
 js::array_slice(JSContext* cx, unsigned argc, Value* vp)
 {
-    AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.slice");
+    AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.slice");
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* Step 1. */
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     /* Step 2. */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1585,16 +1585,17 @@ JSContext::sizeOfExcludingThis(mozilla::
      */
     return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf);
 }
 
 void
 JSContext::trace(JSTracer* trc)
 {
     cycleDetectorVector().trace(trc);
+    geckoProfiler().trace(trc);
 
     if (trc->isMarkingTracer() && compartment_)
         compartment_->mark();
 }
 
 void*
 JSContext::stackLimitAddressForJitCode(JS::StackKind kind)
 {
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -385,19 +385,16 @@ struct JSContext : public JS::RootingCon
     }
     static size_t offsetOfActivation() {
         return offsetof(JSContext, activation_);
     }
 
     js::Activation* profilingActivation() const {
         return profilingActivation_;
     }
-    void* addressOfProfilingActivation() {
-        return (void*) &profilingActivation_;
-    }
     static size_t offsetOfProfilingActivation() {
         return offsetof(JSContext, profilingActivation_);
      }
 
   private:
     /* Space for interpreter frames. */
     js::ThreadLocalData<js::InterpreterStack> interpreterStack_;
 
@@ -598,16 +595,22 @@ struct JSContext : public JS::RootingCon
     }
     void disableProfilerSampling() {
         suppressProfilerSampling = true;
     }
     void enableProfilerSampling() {
         suppressProfilerSampling = false;
     }
 
+  private:
+    /* Gecko profiling metadata */
+    js::UnprotectedData<js::GeckoProfilerThread> geckoProfiler_;
+  public:
+    js::GeckoProfilerThread& geckoProfiler() { return geckoProfiler_.ref(); }
+
 #if defined(XP_DARWIN)
     js::wasm::MachExceptionHandler wasmMachExceptionHandler;
 #endif
 
     /* Temporary arena pool used while compiling and decompiling. */
     static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024;
   private:
     js::ThreadLocalData<js::LifoAlloc> tempLifoAlloc_;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -6211,17 +6211,17 @@ AllNurseriesAreEmpty(JSRuntime* rt)
 }
 #endif
 
 /* 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(TlsContext.get(), HeapStateToLabel(heapState), ProfileEntry::Category::GC)
 {
     MOZ_ASSERT(prevState == JS::HeapState::Idle);
     MOZ_ASSERT(heapState != JS::HeapState::Idle);
     MOZ_ASSERT_IF(heapState == JS::HeapState::MajorCollecting, AllNurseriesAreEmpty(rt));
     TlsContext.get()->heapState = heapState;
 }
 
 AutoTraceSession::~AutoTraceSession()
--- a/js/src/jsprf.cpp
+++ b/js/src/jsprf.cpp
@@ -24,21 +24,16 @@ JS_PUBLIC_API(JS::UniqueChars) JS_smprin
 {
     va_list ap;
     va_start(ap, fmt);
     JSSmprintfPointer result = mozilla::Vsmprintf<js::SystemAllocPolicy>(fmt, ap);
     va_end(ap);
     return JS::UniqueChars(result.release());
 }
 
-JS_PUBLIC_API(void) JS_smprintf_free(char* mem)
-{
-    mozilla::SmprintfFree<js::SystemAllocPolicy>(mem);
-}
-
 JS_PUBLIC_API(JS::UniqueChars) JS_sprintf_append(JS::UniqueChars&& last, const char* fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
     JSSmprintfPointer lastPtr(last.release());
     JSSmprintfPointer result =
         mozilla::VsmprintfAppend<js::SystemAllocPolicy>(Move(lastPtr), fmt, ap);
     va_end(ap);
--- a/js/src/jsprf.h
+++ b/js/src/jsprf.h
@@ -15,18 +15,16 @@
 #include "js/Utility.h"
 
 /* Wrappers for mozilla::Smprintf and friends that are used throughout
    JS.  */
 
 extern JS_PUBLIC_API(JS::UniqueChars) JS_smprintf(const char* fmt, ...)
     MOZ_FORMAT_PRINTF(1, 2);
 
-extern JS_PUBLIC_API(void) JS_smprintf_free(char* mem);
-
 extern JS_PUBLIC_API(JS::UniqueChars) JS_sprintf_append(JS::UniqueChars&& last,
                                                         const char* fmt, ...)
      MOZ_FORMAT_PRINTF(2, 3);
 
 extern JS_PUBLIC_API(JS::UniqueChars) JS_vsmprintf(const char* fmt, va_list ap)
     MOZ_FORMAT_PRINTF(1, 0);
 extern JS_PUBLIC_API(JS::UniqueChars) JS_vsprintf_append(JS::UniqueChars&& last,
                                                          const char* fmt, va_list ap)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -4194,34 +4194,37 @@ JSScript::argumentsOptimizationFailed(JS
      * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are
      * three things that need fixup:
      *  - there may be any number of activations of this script that don't have
      *    an argsObj that now need one.
      *  - jit code compiled (and possible active on the stack) with the static
      *    assumption of !script->needsArgsObj();
      *  - type inference data for the script assuming script->needsArgsObj
      */
-    for (AllScriptFramesIter i(cx); !i.done(); ++i) {
-        /*
-         * We cannot reliably create an arguments object for Ion activations of
-         * this script.  To maintain the invariant that "script->needsArgsObj
-         * implies fp->hasArgsObj", the Ion bail mechanism will create an
-         * arguments object right after restoring the BaselineFrame and before
-         * entering Baseline code (in jit::FinishBailoutToBaseline).
-         */
-        if (i.isIon())
-            continue;
-        AbstractFramePtr frame = i.abstractFramePtr();
-        if (frame.isFunctionFrame() && frame.script() == script) {
-            /* We crash on OOM since cleaning up here would be complicated. */
-            AutoEnterOOMUnsafeRegion oomUnsafe;
-            ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame);
-            if (!argsobj)
-                oomUnsafe.crash("JSScript::argumentsOptimizationFailed");
-            SetFrameArgumentsObject(cx, frame, script, argsobj);
+    JSRuntime::AutoProhibitActiveContextChange apacc(cx->runtime());
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        for (AllScriptFramesIter i(cx, target); !i.done(); ++i) {
+            /*
+             * We cannot reliably create an arguments object for Ion activations of
+             * this script.  To maintain the invariant that "script->needsArgsObj
+             * implies fp->hasArgsObj", the Ion bail mechanism will create an
+             * arguments object right after restoring the BaselineFrame and before
+             * entering Baseline code (in jit::FinishBailoutToBaseline).
+             */
+            if (i.isIon())
+                continue;
+            AbstractFramePtr frame = i.abstractFramePtr();
+            if (frame.isFunctionFrame() && frame.script() == script) {
+                /* We crash on OOM since cleaning up here would be complicated. */
+                AutoEnterOOMUnsafeRegion oomUnsafe;
+                ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame);
+                if (!argsobj)
+                    oomUnsafe.crash("JSScript::argumentsOptimizationFailed");
+                SetFrameArgumentsObject(cx, frame, script, argsobj);
+            }
         }
     }
 
     return true;
 }
 
 bool
 JSScript::formalIsAliased(unsigned argSlot)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1483,18 +1483,17 @@ Evaluate(JSContext* cx, unsigned argc, V
     bool saveIncrementalBytecode = false;
     bool assertEqBytecode = false;
     RootedObject callerGlobal(cx, cx->global());
 
     options.setIntroductionType("js shell evaluate")
            .setFileAndLine("@evaluate", 1);
 
     global = JS_GetGlobalForObject(cx, &args.callee());
-    if (!global)
-        return false;
+    MOZ_ASSERT(global);
 
     if (args.length() == 2) {
         RootedObject opts(cx, &args[1].toObject());
         RootedValue v(cx);
 
         if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
             return false;
 
@@ -1524,16 +1523,51 @@ Evaluate(JSContext* cx, unsigned argc, V
             }
             if (!global || !(JS_GetClass(global)->flags & JSCLASS_IS_GLOBAL)) {
                 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
                                           "\"global\" passed to evaluate()", "not a global object");
                 return false;
             }
         }
 
+        if (!JS_GetProperty(cx, opts, "zoneGroup", &v))
+            return false;
+        if (!v.isUndefined()) {
+            if (global != JS_GetGlobalForObject(cx, &args.callee())) {
+                JS_ReportErrorASCII(cx, "zoneGroup and global cannot both be specified.");
+                return false;
+            }
+
+            // Find all eligible globals to execute in: any global in another
+            // zone group which has not been entered by a cooperative thread.
+            JS::AutoObjectVector eligibleGlobals(cx);
+            for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) {
+                if (!c->zone()->group()->ownerContext().context() &&
+                    c->maybeGlobal() &&
+                    !cx->runtime()->isSelfHostingGlobal(c->maybeGlobal()))
+                {
+                    if (!eligibleGlobals.append(c->maybeGlobal()))
+                        return false;
+                }
+            }
+
+            if (eligibleGlobals.empty()) {
+                JS_ReportErrorASCII(cx, "zoneGroup can only be used if another"
+                                    " cooperative thread has called cooperativeYield(true).");
+                return false;
+            }
+
+            // Pick an eligible global to use based on the value of the zoneGroup property.
+            int32_t which;
+            if (!ToInt32(cx, v, &which))
+                return false;
+            which = Min<int32_t>(Max(which, 0), eligibleGlobals.length() - 1);
+            global = eligibleGlobals[which];
+        }
+
         if (!JS_GetProperty(cx, opts, "catchTermination", &v))
             return false;
         if (!v.isUndefined())
             catchTermination = ToBoolean(v);
 
         if (!JS_GetProperty(cx, opts, "loadBytecode", &v))
             return false;
         if (!v.isUndefined())
@@ -3290,19 +3324,39 @@ CooperativeYieldThread(JSContext* cx, un
         return false;
     }
 
     if (cooperationState->singleThreaded) {
         JS_ReportErrorASCII(cx, "Yielding is not allowed while single threaded");
         return false;
     }
 
-    CooperativeBeginWait(cx);
-    CooperativeYield();
-    CooperativeEndWait(cx);
+    // To avoid contention issues between threads, yields are not allowed while
+    // a thread has access to zone groups other than its original one, i.e. if
+    // the thread is inside an evaluate() call with a different zone group.
+    // This is not a limit which the browser has, but is necessary in the
+    // shell: the shell can have arbitrary interleavings between cooperative
+    // threads, whereas the browser has more control over which threads are
+    // running at different times.
+    for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) {
+        if (group->ownerContext().context() == cx && group != cx->zone()->group()) {
+            JS_ReportErrorASCII(cx, "Yielding is not allowed while owning multiple zone groups");
+            return false;
+        }
+    }
+
+    {
+        Maybe<JS::AutoRelinquishZoneGroups> artzg;
+        if ((args.length() > 0) && ToBoolean(args[0]))
+            artzg.emplace(cx);
+
+        CooperativeBeginWait(cx);
+        CooperativeYield();
+        CooperativeEndWait(cx);
+    }
 
     args.rval().setUndefined();
     return true;
 }
 
 static void
 CooperativeBeginSingleThreadedExecution(JSContext* cx)
 {
@@ -3327,16 +3381,35 @@ CooperativeBeginSingleThreadedExecution(
 
 static void
 CooperativeEndSingleThreadedExecution(JSContext* cx)
 {
     if (cooperationState)
         cooperationState->singleThreaded = false;
 }
 
+static bool
+EnsureGeckoProfilingStackInstalled(JSContext* cx, ShellContext* sc)
+{
+    if (cx->geckoProfiler().installed()) {
+        MOZ_ASSERT(sc->geckoProfilingStack);
+        return true;
+    }
+
+    MOZ_ASSERT(!sc->geckoProfilingStack);
+    sc->geckoProfilingStack = MakeUnique<PseudoStack>();
+    if (!sc->geckoProfilingStack) {
+        JS_ReportOutOfMemory(cx);
+        return false;
+    }
+
+    SetContextProfilingStack(cx, sc->geckoProfilingStack.get());
+    return true;
+}
+
 struct WorkerInput
 {
     JSRuntime* parentRuntime;
     JSContext* siblingContext;
     char16_t* chars;
     size_t length;
 
     WorkerInput(JSRuntime* parentRuntime, char16_t* chars, size_t length)
@@ -3395,16 +3468,20 @@ WorkerMain(void* arg)
         js::UseInternalJobQueues(cx);
 
         if (!JS::InitSelfHostedCode(cx))
             return;
 
         environmentPreparer.emplace(cx);
     } else {
         JS_AddInterruptCallback(cx, ShellInterruptCallback);
+
+        // The Gecko Profiler requires that all cooperating contexts have
+        // profiling stacks installed.
+        MOZ_ALWAYS_TRUE(EnsureGeckoProfilingStackInstalled(cx, sc.get()));
     }
 
     do {
         JSAutoRequest ar(cx);
 
         JS::CompartmentOptions compartmentOptions;
         SetStandardCompartmentOptions(compartmentOptions);
         if (input->siblingContext)
@@ -3425,21 +3502,16 @@ WorkerMain(void* arg)
         if (!JS::Compile(cx, options, input->chars, input->length, &script))
             break;
         RootedValue result(cx);
         JS_ExecuteScript(cx, script, &result);
     } while (0);
 
     KillWatchdog(cx);
     JS_SetGrayGCRootsTracer(cx, nullptr, nullptr);
-
-    if (sc->geckoProfilingStack) {
-        MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
-        SetContextProfilingStack(cx, nullptr);
-    }
 }
 
 // Workers can spawn other workers, so we need a lock to access workerThreads.
 static Mutex* workerThreadsLock = nullptr;
 static Vector<js::Thread*, 0, SystemAllocPolicy> workerThreads;
 
 class MOZ_RAII AutoLockWorkerThreads : public LockGuard<Mutex>
 {
@@ -5205,116 +5277,80 @@ static bool
 IsLatin1(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     bool isLatin1 = args.get(0).isString() && args[0].toString()->hasLatin1Chars();
     args.rval().setBoolean(isLatin1);
     return true;
 }
 
-static bool
-EnsureGeckoProfilingStackInstalled(JSContext* cx, ShellContext* sc)
-{
-    if (cx->runtime()->geckoProfiler().installed()) {
-        if (!sc->geckoProfilingStack) {
-            JS_ReportErrorASCII(cx, "Profiler already installed by another context");
-            return false;
-        }
-
-        return true;
-    }
-
-    MOZ_ASSERT(!sc->geckoProfilingStack);
-    sc->geckoProfilingStack = MakeUnique<PseudoStack>();
-    if (!sc->geckoProfilingStack) {
-        JS_ReportOutOfMemory(cx);
-        return false;
-    }
-
-    SetContextProfilingStack(cx, sc->geckoProfilingStack.get());
+// Set the profiling stack for each cooperating context in a runtime.
+static bool
+EnsureAllContextProfilingStacks(JSContext* cx)
+{
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        ShellContext* sc = GetShellContext(target.context());
+        if (!EnsureGeckoProfilingStackInstalled(target.context(), sc))
+            return false;
+    }
+
     return true;
 }
 
 static bool
 EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    ShellContext* sc = GetShellContext(cx);
-
-    if (!EnsureGeckoProfilingStackInstalled(cx, sc))
-        return false;
-
-    // Disable before re-enabling; see the assertion in
-    // |GeckoProfiler::setProfilingStack|.
-    if (cx->runtime()->geckoProfiler().installed())
-        MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
+    if (!EnsureAllContextProfilingStacks(cx))
+        return false;
 
     cx->runtime()->geckoProfiler().enableSlowAssertions(false);
-    if (!cx->runtime()->geckoProfiler().enable(true)) {
-        JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler");
-        return false;
-    }
+    cx->runtime()->geckoProfiler().enable(true);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 EnableGeckoProfilingWithSlowAssertions(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setUndefined();
 
-    ShellContext* sc = GetShellContext(cx);
-
-    if (!EnsureGeckoProfilingStackInstalled(cx, sc))
-        return false;
-
     if (cx->runtime()->geckoProfiler().enabled()) {
         // If profiling already enabled with slow assertions disabled,
         // this is a no-op.
         if (cx->runtime()->geckoProfiler().slowAssertionsEnabled())
             return true;
 
         // Slow assertions are off.  Disable profiling before re-enabling
         // with slow assertions on.
-        MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
-    }
-
-    // Disable before re-enabling; see the assertion in |GeckoProfiler::setProfilingStack|.
-    if (cx->runtime()->geckoProfiler().installed())
-        MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
+        cx->runtime()->geckoProfiler().enable(false);
+    }
+
+    if (!EnsureAllContextProfilingStacks(cx))
+        return false;
 
     cx->runtime()->geckoProfiler().enableSlowAssertions(true);
-    if (!cx->runtime()->geckoProfiler().enable(true)) {
-        JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler");
-        return false;
-    }
+    cx->runtime()->geckoProfiler().enable(true);
 
     return true;
 }
 
 static bool
 DisableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setUndefined();
 
-    ShellContext* sc = GetShellContext(cx);
-
-    if (!cx->runtime()->geckoProfiler().installed())
+    if (!cx->runtime()->geckoProfiler().enabled())
         return true;
 
-    if (!sc->geckoProfilingStack) {
-        JS_ReportErrorASCII(cx, "Profiler was not installed by this context");
-        return false;
-    }
-
-    MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false));
+    cx->runtime()->geckoProfiler().enable(false);
     return true;
 }
 
 // Global mailbox that is used to communicate a SharedArrayBuffer
 // value from one worker to another.
 //
 // For simplicity we store only the SharedArrayRawBuffer; retaining
 // the SAB object would require per-runtime storage, and would have no
@@ -6148,16 +6184,18 @@ static const JSFunctionSpecWithHelp shel
 "  Evaluate code as though it were the contents of a file.\n"
 "  options is an optional object that may have these properties:\n"
 "      isRunOnce: use the isRunOnce compiler option (default: false)\n"
 "      noScriptRval: use the no-script-rval compiler option (default: false)\n"
 "      fileName: filename for error messages and debug info\n"
 "      lineNumber: starting line number for error messages and debug info\n"
 "      columnNumber: starting column number for error messages and debug info\n"
 "      global: global in which to execute the code\n"
+"      zoneGroup: pick a global from another zone group with no current context\n"
+"         to execute the code in\n"
 "      newContext: if true, create and use a new cx (default: false)\n"
 "      catchTermination: if true, catch termination (failure without\n"
 "         an exception value, as for slow scripts or out-of-memory)\n"
 "         and return 'terminated'\n"
 "      element: if present with value |v|, convert |v| to an object |o| and\n"
 "         mark the source as being attached to the DOM element |o|. If the\n"
 "         property is omitted or |v| is null, don't attribute the source to\n"
 "         any DOM element.\n"
@@ -6286,19 +6324,21 @@ static const JSFunctionSpecWithHelp shel
     JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0,
 "evalInWorker(str)",
 "  Evaluate 'str' in a separate thread with its own runtime.\n"),
 
     JS_FN_HELP("evalInCooperativeThread", EvalInCooperativeThread, 1, 0,
 "evalInCooperativeThread(str)",
 "  Evaluate 'str' in a separate cooperatively scheduled thread using the same runtime.\n"),
 
-    JS_FN_HELP("cooperativeYield", CooperativeYieldThread, 0, 0,
-"evalInCooperativeThread()",
-"  Yield execution to another cooperatively scheduled thread using the same runtime.\n"),
+    JS_FN_HELP("cooperativeYield", CooperativeYieldThread, 1, 0,
+"cooperativeYield(leaveZoneGroup)",
+"  Yield execution to another cooperatively scheduled thread using the same runtime.\n"
+"  If leaveZoneGroup is specified then other threads may execute code in the\n"
+"  current thread's zone group via evaluate(..., {zoneGroup:N}).\n"),
 
     JS_FN_HELP("getSharedArrayBuffer", GetSharedArrayBuffer, 0, 0,
 "getSharedArrayBuffer()",
 "  Retrieve the SharedArrayBuffer object from the cross-worker mailbox.\n"
 "  The object retrieved may not be identical to the object that was\n"
 "  installed, but it references the same shared memory.\n"
 "  getSharedArrayBuffer performs an ordering memory barrier.\n"),
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2590,31 +2590,33 @@ 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(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(cx, &iter); inlineIter.more(); ++inlineIter)
-                    MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs);
-                break;
-              default:;
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        for (JitActivationIterator actIter(cx, target); !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(cx, &iter); inlineIter.more(); ++inlineIter)
+                        MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs);
+                    break;
+                  default:;
+                }
             }
         }
     }
 
     // Iterate through the scripts again and finish discarding
     // BaselineScripts. This must be done as a separate phase as we can only
     // discard the BaselineScript on scripts that have no IonScript.
     for (size_t i = 0; i < scripts.length(); i++) {
--- a/js/src/vm/GeckoProfiler-inl.h
+++ b/js/src/vm/GeckoProfiler-inl.h
@@ -4,20 +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 vm_GeckoProfiler_inl_h
 #define vm_GeckoProfiler_inl_h
 
 #include "vm/GeckoProfiler.h"
 
+#include "jscntxt.h"
+
 #include "vm/Runtime.h"
 
 namespace js {
 
+inline void
+GeckoProfilerThread::updatePC(JSContext* cx, JSScript* script, jsbytecode* pc)
+{
+    if (!cx->runtime()->geckoProfiler().enabled())
+        return;
+
+    uint32_t sp = pseudoStack_->stackPointer;
+    if (sp - 1 < PseudoStack::MaxEntries) {
+        MOZ_ASSERT(sp > 0);
+        MOZ_ASSERT(pseudoStack_->entries[sp - 1].rawScript() == script);
+        pseudoStack_->entries[sp - 1].setPC(pc);
+    }
+}
+
 /*
  * 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);
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -20,48 +20,49 @@
 #include "vm/StringBuffer.h"
 
 #include "jsgcinlines.h"
 
 using namespace js;
 
 using mozilla::DebugOnly;
 
-GeckoProfiler::GeckoProfiler(JSRuntime* rt)
+GeckoProfilerThread::GeckoProfilerThread()
+  : pseudoStack_(nullptr)
+{
+}
+
+GeckoProfilerRuntime::GeckoProfilerRuntime(JSRuntime* rt)
   : rt(rt),
     strings(mutexid::GeckoProfilerStrings),
-    pseudoStack_(nullptr),
     slowAssertions(false),
     enabled_(false),
     eventMarker_(nullptr)
 {
     MOZ_ASSERT(rt != nullptr);
 }
 
 bool
-GeckoProfiler::init()
+GeckoProfilerRuntime::init()
 {
     auto locked = strings.lock();
     if (!locked->init())
         return false;
 
     return true;
 }
 
 void
-GeckoProfiler::setProfilingStack(PseudoStack* pseudoStack)
+GeckoProfilerThread::setProfilingStack(PseudoStack* pseudoStack)
 {
-    MOZ_ASSERT_IF(pseudoStack_, !enabled());
-    MOZ_ASSERT(strings.lock()->initialized());
-
     pseudoStack_ = pseudoStack;
 }
 
 void
-GeckoProfiler::setEventMarker(void (*fn)(const char*))
+GeckoProfilerRuntime::setEventMarker(void (*fn)(const char*))
 {
     eventMarker_ = fn;
 }
 
 /* Get a pointer to the top-most profiling frame, given the exit frame pointer. */
 static void*
 GetTopProfilingJitFrame(Activation* act)
 {
@@ -73,34 +74,29 @@ GetTopProfilingJitFrame(Activation* act)
     if (!exitFP)
         return nullptr;
 
     jit::JitProfilingFrameIterator iter(exitFP);
     MOZ_ASSERT(!iter.done());
     return iter.fp();
 }
 
-bool
-GeckoProfiler::enable(bool enabled)
+void
+GeckoProfilerRuntime::enable(bool enabled)
 {
-    MOZ_ASSERT(installed());
+#ifdef DEBUG
+    // All cooperating contexts must have profile stacks installed before the
+    // profiler can be enabled. Cooperating threads created while the profiler
+    // is enabled must have stacks set before they execute any JS.
+    for (const CooperatingContext& target : rt->cooperatingContexts())
+        MOZ_ASSERT(target.context()->geckoProfiler().installed());
+#endif
 
     if (enabled_ == enabled)
-        return true;
-
-    // Execution in the runtime must be single threaded if the Gecko profiler
-    // is enabled. There is only a single profiler stack in the runtime, from
-    // which entries must be added/removed in a LIFO fashion.
-    JSContext* cx = rt->activeContextFromOwnThread();
-    if (enabled) {
-        if (!rt->beginSingleThreadedExecution(cx))
-            return false;
-    } else {
-        rt->endSingleThreadedExecution(cx);
-    }
+        return;
 
     /*
      * Ensure all future generated code will be instrumented, or that all
      * currently instrumented code is discarded
      */
     ReleaseAllJITCode(rt->defaultFreeOp());
 
     // This function is called when the Gecko profiler makes a new Sampler
@@ -158,69 +154,67 @@ GeckoProfiler::enable(bool enabled)
         }
     }
 
     // WebAssembly code does not need to be released, but profiling string
     // labels have to be generated so that they are available during async
     // profiling stack iteration.
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
         c->wasm.ensureProfilingLabels(enabled);
-
-    return true;
 }
 
 /* Lookup the string for the function/script, creating one if necessary */
 const char*
-GeckoProfiler::profileString(JSScript* script, JSFunction* maybeFun)
+GeckoProfilerRuntime::profileString(JSScript* script, JSFunction* maybeFun)
 {
     auto locked = strings.lock();
     MOZ_ASSERT(locked->initialized());
 
     ProfileStringMap::AddPtr s = locked->lookupForAdd(script);
 
     if (!s) {
         auto str = allocProfileString(script, maybeFun);
         if (!str || !locked->add(s, script, mozilla::Move(str)))
             return nullptr;
     }
 
     return s->value().get();
 }
 
 void
-GeckoProfiler::onScriptFinalized(JSScript* script)
+GeckoProfilerRuntime::onScriptFinalized(JSScript* script)
 {
     /*
      * This function is called whenever a script is destroyed, regardless of
      * whether profiling has been turned on, so don't invoke a function on an
      * invalid hash set. Also, even if profiling was enabled but then turned
      * off, we still want to remove the string, so no check of enabled() is
      * done.
      */
     auto locked = strings.lock();
     if (!locked->initialized())
         return;
     if (ProfileStringMap::Ptr entry = locked->lookup(script))
         locked->remove(entry);
 }
 
 void
-GeckoProfiler::markEvent(const char* event)
+GeckoProfilerRuntime::markEvent(const char* event)
 {
     MOZ_ASSERT(enabled());
     if (eventMarker_) {
         JS::AutoSuppressGCAnalysis nogc;
         eventMarker_(event);
     }
 }
 
 bool
-GeckoProfiler::enter(JSContext* cx, JSScript* script, JSFunction* maybeFun)
+GeckoProfilerThread::enter(JSContext* cx, JSScript* script, JSFunction* maybeFun)
 {
-    const char* dynamicString = profileString(script, maybeFun);
+    const char* dynamicString = cx->runtime()->geckoProfiler().profileString(script, maybeFun);
     if (dynamicString == nullptr) {
         ReportOutOfMemory(cx);
         return false;
     }
 
 #ifdef DEBUG
     // In debug builds, assert the JS pseudo frames already on the stack
     // have a non-null pc. Only look at the top frames to avoid quadratic
@@ -233,25 +227,26 @@ GeckoProfiler::enter(JSContext* cx, JSSc
     }
 #endif
 
     pseudoStack_->pushJsFrame("", dynamicString, script, script->code());
     return true;
 }
 
 void
-GeckoProfiler::exit(JSScript* script, JSFunction* maybeFun)
+GeckoProfilerThread::exit(JSScript* script, JSFunction* maybeFun)
 {
     pseudoStack_->pop();
 
 #ifdef DEBUG
     /* Sanity check to make sure push/pop balanced */
     uint32_t sp = pseudoStack_->stackPointer;
     if (sp < PseudoStack::MaxEntries) {
-        const char* dynamicString = profileString(script, maybeFun);
+        JSRuntime* rt = script->runtimeFromActiveCooperatingThread();
+        const char* dynamicString = rt->geckoProfiler().profileString(script, maybeFun);
         /* Can't fail lookup because we should already be in the set */
         MOZ_ASSERT(dynamicString);
 
         // Bug 822041
         if (!pseudoStack_->entries[sp].isJs()) {
             fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
             fprintf(stderr, " entries=%p size=%u/%u\n",
                             (void*) pseudoStack_->entries,
@@ -276,17 +271,17 @@ GeckoProfiler::exit(JSScript* script, JS
 
 /*
  * Serializes the script/function pair into a "descriptive string" which is
  * allowed to fail. This function cannot trigger a GC because it could finalize
  * some scripts, resize the hash table of profile strings, and invalidate the
  * AddPtr held while invoking allocProfileString.
  */
 UniqueChars
-GeckoProfiler::allocProfileString(JSScript* script, JSFunction* maybeFun)
+GeckoProfilerRuntime::allocProfileString(JSScript* script, JSFunction* maybeFun)
 {
     // Note: this profiler string is regexp-matched by
     // devtools/client/profiler/cleopatra/js/parserWorker.js.
 
     // Get the function name, if any.
     JSAtom* atom = maybeFun ? maybeFun->displayAtom() : nullptr;
 
     // Get the script filename, if any, and its length.
@@ -324,44 +319,44 @@ GeckoProfiler::allocProfileString(JSScri
     }
 
     MOZ_ASSERT(ret == len, "Computed length should match actual length!");
 
     return cstr;
 }
 
 void
-GeckoProfiler::trace(JSTracer* trc)
+GeckoProfilerThread::trace(JSTracer* trc)
 {
     if (pseudoStack_) {
         size_t size = pseudoStack_->stackSize();
         for (size_t i = 0; i < size; i++)
             pseudoStack_->entries[i].trace(trc);
     }
 }
 
 void
-GeckoProfiler::fixupStringsMapAfterMovingGC()
+GeckoProfilerRuntime::fixupStringsMapAfterMovingGC()
 {
     auto locked = strings.lock();
     if (!locked->initialized())
         return;
 
     for (ProfileStringMap::Enum e(locked.get()); !e.empty(); e.popFront()) {
         JSScript* script = e.front().key();
         if (IsForwarded(script)) {
             script = Forwarded(script);
             e.rekeyFront(script);
         }
     }
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void
-GeckoProfiler::checkStringsMapAfterMovingGC()
+GeckoProfilerRuntime::checkStringsMapAfterMovingGC()
 {
     auto locked = strings.lock();
     if (!locked->initialized())
         return;
 
     for (auto r = locked->all(); !r.empty(); r.popFront()) {
         JSScript* script = r.front().key();
         CheckGCThingAfterMovingGC(script);
@@ -376,20 +371,20 @@ ProfileEntry::trace(JSTracer* trc)
 {
     if (isJs()) {
         JSScript* s = rawScript();
         TraceNullableRoot(trc, &s, "ProfileEntry script");
         spOrScript = s;
     }
 }
 
-GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSRuntime* rt,
+GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSContext* cx,
                                                    JSScript* script
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-    : profiler(&rt->geckoProfiler())
+    : profiler(&cx->geckoProfiler())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (!profiler->installed()) {
         profiler = nullptr;
         return;
     }
     spBefore_ = profiler->stackPointer();
 
@@ -408,20 +403,20 @@ GeckoProfilerEntryMarker::~GeckoProfiler
     if (profiler == nullptr)
         return;
 
     profiler->pseudoStack_->pop();    // the JS frame
     profiler->pseudoStack_->pop();    // the BEGIN_PSEUDO_JS frame
     MOZ_ASSERT(spBefore_ == profiler->stackPointer());
 }
 
-AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSRuntime* rt, const char* label,
+AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSContext* cx, const char* label,
                                                ProfileEntry::Category category
                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-    : profiler_(&rt->geckoProfiler())
+    : profiler_(&cx->geckoProfiler())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (!profiler_->installed()) {
         profiler_ = nullptr;
         return;
     }
     spBefore_ = profiler_->stackPointer();
 
@@ -434,22 +429,22 @@ AutoGeckoProfilerEntry::~AutoGeckoProfil
 {
     if (!profiler_)
         return;
 
     profiler_->pseudoStack_->pop();
     MOZ_ASSERT(spBefore_ == profiler_->stackPointer());
 }
 
-GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame
+GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSContext* cx, bool hasProfilerFrame
                                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-    : profiler(&rt->geckoProfiler())
+    : profiler(&cx->geckoProfiler())
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    if (!hasProfilerFrame || !profiler->enabled()) {
+    if (!hasProfilerFrame || !cx->runtime()->geckoProfiler().enabled()) {
         profiler = nullptr;
         return;
     }
 
     uint32_t sp = profiler->pseudoStack_->stackPointer;
     if (sp >= PseudoStack::MaxEntries) {
         profiler = nullptr;
         return;
@@ -525,24 +520,23 @@ ProfileEntry::setPC(jsbytecode* pc)
     JSScript* script = this->script();
     MOZ_ASSERT(script); // This should not be called while profiling is suppressed.
     lineOrPcOffset = pcToOffset(script, pc);
 }
 
 JS_FRIEND_API(void)
 js::SetContextProfilingStack(JSContext* cx, PseudoStack* pseudoStack)
 {
-    cx->runtime()->geckoProfiler().setProfilingStack(pseudoStack);
+    cx->geckoProfiler().setProfilingStack(pseudoStack);
 }
 
 JS_FRIEND_API(void)
 js::EnableContextProfilingStack(JSContext* cx, bool enabled)
 {
-    if (!cx->runtime()->geckoProfiler().enable(enabled))
-        MOZ_CRASH("Execution in this runtime should already be single threaded");
+    cx->runtime()->geckoProfiler().enable(enabled);
 }
 
 JS_FRIEND_API(void)
 js::RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*))
 {
     MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
     cx->runtime()->geckoProfiler().setEventMarker(fn);
 }
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -109,155 +109,155 @@ using ProfileStringMap = HashMap<JSScrip
                                  UniqueChars,
                                  DefaultHasher<JSScript*>,
                                  SystemAllocPolicy>;
 
 class AutoGeckoProfilerEntry;
 class GeckoProfilerEntryMarker;
 class GeckoProfilerBaselineOSRMarker;
 
-class GeckoProfiler
+class GeckoProfilerThread
 {
     friend class AutoGeckoProfilerEntry;
     friend class GeckoProfilerEntryMarker;
     friend class GeckoProfilerBaselineOSRMarker;
 
-    JSRuntime*           rt;
-    ExclusiveData<ProfileStringMap> strings;
     PseudoStack*         pseudoStack_;
-    bool                 slowAssertions;
-    uint32_t             enabled_;
-    void                (*eventMarker_)(const char*);
-
-    UniqueChars allocProfileString(JSScript* script, JSFunction* function);
 
   public:
-    explicit GeckoProfiler(JSRuntime* rt);
-
-    bool init();
+    GeckoProfilerThread();
 
     uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; }
     ProfileEntry* stack() { return pseudoStack_->entries; }
 
     /* management of whether instrumentation is on or off */
-    bool enabled() { MOZ_ASSERT_IF(enabled_, installed()); return enabled_; }
     bool installed() { return pseudoStack_ != nullptr; }
-    MOZ_MUST_USE bool enable(bool enabled);
-    void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
-    bool slowAssertionsEnabled() { return slowAssertions; }
+
+    void setProfilingStack(PseudoStack* pseudoStack);
+    void trace(JSTracer* trc);
 
     /*
      * Functions which are the actual instrumentation to track run information
      *
      *   - enter: a function has started to execute
      *   - updatePC: updates the pc information about where a function
      *               is currently executing
      *   - exit: this function has ceased execution, and no further
      *           entries/exits will be made
      */
     bool enter(JSContext* cx, JSScript* script, JSFunction* maybeFun);
     void exit(JSScript* script, JSFunction* maybeFun);
-    void updatePC(JSScript* script, jsbytecode* pc) {
-        if (!enabled())
-            return;
+    inline void updatePC(JSContext* cx, JSScript* script, jsbytecode* pc);
+};
+
+class GeckoProfilerRuntime
+{
+    JSRuntime*           rt;
+    ExclusiveData<ProfileStringMap> strings;
+    bool                 slowAssertions;
+    uint32_t             enabled_;
+    void                (*eventMarker_)(const char*);
 
-        uint32_t sp = pseudoStack_->stackPointer;
-        if (sp - 1 < PseudoStack::MaxEntries) {
-            MOZ_ASSERT(sp > 0);
-            MOZ_ASSERT(pseudoStack_->entries[sp - 1].rawScript() == script);
-            pseudoStack_->entries[sp - 1].setPC(pc);
-        }
-    }
+    UniqueChars allocProfileString(JSScript* script, JSFunction* function);
+
+  public:
+    explicit GeckoProfilerRuntime(JSRuntime* rt);
+
+    bool init();
 
-    void setProfilingStack(PseudoStack* pseudoStack);
+    /* management of whether instrumentation is on or off */
+    bool enabled() { return enabled_; }
+    void enable(bool enabled);
+    void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
+    bool slowAssertionsEnabled() { return slowAssertions; }
+
     void setEventMarker(void (*fn)(const char*));
     const char* profileString(JSScript* script, JSFunction* maybeFun);
     void onScriptFinalized(JSScript* script);
 
     void markEvent(const char* event);
 
     /* meant to be used for testing, not recommended to call in normal code */
     size_t stringsCount();
     void stringsReset();
 
     uint32_t* addressOfEnabled() {
         return &enabled_;
     }
 
-    void trace(JSTracer* trc);
     void fixupStringsMapAfterMovingGC();
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkStringsMapAfterMovingGC();
 #endif
 };
 
 inline size_t
-GeckoProfiler::stringsCount()
+GeckoProfilerRuntime::stringsCount()
 {
     return strings.lock()->count();
 }
 
 inline void
-GeckoProfiler::stringsReset()
+GeckoProfilerRuntime::stringsReset()
 {
     strings.lock()->clear();
 }
 
 /*
  * This class is used in RunScript() to push the marker onto the sampling stack
  * that we're about to enter JS function calls. This is the only time in which a
  * valid stack pointer is pushed to the sampling stack.
  */
 class MOZ_RAII GeckoProfilerEntryMarker
 {
   public:
-    explicit GeckoProfilerEntryMarker(JSRuntime* rt,
+    explicit GeckoProfilerEntryMarker(JSContext* cx,
                                       JSScript* script
                                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~GeckoProfilerEntryMarker();
 
   private:
-    GeckoProfiler* profiler;
+    GeckoProfilerThread* profiler;
     mozilla::DebugOnly<uint32_t> spBefore_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * RAII class to automatically add Gecko Profiler pseudo frame entries.
  *
  * NB: The `label` string must be statically allocated.
  */
 class MOZ_NONHEAP_CLASS AutoGeckoProfilerEntry
 {
   public:
-    explicit AutoGeckoProfilerEntry(JSRuntime* rt, const char* label,
+    explicit AutoGeckoProfilerEntry(JSContext* cx, const char* label,
                                     ProfileEntry::Category category = ProfileEntry::Category::JS
                                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~AutoGeckoProfilerEntry();
 
   private:
-    GeckoProfiler* profiler_;
+    GeckoProfilerThread* profiler_;
     mozilla::DebugOnly<uint32_t> spBefore_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * This class is used in the interpreter to bound regions where the baseline JIT
  * being entered via OSR.  It marks the current top pseudostack entry as
  * OSR-ed
  */
 class MOZ_RAII GeckoProfilerBaselineOSRMarker
 {
   public:
-    explicit GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame
+    explicit GeckoProfilerBaselineOSRMarker(JSContext* cx, bool hasProfilerFrame
                                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~GeckoProfilerBaselineOSRMarker();
 
   private:
-    GeckoProfiler* profiler;
+    GeckoProfilerThread* profiler;
     mozilla::DebugOnly<uint32_t> spBefore_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * This class manages the instrumentation portion of the profiling for JIT
  * code.
  *
@@ -267,26 +267,26 @@ class MOZ_RAII GeckoProfilerBaselineOSRM
  * manages the instrumentation which needs to be attached to them as well.
  *
  * The basic methods which emit instrumentation are at the end of this class,
  * and the management functions are all described in the middle.
  */
 template<class Assembler, class Register>
 class GeckoProfilerInstrumentation
 {
-    GeckoProfiler* profiler_; // Instrumentation location management
+    GeckoProfilerRuntime* profiler_; // Instrumentation location management
 
   public:
     /*
      * Creates instrumentation which writes information out the the specified
      * profiler's stack and constituent fields.
      */
-    explicit GeckoProfilerInstrumentation(GeckoProfiler* profiler) : profiler_(profiler) {}
+    explicit GeckoProfilerInstrumentation(GeckoProfilerRuntime* profiler) : profiler_(profiler) {}
 
     /* Small proxies around GeckoProfiler */
     bool enabled() { return profiler_ && profiler_->enabled(); }
-    GeckoProfiler* profiler() { MOZ_ASSERT(enabled()); return profiler_; }
+    GeckoProfilerRuntime* profiler() { MOZ_ASSERT(enabled()); return profiler_; }
     void disable() { profiler_ = nullptr; }
 };
 
 } /* namespace js */
 
 #endif /* vm_GeckoProfiler_h */
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -51,16 +51,17 @@
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jsfuninlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/JitFrames-inl.h"
 #include "vm/Debugger-inl.h"
 #include "vm/EnvironmentObject-inl.h"
+#include "vm/GeckoProfiler-inl.h"
 #include "vm/NativeObject-inl.h"
 #include "vm/Probes-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayLength;
@@ -371,17 +372,17 @@ js::RunScript(JSContext* cx, RunState& s
 
     if (!Debugger::checkNoExecute(cx, state.script()))
         return false;
 
 #if defined(MOZ_HAVE_RDTSC)
     js::AutoStopwatch stopwatch(cx);
 #endif // defined(MOZ_HAVE_RDTSC)
 
-    GeckoProfilerEntryMarker marker(cx->runtime(), state.script());
+    GeckoProfilerEntryMarker marker(cx, state.script());
 
     state.script()->ensureNonLazyCanonicalFunction();
 
     if (jit::IsIonEnabled(cx)) {
         jit::MethodStatus status = jit::CanEnter(cx, state);
         if (status == jit::Method_Error)
             return false;
         if (status == jit::Method_Compiled) {
@@ -2010,31 +2011,31 @@ CASE(JSOP_LOOPENTRY)
         jit::MethodStatus status = jit::CanEnterBaselineAtBranch(cx, REGS.fp(), false);
         if (status == jit::Method_Error)
             goto error;
         if (status == jit::Method_Compiled) {
             bool wasProfiler = REGS.fp()->hasPushedGeckoProfilerFrame();
 
             jit::JitExecStatus maybeOsr;
             {
-                GeckoProfilerBaselineOSRMarker osr(cx->runtime(), wasProfiler);
+                GeckoProfilerBaselineOSRMarker osr(cx, wasProfiler);
                 maybeOsr = jit::EnterBaselineAtBranch(cx, REGS.fp(), REGS.pc);
             }
 
             // We failed to call into baseline at all, so treat as an error.
             if (maybeOsr == jit::JitExec_Aborted)
                 goto error;
 
             interpReturnOK = (maybeOsr == jit::JitExec_Ok);
 
             // Pop the profiler frame pushed by the interpreter.  (The compiled
             // version of the function popped a copy of the frame pushed by the
             // OSR trampoline.)
             if (wasProfiler)
-                cx->runtime()->geckoProfiler().exit(script, script->functionNonDelazifying());
+                cx->geckoProfiler().exit(script, script->functionNonDelazifying());
 
             if (activation.entryFrame() != REGS.fp())
                 goto jit_return_pop_frame;
             goto leave_on_safe_point;
         }
     }
 END_CASE(JSOP_LOOPENTRY)
 
@@ -2984,17 +2985,17 @@ CASE(JSOP_STRICTEVAL)
     TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]);
 }
 END_CASE(JSOP_EVAL)
 
 CASE(JSOP_SPREADNEW)
 CASE(JSOP_SPREADCALL)
 CASE(JSOP_SPREADSUPERCALL)
     if (REGS.fp()->hasPushedGeckoProfilerFrame())
-        cx->runtime()->geckoProfiler().updatePC(script, REGS.pc);
+        cx->geckoProfiler().updatePC(cx, script, REGS.pc);
     /* FALL THROUGH */
 
 CASE(JSOP_SPREADEVAL)
 CASE(JSOP_STRICTSPREADEVAL)
 {
     static_assert(JSOP_SPREADEVAL_LENGTH == JSOP_STRICTSPREADEVAL_LENGTH,
                   "spreadeval and strictspreadeval must be the same size");
     bool construct = JSOp(*REGS.pc) == JSOP_SPREADNEW || JSOp(*REGS.pc) == JSOP_SPREADSUPERCALL;;
@@ -3030,17 +3031,17 @@ CASE(JSOP_FUNAPPLY)
 CASE(JSOP_NEW)
 CASE(JSOP_CALL)
 CASE(JSOP_CALL_IGNORES_RV)
 CASE(JSOP_CALLITER)
 CASE(JSOP_SUPERCALL)
 CASE(JSOP_FUNCALL)
 {
     if (REGS.fp()->hasPushedGeckoProfilerFrame())
-        cx->runtime()->geckoProfiler().updatePC(script, REGS.pc);
+        cx->geckoProfiler().updatePC(cx, script, REGS.pc);
 
     MaybeConstruct construct = MaybeConstruct(*REGS.pc == JSOP_NEW || *REGS.pc == JSOP_SUPERCALL);
     bool ignoresReturnValue = *REGS.pc == JSOP_CALL_IGNORES_RV;
     unsigned argStackSlots = GET_ARGC(REGS.pc) + construct;
 
     MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc));
     CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct, ignoresReturnValue);
 
--- a/js/src/vm/Probes-inl.h
+++ b/js/src/vm/Probes-inl.h
@@ -36,17 +36,17 @@ probes::EnterScript(JSContext* cx, JSScr
 {
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
         DTraceEnterJSFun(cx, maybeFun, script);
 #endif
 
     JSRuntime* rt = cx->runtime();
     if (rt->geckoProfiler().enabled()) {
-        if (!rt->geckoProfiler().enter(cx, script, maybeFun))
+        if (!cx->geckoProfiler().enter(cx, script, maybeFun))
             return false;
         MOZ_ASSERT_IF(!fp->script()->isStarGenerator() &&
                       !fp->script()->isLegacyGenerator() &&
                       !fp->script()->isAsync(),
                       !fp->hasPushedGeckoProfilerFrame());
         fp->setPushedGeckoProfilerFrame();
     }
 
@@ -57,17 +57,17 @@ inline void
 probes::ExitScript(JSContext* cx, JSScript* script, JSFunction* maybeFun, bool popProfilerFrame)
 {
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
         DTraceExitJSFun(cx, maybeFun, script);
 #endif
 
     if (popProfilerFrame)
-        cx->runtime()->geckoProfiler().exit(script, maybeFun);
+        cx->geckoProfiler().exit(script, maybeFun);
 }
 
 inline bool
 probes::StartExecution(JSScript* script)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -312,18 +312,16 @@ JSRuntime::destroyRuntime()
         profilerSampleBufferGen_ = UINT32_MAX;
 
         JS::PrepareForFullGC(cx);
         gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME);
     }
 
     AutoNoteSingleThreadedRegion anstr;
 
-    MOZ_ASSERT_IF(!geckoProfiler().enabled(), !singleThreadedExecutionRequired_);
-
     MOZ_ASSERT(!hasHelperThreadZones());
     AutoLockForExclusiveAccess lock(this);
 
     /*
      * Even though all objects in the compartment are dead, we may have keep
      * some filenames around because of gcKeepAtoms.
      */
     FreeScriptData(this, lock);
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -504,19 +504,19 @@ struct JSRuntime : public js::MallocProv
     js::ActiveThreadData<JSDestroyPrincipalsOp> destroyPrincipals;
     js::ActiveThreadData<JSReadPrincipalsOp> readPrincipals;
 
     /* Optional warning reporter. */
     js::ActiveThreadData<JS::WarningReporter> warningReporter;
 
   private:
     /* Gecko profiling metadata */
-    js::UnprotectedData<js::GeckoProfiler> geckoProfiler_;
+    js::UnprotectedData<js::GeckoProfilerRuntime> geckoProfiler_;
   public:
-    js::GeckoProfiler& geckoProfiler() { return geckoProfiler_.ref(); }
+    js::GeckoProfilerRuntime& geckoProfiler() { return geckoProfiler_.ref(); }
 
     // Heap GC roots for PersistentRooted pointers.
     js::ActiveThreadData<mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit,
                                                  mozilla::LinkedList<JS::PersistentRooted<void*>>>> heapRoots;
 
     void tracePersistentRoots(JSTracer* trc);
     void finishPersistentRoots();
 
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1168,17 +1168,17 @@ SavedStacks::saveCurrentStack(JSContext*
         cx->isExceptionPending() ||
         !cx->global() ||
         !cx->global()->isStandardClassResolved(JSProto_Object))
     {
         frame.set(nullptr);
         return true;
     }
 
-    AutoGeckoProfilerEntry psuedoFrame(cx->runtime(), "js::SavedStacks::saveCurrentStack");
+    AutoGeckoProfilerEntry psuedoFrame(cx, "js::SavedStacks::saveCurrentStack");
     FrameIter iter(cx);
     return insertFrames(cx, iter, frame, mozilla::Move(capture));
 }
 
 bool
 SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
                             MutableHandleSavedFrame adoptedStack, uint32_t maxFrameCount)
 {
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -592,30 +592,55 @@ FrameIter::Data::Data(JSContext* cx, Deb
     interpFrames_(nullptr),
     activations_(cx),
     jitFrames_(),
     ionInlineFrameNo_(0),
     wasmFrames_()
 {
 }
 
+FrameIter::Data::Data(JSContext* cx, const CooperatingContext& target,
+                      DebuggerEvalOption debuggerEvalOption)
+  : cx_(cx),
+    debuggerEvalOption_(debuggerEvalOption),
+    principals_(nullptr),
+    state_(DONE),
+    pc_(nullptr),
+    interpFrames_(nullptr),
+    activations_(cx, target),
+    jitFrames_(),
+    ionInlineFrameNo_(0),
+    wasmFrames_()
+{
+}
+
 FrameIter::Data::Data(const FrameIter::Data& other)
   : cx_(other.cx_),
     debuggerEvalOption_(other.debuggerEvalOption_),
     principals_(other.principals_),
     state_(other.state_),
     pc_(other.pc_),
     interpFrames_(other.interpFrames_),
     activations_(other.activations_),
     jitFrames_(other.jitFrames_),
     ionInlineFrameNo_(other.ionInlineFrameNo_),
     wasmFrames_(other.wasmFrames_)
 {
 }
 
+FrameIter::FrameIter(JSContext* cx, const CooperatingContext& target,
+                     DebuggerEvalOption debuggerEvalOption)
+  : data_(cx, target, debuggerEvalOption),
+    ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
+{
+    // settleOnActivation can only GC if principals are given.
+    JS::AutoSuppressGCAnalysis nogc;
+    settleOnActivation();
+}
+
 FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption)
   : data_(cx, debuggerEvalOption, nullptr),
     ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 {
     // settleOnActivation can only GC if principals are given.
     JS::AutoSuppressGCAnalysis nogc;
     settleOnActivation();
 }
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -47,17 +47,17 @@ class JS_PUBLIC_API(AutoEntryMonitor);
 
 namespace js {
 
 class InterpreterRegs;
 class CallObject;
 class FrameIter;
 class EnvironmentObject;
 class ScriptFrameIter;
-class GeckoProfiler;
+class GeckoProfilerRuntime;
 class InterpreterFrame;
 class LexicalEnvironmentObject;
 class EnvironmentIter;
 class EnvironmentCoordinate;
 
 class SavedFrame;
 
 namespace jit {
@@ -1810,21 +1810,23 @@ class FrameIter
         InterpreterFrameIterator interpFrames_;
         ActivationIterator activations_;
 
         jit::JitFrameIterator jitFrames_;
         unsigned ionInlineFrameNo_;
         wasm::FrameIterator wasmFrames_;
 
         Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption, JSPrincipals* principals);
+        Data(JSContext* cx, const CooperatingContext& target, DebuggerEvalOption debuggerEvalOption);
         Data(const Data& other);
     };
 
     explicit FrameIter(JSContext* cx,
                        DebuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK);
+    FrameIter(JSContext* cx, const CooperatingContext&, DebuggerEvalOption);
     FrameIter(JSContext* cx, DebuggerEvalOption, JSPrincipals*);
     FrameIter(const FrameIter& iter);
     MOZ_IMPLICIT FrameIter(const Data& data);
     MOZ_IMPLICIT FrameIter(AbstractFramePtr frame);
 
     bool done() const { return data_.state_ == DONE; }
 
     // -------------------------------------------------------
@@ -1970,16 +1972,24 @@ class ScriptFrameIter : public FrameIter
     explicit ScriptFrameIter(JSContext* cx,
                              DebuggerEvalOption debuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK)
       : FrameIter(cx, debuggerEvalOption)
     {
         settle();
     }
 
     ScriptFrameIter(JSContext* cx,
+                     const CooperatingContext& target,
+                     DebuggerEvalOption debuggerEvalOption)
+       : FrameIter(cx, target, debuggerEvalOption)
+    {
+        settle();
+    }
+
+    ScriptFrameIter(JSContext* cx,
                     DebuggerEvalOption debuggerEvalOption,
                     JSPrincipals* prin)
       : FrameIter(cx, debuggerEvalOption, prin)
     {
         settle();
     }
 
     ScriptFrameIter(const ScriptFrameIter& iter) : FrameIter(iter) { settle(); }
@@ -2091,16 +2101,20 @@ class AllFramesIter : public FrameIter
  * See also AllFramesIter and ScriptFrameIter.
  */
 class AllScriptFramesIter : public ScriptFrameIter
 {
   public:
     explicit AllScriptFramesIter(JSContext* cx)
       : ScriptFrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK)
     {}
+
+    explicit AllScriptFramesIter(JSContext* cx, const CooperatingContext& target)
+      : ScriptFrameIter(cx, target, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK)
+    {}
 };
 
 /* Popular inline definitions. */
 
 inline JSScript*
 FrameIter::script() const
 {
     MOZ_ASSERT(!done());
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -580,17 +580,17 @@ JSRope::flattenInternal(JSContext* maybe
     return flattenInternal<b, Latin1Char>(maybecx);
 }
 
 JSFlatString*
 JSRope::flatten(JSContext* maybecx)
 {
     mozilla::Maybe<AutoGeckoProfilerEntry> entry;
     if (maybecx && !maybecx->helperThread())
-        entry.emplace(maybecx->runtime(), "JSRope::flatten");
+        entry.emplace(maybecx, "JSRope::flatten");
 
     if (zone()->needsIncrementalBarrier())
         return flattenInternal<WithIncrementalBarrier>(maybecx);
     return flattenInternal<NoBarrier>(maybecx);
 }
 
 template <AllowGC allowGC>
 static JSLinearString*
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -2106,17 +2106,17 @@ class ConstraintDataConstantProperty
     JSCompartment* maybeCompartment() { return nullptr; }
 };
 
 } /* anonymous namespace */
 
 bool
 HeapTypeSetKey::constant(CompilerConstraintList* constraints, Value* valOut)
 {
-    if (nonData(constraints) || !object()->isSingleton())
+    if (nonData(constraints))
         return false;
 
     // Only singleton object properties can be marked as constants.
     JSObject* obj = object()->singleton();
     if (!obj || !obj->isNative())
         return false;
 
     if (maybeTypes() && maybeTypes()->nonConstantProperty())
@@ -3994,99 +3994,102 @@ TypeNewScript::rollbackPartiallyInitiali
 
     if (!initializerList)
         return false;
 
     bool found = false;
 
     RootedFunction function(cx, this->function());
     Vector<uint32_t, 32> pcOffsets(cx);
-    for (ScriptFrameIter iter(cx); !iter.done(); ++iter) {
-        {
-            AutoEnterOOMUnsafeRegion oomUnsafe;
-            if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc())))
-                oomUnsafe.crash("rollbackPartiallyInitializedObjects");
-        }
-
-        if (!iter.isConstructing() || !iter.matchCallee(cx, function))
-            continue;
-
-        // Derived class constructors initialize their this-binding later and
-        // we shouldn't run the definite properties analysis on them.
-        MOZ_ASSERT(!iter.script()->isDerivedClassConstructor());
-
-        Value thisv = iter.thisArgument(cx);
-        if (!thisv.isObject() ||
-            thisv.toObject().hasLazyGroup() ||
-            thisv.toObject().group() != group)
-        {
-            continue;
-        }
-
-        if (thisv.toObject().is<UnboxedPlainObject>()) {
-            AutoEnterOOMUnsafeRegion oomUnsafe;
-            if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject()))
-                oomUnsafe.crash("rollbackPartiallyInitializedObjects");
-        }
-
-        // Found a matching frame.
-        RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
-
-        // Whether all identified 'new' properties have been initialized.
-        bool finished = false;
-
-        // If not finished, number of properties that have been added.
-        uint32_t numProperties = 0;
-
-        // Whether the current SETPROP is within an inner frame which has
-        // finished entirely.
-        bool pastProperty = false;
-
-        // Index in pcOffsets of the outermost frame.
-        int callDepth = pcOffsets.length() - 1;
-
-        // Index in pcOffsets of the frame currently being checked for a SETPROP.
-        int setpropDepth = callDepth;
-
-        for (Initializer* init = initializerList;; init++) {
-            if (init->kind == Initializer::SETPROP) {
-                if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
-                    // Have not yet reached this setprop.
+    JSRuntime::AutoProhibitActiveContextChange apacc(cx->runtime());
+    for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) {
+        for (AllScriptFramesIter iter(cx, target); !iter.done(); ++iter) {
+            {
+                AutoEnterOOMUnsafeRegion oomUnsafe;
+                if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc())))
+                    oomUnsafe.crash("rollbackPartiallyInitializedObjects");
+            }
+
+            if (!iter.isConstructing() || !iter.matchCallee(cx, function))
+                continue;
+
+            // Derived class constructors initialize their this-binding later and
+            // we shouldn't run the definite properties analysis on them.
+            MOZ_ASSERT(!iter.script()->isDerivedClassConstructor());
+
+            Value thisv = iter.thisArgument(cx);
+            if (!thisv.isObject() ||
+                thisv.toObject().hasLazyGroup() ||
+                thisv.toObject().group() != group)
+            {
+                continue;
+            }
+
+            if (thisv.toObject().is<UnboxedPlainObject>()) {
+                AutoEnterOOMUnsafeRegion oomUnsafe;
+                if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject()))
+                    oomUnsafe.crash("rollbackPartiallyInitializedObjects");
+            }
+
+            // Found a matching frame.
+            RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>());
+
+            // Whether all identified 'new' properties have been initialized.
+            bool finished = false;
+
+            // If not finished, number of properties that have been added.
+            uint32_t numProperties = 0;
+
+            // Whether the current SETPROP is within an inner frame which has
+            // finished entirely.
+            bool pastProperty = false;
+
+            // Index in pcOffsets of the outermost frame.
+            int callDepth = pcOffsets.length() - 1;
+
+            // Index in pcOffsets of the frame currently being checked for a SETPROP.
+            int setpropDepth = callDepth;
+
+            for (Initializer* init = initializerList;; init++) {
+                if (init->kind == Initializer::SETPROP) {
+                    if (!pastProperty && pcOffsets[setpropDepth] < init->offset) {
+                        // Have not yet reached this setprop.
+                        break;
+                    }
+                    // This setprop has executed, reset state for the next one.
+                    numProperties++;
+                    pastProperty = false;
+                    setpropDepth = callDepth;
+                } else if (init->kind == Initializer::SETPROP_FRAME) {
+                    if (!pastProperty) {
+                        if (pcOffsets[setpropDepth] < init->offset) {
+                            // Have not yet reached this inner call.
+                            break;
+                        } else if (pcOffsets[setpropDepth] > init->offset) {
+                            // Have advanced past this inner call.
+                            pastProperty = true;
+                        } else if (setpropDepth == 0) {
+                            // Have reached this call but not yet in it.
+                            break;
+                        } else {
+                            // Somewhere inside this inner call.
+                            setpropDepth--;
+                        }
+                    }
+                } else {
+                    MOZ_ASSERT(init->kind == Initializer::DONE);
+                    finished = true;
                     break;
                 }
-                // This setprop has executed, reset state for the next one.
-                numProperties++;
-                pastProperty = false;
-                setpropDepth = callDepth;
-            } else if (init->kind == Initializer::SETPROP_FRAME) {
-                if (!pastProperty) {
-                    if (pcOffsets[setpropDepth] < init->offset) {
-                        // Have not yet reached this inner call.
-                        break;
-                    } else if (pcOffsets[setpropDepth] > init->offset) {
-                        // Have advanced past this inner call.
-                        pastProperty = true;
-                    } else if (setpropDepth == 0) {
-                        // Have reached this call but not yet in it.
-                        break;
-                    } else {
-                        // Somewhere inside this inner call.
-                        setpropDepth--;
-                    }
-                }
-            } else {
-                MOZ_ASSERT(init->kind == Initializer::DONE);
-                finished = true;
-                break;
             }
-        }
-
-        if (!finished) {
-            (void) NativeObject::rollbackProperties(cx, obj, numProperties);
-            found = true;
+
+            if (!finished) {
+                (void) NativeObject::rollbackProperties(cx, obj, numProperties);
+                found = true;
+            }
         }
     }
 
     return found;
 }
 
 void
 TypeNewScript::trace(JSTracer* trc)
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -261,35 +261,51 @@ ScriptPreloader::Cleanup()
         // the main thread, or we will deadlock.
         MOZ_RELEASE_ASSERT(!mBlockedOnSyncDispatch);
 
         while (!mSaveComplete && mSaveThread) {
             mal.Wait();
         }
     }
 
-    mScripts.Clear();
+    // Wait for any pending parses to finish before clearing the mScripts
+    // hashtable, since the parse tasks depend on memory allocated by those
+    // scripts.
+    {
+        MonitorAutoLock mal(mMonitor);
+        FinishPendingParses(mal);
+
+        mScripts.Clear();
+    }
 
     AutoSafeJSAPI jsapi;
     JS_RemoveExtraGCRootsTracer(jsapi.cx(), TraceOp, this);
 
     UnregisterWeakMemoryReporter(this);
 }
 
 void
 ScriptPreloader::InvalidateCache()
 {
     mMonitor.AssertNotCurrentThreadOwns();
     MonitorAutoLock mal(mMonitor);
 
     mCacheInvalidated = true;
 
-    mParsingScripts.clearAndFree();
-    while (auto script = mPendingScripts.getFirst())
-        script->remove();
+    // Wait for pending off-thread parses to finish, since they depend on the
+    // memory allocated by our CachedScripts, and can't be canceled
+    // asynchronously.
+    FinishPendingParses(mal);
+
+    // Pending scripts should have been cleared by the above, and new parses
+    // should not have been queued.
+    MOZ_ASSERT(mParsingScripts.empty());
+    MOZ_ASSERT(mParsingSources.empty());
+    MOZ_ASSERT(mPendingScripts.isEmpty());
+
     for (auto& script : IterHash(mScripts))
         script.Remove();
 
     // If we've already finished saving the cache at this point, start a new
     // delayed save operation. This will write out an empty cache file in place
     // of any cache file we've already written out this session, which will
     // prevent us from falling back to the current session's cache file on the
     // next startup.
@@ -543,18 +559,16 @@ ScriptPreloader::PrepareCacheWriteIntern
 
         if (!(script->mProcessTypes == script->mOriginalProcessTypes)) {
             // Note: EnumSet doesn't support operator!=, hence the weird form above.
             found = true;
         }
 
         if (!script->mSize && !script->XDREncode(jsapi.cx())) {
             script.Remove();
-        } else {
-            script->mSize = script->Range().length();
         }
     }
 
     if (!found) {
         mSaveComplete = true;
         return;
     }
 
@@ -617,16 +631,21 @@ ScriptPreloader::WriteCache()
     if (exists) {
         NS_TRY(cacheFile->Remove(false));
     }
 
     {
         AutoFDClose fd;
         NS_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget()));
 
+        // We also need to hold mMonitor while we're touching scripts in
+        // mScripts, or they may be freed before we're done with them.
+        mMonitor.AssertNotCurrentThreadOwns();
+        MonitorAutoLock mal(mMonitor);
+
         nsTArray<CachedScript*> scripts;
         for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
             scripts.AppendElement(script);
         }
 
         // Sort scripts by load time, with async loaded scripts before sync scripts.
         // Since async scripts are always loaded immediately at startup, it helps to
         // have them stored contiguously.
@@ -691,17 +710,17 @@ ScriptPreloader::Run()
 }
 
 void
 ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
                             JS::HandleScript jsscript)
 {
     // Don't bother trying to cache any URLs with cache-busting query
     // parameters.
-    if (mStartupFinished || !mCacheInitialized || cachePath.FindChar('?') >= 0) {
+    if (!Active() || cachePath.FindChar('?') >= 0) {
         return;
     }
 
     // Don't bother caching files that belong to the mochitest harness.
     NS_NAMED_LITERAL_CSTRING(mochikitPrefix, "chrome://mochikit/");
     if (StringHead(url, mochikitPrefix.Length()) == mochikitPrefix) {
         return;
     }
@@ -709,16 +728,31 @@ ScriptPreloader::NoteScript(const nsCStr
     auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, jsscript);
 
     if (!script->mScript) {
         MOZ_ASSERT(jsscript);
         script->mScript = jsscript;
         script->mReadyToExecute = true;
     }
 
+    // If we don't already have bytecode for this script, and it doesn't already
+    // exist in the child cache, encode it now, before it's ever executed.
+    //
+    // Ideally, we would like to do the encoding lazily, during idle slices.
+    // There are subtle issues with encoding scripts which have already been
+    // executed, though, which makes that somewhat risky. So until that
+    // situation is improved, and thoroughly tested, we need to encode eagerly.
+    //
+    // (See also the TranscodeResult_Failure_RunOnceNotSupported failure case in
+    // js::XDRScript)
+    if (!script->mSize && !(mChildCache && mChildCache->mScripts.Get(cachePath))) {
+        AutoSafeJSAPI jsapi;
+        Unused << script->XDREncode(jsapi.cx());
+    }
+
     script->UpdateLoadTime(TimeStamp::Now());
     script->mProcessTypes += CurrentProcessType();
 }
 
 void
 ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
                             ProcessType processType, nsTArray<uint8_t>&& xdrData,
                             TimeStamp loadTime)
@@ -777,40 +811,40 @@ ScriptPreloader::GetCachedScript(JSConte
 JSScript*
 ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script)
 {
     // Check for finished operations before locking so that we can move onto
     // decoding the next batch as soon as possible after the pending batch is
     // ready. If we wait until we hit an unfinished script, we wind up having at
     // most one batch of buffered scripts, and occasionally under-running that
     // buffer.
-    FinishOffThreadDecode();
+    MaybeFinishOffThreadDecode();
 
     if (!script->mReadyToExecute) {
         LOG(Info, "Must wait for async script load: %s\n", script->mURL.get());
         auto start = TimeStamp::Now();
 
         mMonitor.AssertNotCurrentThreadOwns();
         MonitorAutoLock mal(mMonitor);
 
         // Check for finished operations again *after* locking, or we may race
         // against mToken being set between our last check and the time we
         // entered the mutex.
-        FinishOffThreadDecode();
+        MaybeFinishOffThreadDecode();
 
         if (!script->mReadyToExecute && script->mSize < MAX_MAINTHREAD_DECODE_SIZE) {
             LOG(Info, "Script is small enough to recompile on main thread\n");
 
             script->mReadyToExecute = true;
         } else {
             while (!script->mReadyToExecute) {
                 mal.Wait();
 
                 MonitorAutoUnlock mau(mMonitor);
-                FinishOffThreadDecode();
+                MaybeFinishOffThreadDecode();
             }
         }
 
         LOG(Debug, "Waited %fms\n", (TimeStamp::Now() - start).ToMilliseconds());
     }
 
     return script->GetJSScript(cx);
 }
@@ -838,24 +872,40 @@ ScriptPreloader::OffThreadDecodeCallback
         NS_DispatchToMainThread(
           NewRunnableMethod("ScriptPreloader::DoFinishOffThreadDecode",
                             cache,
                             &ScriptPreloader::DoFinishOffThreadDecode));
     }
 }
 
 void
+ScriptPreloader::FinishPendingParses(MonitorAutoLock& aMal)
+{
+    mMonitor.AssertCurrentThreadOwns();
+
+    mPendingScripts.clear();
+
+    MaybeFinishOffThreadDecode();
+
+    // Loop until all pending decode operations finish.
+    while (!mParsingScripts.empty()) {
+        aMal.Wait();
+        MaybeFinishOffThreadDecode();
+    }
+}
+
+void
 ScriptPreloader::DoFinishOffThreadDecode()
 {
     mFinishDecodeRunnablePending = false;
-    FinishOffThreadDecode();
+    MaybeFinishOffThreadDecode();
 }
 
 void
-ScriptPreloader::FinishOffThreadDecode()
+ScriptPreloader::MaybeFinishOffThreadDecode()
 {
     if (!mToken) {
         return;
     }
 
     auto cleanup = MakeScopeExit([&] () {
         mToken = nullptr;
         mParsingSources.clear();
@@ -935,17 +985,18 @@ ScriptPreloader::DecodeNextBatch(size_t 
         return;
     }
 
     AutoJSAPI jsapi;
     MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
     JSContext* cx = jsapi.cx();
 
     JS::CompileOptions options(cx, JSVERSION_LATEST);
-    options.setNoScriptRval(true);
+    options.setNoScriptRval(true)
+           .setSourceIsLazy(true);
 
     if (!JS::CanCompileOffThread(cx, options, size) ||
         !JS::DecodeMultiOffThreadScripts(cx, options, mParsingSources,
                                          OffThreadDecodeCallback,
                                          static_cast<void*>(this))) {
         // If we fail here, we don't move on to process the next batch, so make
         // sure we don't have any other scripts left to process.
         MOZ_ASSERT(mPendingScripts.isEmpty());
@@ -985,18 +1036,20 @@ ScriptPreloader::CachedScript::XDREncode
     JSAutoCompartment ac(cx, mScript);
     JS::RootedScript jsscript(cx, mScript);
 
     mXDRData.construct<JS::TranscodeBuffer>();
 
     JS::TranscodeResult code = JS::EncodeScript(cx, Buffer(), jsscript);
     if (code == JS::TranscodeResult_Ok) {
         mXDRRange.emplace(Buffer().begin(), Buffer().length());
+        mSize = Range().length();
         return true;
     }
+    mXDRData.destroy();
     JS_ClearPendingException(cx);
     return false;
 }
 
 JSScript*
 ScriptPreloader::CachedScript::GetJSScript(JSContext* cx)
 {
     MOZ_ASSERT(mReadyToExecute);
--- a/js/xpconnect/loader/ScriptPreloader.h
+++ b/js/xpconnect/loader/ScriptPreloader.h
@@ -86,16 +86,21 @@ public:
                     ProcessType processType, nsTArray<uint8_t>&& xdrData,
                     TimeStamp loadTime);
 
     // Initializes the script cache from the startup script cache file.
     Result<Ok, nsresult> InitCache(const nsAString& = NS_LITERAL_STRING("scriptCache"));
 
     Result<Ok, nsresult> InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCacheChild* cacheChild);
 
+    bool Active()
+    {
+      return mCacheInitialized && !mStartupFinished;
+    }
+
 private:
     Result<Ok, nsresult> InitCacheInternal();
 
 public:
     void Trace(JSTracer* trc);
 
     static ProcessType CurrentProcessType()
     {
@@ -350,16 +355,17 @@ private:
     // thread for quite as long.
     static constexpr int MAX_MAINTHREAD_DECODE_SIZE = 50 * 1024;
 
     ScriptPreloader();
 
     void ForceWriteCacheFile();
     void Cleanup();
 
+    void FinishPendingParses(MonitorAutoLock& aMal);
     void InvalidateCache();
 
     // Opens the cache file for reading.
     Result<Ok, nsresult> OpenCache();
 
     // Writes a new cache file to disk. Must not be called on the main thread.
     Result<Ok, nsresult> WriteCache();
 
@@ -376,17 +382,17 @@ private:
 
     // Waits for the given cached script to finish compiling off-thread, or
     // decodes it synchronously on the main thread, as appropriate.
     JSScript* WaitForCachedScript(JSContext* cx, CachedScript* script);
 
     void DecodeNextBatch(size_t chunkSize);
 
     static void OffThreadDecodeCallback(void* token, void* context);
-    void FinishOffThreadDecode();
+    void MaybeFinishOffThreadDecode();
     void DoFinishOffThreadDecode();
 
     size_t ShallowHeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
     {
         return (mallocSizeOf(this) + mScripts.ShallowSizeOfExcludingThis(mallocSizeOf) +
                 mallocSizeOf(mSaveThread.get()) + mallocSizeOf(mProfD.get()));
     }
 
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -643,17 +643,17 @@ mozJSComponentLoader::ObjectForLocation(
         // XDR encoded modules in omni.ja). Also, XDR decoding is relatively
         // fast. When we're not using the startup cache, we want to use non-lazy
         // source code so that we can use lazy parsing.
         // See bug 1303754.
         CompileOptions options(cx);
         options.setNoScriptRval(true)
                .setVersion(JSVERSION_LATEST)
                .setFileAndLine(nativePath.get(), 1)
-               .setSourceIsLazy(!!cache);
+               .setSourceIsLazy(cache || ScriptPreloader::GetSingleton().Active());
 
         if (realFile) {
             AutoMemMap map;
             MOZ_TRY(map.init(aComponentFile));
 
             // Note: exceptions will get handled further down;
             // don't early return for them here.
             auto buf = map.get<char>();
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -135,17 +135,17 @@ public:
           mActive = false;
       }
       return NS_OK;
   }
 
   nsresult Dispatch()
   {
       nsCOMPtr<nsIRunnable> self(this);
-      return NS_IdleDispatchToCurrentThread(self.forget(), 1000);
+      return NS_IdleDispatchToCurrentThread(self.forget(), 2500);
   }
 
   void Start(bool aContinuation = false, bool aPurge = false)
   {
       if (mContinuation) {
           mContinuation = aContinuation;
       }
       mPurge = aPurge;
--- a/js/xpconnect/src/XPCThrower.cpp
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -80,17 +80,17 @@ XPCThrower::Throw(nsresult rv, XPCCallCo
     NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, false);
 
     dom::Throw(ccx, rv, nsDependentCString(sz));
 
     if (sz && sz != format)
-        JS_smprintf_free(sz);
+        js_free(sz);
 }
 
 
 // static
 void
 XPCThrower::ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx)
 {
     char* sz;
@@ -122,17 +122,17 @@ XPCThrower::ThrowBadResult(nsresult rv, 
     NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, true);
 
     dom::Throw(ccx, result, nsDependentCString(sz));
 
     if (sz)
-        JS_smprintf_free(sz);
+        js_free(sz);
 }
 
 // static
 void
 XPCThrower::ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx)
 {
     char* sz;
     const char* format;
@@ -144,17 +144,17 @@ XPCThrower::ThrowBadParam(nsresult rv, u
     NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, true);
 
     dom::Throw(ccx, rv, nsDependentCString(sz));
 
     if (sz)
-        JS_smprintf_free(sz);
+        js_free(sz);
 }
 
 
 // static
 void
 XPCThrower::Verbosify(XPCCallContext& ccx,
                       char** psz, bool own)
 {
@@ -168,12 +168,12 @@ XPCThrower::Verbosify(XPCCallContext& cc
         if (!name) {
             name = "";
         }
         sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name).release();
     }
 
     if (sz) {
         if (own)
-            JS_smprintf_free(*psz);
+            js_free(*psz);
         *psz = sz;
     }
 }
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1725,17 +1725,18 @@ public:
     static void Trace(JSTracer* trc, JSObject* obj);
 
     void AutoTrace(JSTracer* trc) {
         TraceSelf(trc);
     }
 
     inline void SweepTearOffs();
 
-    // Returns a string that shuld be free'd using JS_smprintf_free (or null).
+    // Returns a string that should be freed with js_free, or nullptr on
+    // failure.
     char* ToString(XPCWrappedNativeTearOff* to = nullptr) const;
 
     static nsIXPCScriptable* GatherProtoScriptable(nsIClassInfo* classInfo);
 
     bool HasExternalReference() const {return mRefCnt > 1;}
 
     void Suspect(nsCycleCollectionNoteRootCallback& cb);
     void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -2048,17 +2048,17 @@ XrayToString(JSContext* cx, unsigned arg
     XPCCallContext ccx(cx, obj);
     XPCWrappedNative* wn = XPCWrappedNativeXrayTraits::getWN(wrapper);
     char* wrapperStr = wn->ToString();
     if (!wrapperStr) {
         JS_ReportOutOfMemory(cx);
         return false;
     }
     result.AppendASCII(wrapperStr);
-    JS_smprintf_free(wrapperStr);
+    js_free(wrapperStr);
 
     result.AppendASCII(end);
 
     JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length());
     if (!str)
         return false;
 
     args.rval().setString(str);
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -2199,19 +2199,25 @@ nsCSSRendering::GetImageLayerClip(const 
              aForFrame->IsSVGOuterSVGFrame());
 
   // Compute the outermost boundary of the area that might be painted.
   // Same coordinate space as aBorderArea.
   Sides skipSides = aForFrame->GetSkipSides();
   nsRect clipBorderArea =
     ::BoxDecorationRectForBorder(aForFrame, aBorderArea, skipSides, &aBorder);
 
-  bool haveRoundedCorners = GetRadii(aForFrame, aBorder, aBorderArea,
-                                     clipBorderArea, aClipState->mRadii);
-
+  bool haveRoundedCorners = false;
+  LayoutFrameType fType = aForFrame->Type();
+  if (fType != LayoutFrameType::TableColGroup &&
+      fType != LayoutFrameType::TableCol &&
+      fType != LayoutFrameType::TableRow &&
+      fType != LayoutFrameType::TableRowGroup) {
+    haveRoundedCorners = GetRadii(aForFrame, aBorder, aBorderArea,
+                                  clipBorderArea, aClipState->mRadii);
+  }
   bool isSolidBorder =
       aWillPaintBorder && IsOpaqueBorder(aBorder);
   if (isSolidBorder && layerClip == StyleGeometryBox::BorderBox) {
     // If we have rounded corners, we need to inflate the background
     // drawing area a bit to avoid seams between the border and
     // background.
     layerClip = haveRoundedCorners
                      ? StyleGeometryBox::MozAlmostPadding
--- a/layout/reftests/bugs/212563-2-inner.html
+++ b/layout/reftests/bugs/212563-2-inner.html
@@ -1,9 +1,9 @@
 <html>
 <head>
 </head>
 <frameset resizable="yes" rows="100%" onload="parent.changeInnermost(document.getElementById(&quot;innermost&quot;))">
 
-  <frame id="innermost" src="data:text/html,<font color=red>old innermost"></frame>
+  <frame id="innermost" src="212563-2-innermost-b.html"></frame>
 
 </frameset>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/212563-2-innermost-a.html
@@ -0,0 +1,13 @@
+<body onload="w(parent.document, top);">
+<font color=blue>new innermost
+<script type="text/javascript">
+function w(md, t)
+{
+  t.p(0); 
+  md.write(t.replacementForMiddleFrame);
+  t.p(3);
+  t.p(md.documentElement.innerHTML); 
+  md.close(); 
+  t.p(4);
+}
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/212563-2-innermost-b.html
@@ -0,0 +1,1 @@
+<font color=red>old innermost
--- a/layout/reftests/bugs/212563-2.html
+++ b/layout/reftests/bugs/212563-2.html
@@ -5,32 +5,20 @@
 function p(n)
 {
   dump("Test 212563-2 says: " + n + "\n");
 }
 
 // Step 1: replace the innermost frame
 function changeInnermost(iframeElement)
 {
-  iframeElement.setAttribute("src", 
-    "data:text/html,<body onload='" + w + "w(parent.document, top);'><font color=blue>new innermost");
+  iframeElement.setAttribute("src", "212563-2-innermost-a.html");
 }
 
 // Step 2: replace the middle iframe (from the new innermost iframe's onload handler)
 var replacementForMiddleFrame = "<body onload=top.p(5);parent.document.documentElement.removeAttribute('class');>replacement for middle frame<script>top.p(2);<\/script><\/body>";
 
-function w(md, t)
-{
-  t.p(0); 
-  md.write(t.replacementForMiddleFrame);
-  t.p(3);
-  t.p(md.documentElement.innerHTML); 
-  md.close(); 
-  t.p(4);
-}
-
-
 </script>
 </head>
 <body>
 <iframe src="212563-2-inner.html"></iframe>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-bordercollapse/bug1375518-2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Table border collapse</title>
+<style>
+  div > span {
+    display: table-cell;
+    background-color: black;
+    height: 100px;
+    width: 100px;
+    border-radius: 50px;
+  }
+  div {
+    display: table;
+    border-collapse: collapse;
+  }
+</style>
+</head>
+<body>
+  <div><span></span></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-bordercollapse/bug1375518-3.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Separated border model table</title>
+<style>
+  div > span {
+    display: table-cell;
+    background-color: black;
+    height: 100px;
+    width: 100px;
+    border-radius: 50px;
+  }
+  div {
+    display: table;
+    border-collapse: separate;
+  }
+</style>
+</head>
+<body>
+  <div><span></span></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-bordercollapse/bug1375518-4-ref.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>border-radius and separated border model tables</title>
+<style>
+
+body { background: white; color: black }
+
+table { border-collapse: separate; margin: 1em 2px; }
+table, td { border: 1px solid black; }
+
+.radius { border: 3px solid teal; background: aqua; color: black; }
+
+</style>
+
+<h1>border-radius and separated border model tables</h1>
+
+<table>
+  <tbody>
+    <tr><td>xx</td><td>xx</td><td>xx
+  </td></tr></tbody>
+  <tbody class="radius">
+    <tr><td>xx</td><td>xx</td><td>xx
+    </td></tr><tr><td>xx</td><td>xx</td><td>xx
+  </td></tr></tbody>
+  <tbody>
+    <tr><td>xx</td><td>xx</td><td>xx
+  </td></tr></tbody>
+</table>
+
+<table>
+  <tbody><tr class="radius"><td>xx</td><td>xx</td><td>xx
+  </td></tr><tr><td>xx</td><td>xx</td><td>xx
+</td></tr></tbody></table>
+
+<table>
+  <colgroup class="radius"><col><col></colgroup><colgroup><col>
+  </colgroup><tbody><tr><td>xx</td><td>xx</td><td>xx
+  </td></tr><tr><td>xx</td><td>xx</td><td>xx
+</td></tr></tbody></table>
+
+<table>
+  <colgroup><col><col class="radius"><col>
+  </colgroup><tbody><tr><td>xx</td><td>xx</td><td>xx
+  </td></tr><tr><td>xx</td><td>xx</td><td>xx
+</td></tr></tbody></table>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-bordercollapse/bug1375518-4.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>border-radius and separated border model tables</title>
+<style>
+
+body { background: white; color: black }
+
+table { border-collapse: separate; margin: 1em 2px; }
+table, td { border: 1px solid black; }
+
+.radius { border: 3px solid teal; background: aqua; color: black; border-radius: 12px }
+
+</style>
+
+<h1>border-radius and separated border model tables</h1>
+
+<table>
+  <tbody>
+    <tr><td>xx</td><td>xx</td><td>xx
+  </td></tr></tbody>
+  <tbody class="radius">
+    <tr><td>xx</td><td>xx</td><td>xx
+    </td></tr><tr><td>xx</td><td>xx</td><td>xx
+  </td></tr></tbody>
+  <tbody>
+    <tr><td>xx</td><td>xx</td><td>xx
+  </td></tr></tbody>
+</table>
+
+<table>
+  <tbody><tr class="radius"><td>xx</td><td>xx</td><td>xx
+  </td></tr><tr><td>xx</td><td>xx</td><td>xx
+</td></tr></tbody></table>
+
+<table>
+  <colgroup class="radius"><col><col></colgroup><colgroup><col>
+  </colgroup><tbody><tr><td>xx</td><td>xx</td><td>xx
+  </td></tr><tr><td>xx</td><td>xx</td><td>xx
+</td></tr></tbody></table>
+
+<table>
+  <colgroup><col><col class="radius"><col>
+  </colgroup><tbody><tr><td>xx</td><td>xx</td><td>xx
+  </td></tr><tr><td>xx</td><td>xx</td><td>xx
+</td></tr></tbody></table>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-bordercollapse/bug1375518-5-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<title>border-radius and border-collapse tables</title>
+<style>
+
+body { background: white; color: black }
+
+table { border-collapse: collapse; margin: 1em 2px; }
+td { border: 1px solid black; }
+
+.radius { border: 3px solid teal; background: aqua; color: black; }
+
+</style>
+
+<h1>border-radius and border-collapse tables</h1>
+
+<table>
+  <tbody>
+    <tr><td>xx<td>xx<td>xx
+  </tbody>
+  <tbody class="radius">
+    <tr><td>xx<td>xx<td>xx
+    <tr><td>xx<td>xx<td>xx
+  </tbody>
+  <tbody>
+    <tr><td>xx<td>xx<td>xx
+  </tbody>
+</table>
+
+<table>
+  <tr class="radius"><td>xx<td>xx<td>xx
+  <tr><td>xx<td>xx<td>xx
+</table>
+
+<table>
+  <colgroup class="radius"><col><col><colgroup><col>
+  <tr><td>xx<td>xx<td>xx
+  <tr><td>xx<td>xx<td>xx
+</table>
+
+<table>
+  <col><col class="radius"><col>
+  <tr><td>xx<td>xx<td>xx
+  <tr><td>xx<td>xx<td>xx
+</table>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-bordercollapse/bug1375518-5.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<title>border-radius and border-collapse tables</title>
+<style>
+
+body { background: white; color: black }
+
+table { border-collapse: collapse; margin: 1em 2px; }
+td { border: 1px solid black; }
+
+.radius { border: 3px solid teal; background: aqua; color: black; border-radius: 12px }
+
+</style>
+
+<h1>border-radius and border-collapse tables</h1>
+
+<table>
+  <tbody>
+    <tr><td>xx<td>xx<td>xx
+  </tbody>
+  <tbody class="radius">
+    <tr><td>xx<td>xx<td>xx
+    <tr><td>xx<td>xx<td>xx
+  </tbody>
+  <tbody>
+    <tr><td>xx<td>xx<td>xx
+  </tbody>
+</table>
+
+<table>
+  <tr class="radius"><td>xx<td>xx<td>xx
+  <tr><td>xx<td>xx<td>xx
+</table>
+
+<table>
+  <colgroup class="radius"><col><col><colgroup><col>
+  <tr><td>xx<td>xx<td>xx
+  <tr><td>xx<td>xx<td>xx
+</table>
+
+<table>
+  <col><col class="radius"><col>
+  <tr><td>xx<td>xx<td>xx
+  <tr><td>xx<td>xx<td>xx
+</table>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-bordercollapse/bug1375518-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Table border collapse</title>
+<style>
+  div {
+    background-color: black;
+    height: 100px;
+    width: 100px;
+    border-radius: 50px;
+  }
+</style>
+</head>
+<body>
+  <div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/table-bordercollapse/bug1375518.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Table border collapse</title>
+<style>
+  table {
+    border-collapse: collapse;
+    height: 100px;
+    width: 100px;
+  }
+  td {
+    background-color: black;
+    border-radius: 50px;
+  }
+</style>
+</head>
+<body>
+  <table>
+    <tr>
+      <td></td>
+    </tr>
+  </table>
+</body>
+</html>
--- a/layout/reftests/table-bordercollapse/reftest.list
+++ b/layout/reftests/table-bordercollapse/reftest.list
@@ -1,8 +1,13 @@
+== bug1375518.html bug1375518-ref.html
+== bug1375518-2.html bug1375518-ref.html
+== bug1375518-3.html bug1375518-ref.html
+== bug1375518-4.html bug1375518-4-ref.html
+== bug1375518-5.html bug1375518-5-ref.html
 == bc_dyn_cell1.html bc_dyn_cell1_ref.html
 == bc_dyn_cell2.html bc_dyn_cell2_ref.html
 == bc_dyn_cell3.html bc_dyn_cell3_ref.html
 == bc_dyn_cell4.html bc_dyn_cell4_ref.html
 == bc_dyn_cell5.html bc_dyn_cell5_ref.html
 == bc_dyn_row1.html bc_dyn_rg1_ref.html
 == bc_dyn_row2.html bc_dyn_rg2_ref.html
 == bc_dyn_row3.html bc_dyn_rg3_ref.html
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1383975.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style>
+color: url(9

+</style>
+</head>
+</html>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -134,24 +134,20 @@ load 1226400-1.html
 load 1227501-1.html
 load 1230408-1.html
 load 1233135-1.html
 load 1233135-2.html
 load 1238660-1.html
 load 1245260-1.html
 load 1247865-1.html
 asserts-if(stylo,0-1) load 1264396-1.html # bug 1324677
+load 1264949.html
 # The following test relies on -webkit-text-fill-color being behind the
 # layout.css.prefixes.webkit pref
 pref(layout.css.prefixes.webkit,false) load 1265611-1.html
-load border-image-visited-link.html
-load font-face-truncated-src.html
-load large_border_image_width.html
-load long-url-list-stack-overflow.html
-load 1264949.html
 load 1270795.html
 load 1275026.html
 load 1278463-1.html
 pref(dom.animations-api.core.enabled,true) load 1277908-1.html # bug 1323652
 load 1277908-2.html
 load 1282076-1.html
 pref(dom.animations-api.core.enabled,true) load 1282076-2.html
 pref(dom.animations-api.core.enabled,true) load 1290994-1.html
@@ -168,25 +164,31 @@ load 1328535-1.html
 load 1331272.html
 HTTP load 1333001-1.html
 pref(dom.animations-api.core.enabled,true) load 1340344.html
 load 1342316-1.html
 load 1356601-1.html
 load 1370793-1.xhtml
 load 1371450-1.html
 load 1374175-1.html
-load content-only-on-link-before.html
-load content-only-on-visited-before.html
 load 1375812-1.html
 load 1377053-1.html
 load 1377256-1.html
 load 1378064-1.html
 load 1378814.html
 load 1380800.html
 load link-transition-before.html
 load 1381420-1.html
 load 1381682.html
 load 1382672.html
 load 1382710.html
-pref(dom.animations-api.core.enabled,true) load 1383589-1.html
 load 1383001.html
 load 1383001-2.html
 load 1383319.html
+pref(dom.animations-api.core.enabled,true) load 1383589-1.html
+load 1383975.html
+load border-image-visited-link.html
+load content-only-on-link-before.html
+load content-only-on-visited-before.html
+load font-face-truncated-src.html
+load large_border_image_width.html
+load link-transition-before.html
+load long-url-list-stack-overflow.html
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -1102,28 +1102,16 @@ nsBCTableCellFrame::~nsBCTableCellFrame(
 
 /* virtual */ nsMargin
 nsBCTableCellFrame::GetUsedBorder() const
 {
   WritingMode wm = GetWritingMode();
   return GetBorderWidth(wm).GetPhysicalMargin(wm);
 }
 
-/* virtual */ bool
-nsBCTableCellFrame::GetBorderRadii(const nsSize& aFrameSize,
-                                   const nsSize& aBorderArea,
-                                   Sides aSkipSides,
-                                   nscoord aRadii[8]) const
-{
-  NS_FOR_CSS_HALF_CORNERS(corner) {
-    aRadii[corner] = 0;
-  }
-  return false;
-}
-
 #ifdef DEBUG_FRAME_DUMP
 nsresult
 nsBCTableCellFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult);
 }
 #endif
 
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -300,20 +300,16 @@ class nsBCTableCellFrame final : public 
 public:
   NS_DECL_FRAMEARENA_HELPERS(nsBCTableCellFrame)
 
   nsBCTableCellFrame(nsStyleContext* aContext, nsTableFrame* aTableFrame);
 
   ~nsBCTableCellFrame();
 
   virtual nsMargin GetUsedBorder() const override;
-  virtual bool GetBorderRadii(const nsSize& aFrameSize,
-                              const nsSize& aBorderArea,
-                              Sides aSkipSides,
-                              nscoord aRadii[8]) const override;
 
   // Get the *inner half of the border only*, in twips.
   virtual LogicalMargin GetBorderWidth(WritingMode aWM) const override;
 
   // Get the *inner half of the border only*, in pixels.
   BCPixelSize GetBorderWidth(LogicalSide aSide) const;
 
   // Set the full (both halves) width of the border
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1426,17 +1426,17 @@ PaintRowGroupBackgroundByColIdx(nsTableR
       if (aColIdx.Contains(curColIdx)) {
         auto cellPos = cell->GetNormalPosition() + rowPos;
         auto cellRect = nsRect(cellPos, cell->GetSize());
         if (!aDirtyRect.Intersects(cellRect)) {
           continue;
         }
         nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect,
                                                              aLists.BorderBackground(),
-                                                             true, nullptr,
+                                                             false, nullptr,
                                                              aFrame->GetRectRelativeToSelf(),
                                                              cell);
       }
     }
   }
 }
 
 static inline bool FrameHasBorder(nsIFrame* f)
--- a/media/webrtc/trunk/webrtc/modules/utility/source/jvm_android.cc
+++ b/media/webrtc/trunk/webrtc/modules/utility/source/jvm_android.cc
@@ -162,25 +162,25 @@ jmethodID JavaClass::GetMethodId(
 jmethodID JavaClass::GetStaticMethodId(
     const char* name, const char* signature) {
   return GetStaticMethodID(jni_, j_class_, name, signature);
 }
 
 jobject JavaClass::CallStaticObjectMethod(jmethodID methodID, ...) {
   va_list args;
   va_start(args, methodID);
-  jobject res = jni_->CallStaticObjectMethod(j_class_, methodID, args);
+  jobject res = jni_->CallStaticObjectMethodV(j_class_, methodID, args);
   CHECK_EXCEPTION(jni_) << "Error during CallStaticObjectMethod";
   return res;
 }
 
 jint JavaClass::CallStaticIntMethod(jmethodID methodID, ...) {
   va_list args;
   va_start(args, methodID);
-  jint res = jni_->CallStaticIntMethod(j_class_, methodID, args);
+  jint res = jni_->CallStaticIntMethodV(j_class_, methodID, args);
   CHECK_EXCEPTION(jni_) << "Error during CallStaticIntMethod";
   return res;
 }
 
 // JNIEnvironment implementation.
 JNIEnvironment::JNIEnvironment(JNIEnv* jni) : jni_(jni) {
   ALOGD("JNIEnvironment::ctor%s", GetThreadInfo().c_str());
 }
--- a/mfbt/BufferList.h
+++ b/mfbt/BufferList.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_BufferList_h
 #define mozilla_BufferList_h
 
 #include <algorithm>
 #include "mozilla/AllocPolicy.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Types.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Vector.h"
 #include <string.h>
 
 // BufferList represents a sequence of buffers of data. A BufferList can choose
@@ -131,16 +132,25 @@ class BufferList : private AllocPolicy
     MOZ_ASSERT(aInitialCapacity % kSegmentAlignment == 0);
 
     return AllocateSegment(aInitialSize, aInitialCapacity);
   }
 
   // Returns the sum of the sizes of all the buffers.
   size_t Size() const { return mSize; }
 
+  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
+  {
+    size_t size = mSegments.sizeOfExcludingThis(aMallocSizeOf);
+    for (Segment& segment : mSegments) {
+      size += aMallocSizeOf(segment.Start());
+    }
+    return size;
+  }
+
   void Clear()
   {
     if (mOwning) {
       for (Segment& segment : mSegments) {
         this->free_(segment.mData);
       }
     }
     mSegments.clear();
--- a/mobile/android/app/src/main/res/layout/gecko_app.xml
+++ b/mobile/android/app/src/main/res/layout/gecko_app.xml
@@ -21,21 +21,20 @@
          android:background="@android:color/transparent">
 
         <RelativeLayout android:id="@+id/gecko_layout"
                         android:layout_width="match_parent"
                         android:layout_height="match_parent"
                         android:layout_below="@+id/tablet_tab_strip"
                         android:layout_above="@+id/find_in_page">
 
-            <fragment class="org.mozilla.gecko.GeckoViewFragment"
-                      android:id="@+id/layer_view"
-                      android:layout_width="match_parent"
-                      android:layout_height="match_parent"
-                      android:scrollbars="none"/>
+            <org.mozilla.gecko.GeckoView android:id="@+id/layer_view"
+                                         android:layout_width="match_parent"
+                                         android:layout_height="match_parent"
+                                         android:scrollbars="none"/>
 
             <AbsoluteLayout android:id="@+id/plugin_container"
                             android:background="@android:color/transparent"
                             android:layout_width="match_parent"
                             android:layout_height="match_parent"/>
 
             <org.mozilla.gecko.FormAssistPopup android:id="@+id/form_assist_popup"
                                                android:layout_width="match_parent"
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -378,17 +378,16 @@ gvjar.sources += [geckoview_source_dir +
     'GeckoInputConnection.java',
     'GeckoNetworkManager.java',
     'GeckoProfile.java',
     'GeckoProfileDirectories.java',
     'GeckoScreenOrientation.java',
     'GeckoSharedPrefs.java',
     'GeckoThread.java',
     'GeckoView.java',
-    'GeckoViewFragment.java',
     'GeckoViewHandler.java',
     'GeckoViewSettings.java',
     'gfx/BitmapUtils.java',
     'gfx/BufferedImage.java',
     'gfx/BufferedImageGLInfo.java',
     'gfx/DynamicToolbarAnimator.java',
     'gfx/FloatSize.java',
     'gfx/FullScreenState.java',
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
@@ -297,17 +297,16 @@ public class GeckoView extends LayerView
         mPermissionHandler.setListener(delegate, this);
     }
 
     private PromptDelegate mPromptDelegate;
     private InputConnectionListener mInputConnectionListener;
 
     private GeckoViewSettings mSettings;
 
-    protected boolean mOnAttachedToWindowCalled;
     protected String mChromeUri;
     protected int mScreenId = 0; // default to the primary screen
 
     @WrapForJNI(dispatchTo = "proxy")
     protected static final class Window extends JNIObject {
         @WrapForJNI(skip = true)
         public final String chromeUri;
 
@@ -504,20 +503,16 @@ public class GeckoView extends LayerView
     protected void onRestoreInstanceState(final Parcelable state) {
         final StateBinder stateBinder = (StateBinder) state;
 
         if (stateBinder.window != null) {
             mWindow = stateBinder.window;
         }
         mStateSaved = false;
 
-        if (mOnAttachedToWindowCalled) {
-            reattachWindow();
-        }
-
         // We have to always call super.onRestoreInstanceState because View keeps
         // track of these calls and throws an exception when we don't call it.
         super.onRestoreInstanceState(stateBinder.superState);
     }
 
     /**
      * Return the URI of the underlying chrome window opened or to be opened, or null if
      * using the default GeckoView URI.
@@ -582,18 +577,16 @@ public class GeckoView extends LayerView
         if (mWindow == null) {
             // Open a new nsWindow if we didn't have one from before.
             openWindow();
         } else {
             reattachWindow();
         }
 
         super.onAttachedToWindow();
-
-        mOnAttachedToWindowCalled = true;
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         super.destroy();
 
         if (mStateSaved) {
@@ -605,18 +598,16 @@ public class GeckoView extends LayerView
             mWindow.close();
             mWindow.disposeNative();
         } else {
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mWindow, "close");
             GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
                     mWindow, "disposeNative");
         }
-
-        mOnAttachedToWindowCalled = false;
     }
 
     @WrapForJNI public static final int LOAD_DEFAULT = 0;
     @WrapForJNI public static final int LOAD_NEW_TAB = 1;
     @WrapForJNI public static final int LOAD_SWITCH_TAB = 2;
 
     /**
     * Load the given URI.
deleted file mode 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewFragment.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * 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/. */
-
-package org.mozilla.gecko;
-
-import android.support.v4.app.Fragment;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-public class GeckoViewFragment extends android.support.v4.app.Fragment {
-    private static final String LOGTAG = "GeckoViewFragment";
-
-    private static Parcelable mSavedState = null;
-    private static GeckoViewFragment mLastUsed = null;
-    private GeckoView mGeckoView = null;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        setRetainInstance(true);
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        mGeckoView = new GeckoView(getContext());
-        return mGeckoView;
-    }
-
-    @Override
-    public void onResume() {
-        if (mSavedState != null && mLastUsed != this) {
-            // "Restore" the window from the previously used GeckoView to this GeckoView and attach it
-            mGeckoView.onRestoreInstanceState(mSavedState);
-            mSavedState = null;
-        }
-        super.onResume();
-    }
-
-    @Override
-    public void onPause() {
-        mSavedState = mGeckoView.onSaveInstanceState();
-        mLastUsed = this;
-        super.onPause();
-    }
-}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -45,19 +45,19 @@ class GeckoLayerClient implements LayerV
      * 3) whenever reading multiple fields from mViewportMetrics without synchronization (i.e. in
      *    case 1 above) you should always first grab a local copy of the reference, and then use
      *    that because mViewportMetrics might get reassigned in between reading the different
      *    fields. */
     private volatile ImmutableViewportMetrics mViewportMetrics;
 
     private volatile boolean mGeckoIsReady;
 
-    /* package */ final PanZoomController mPanZoomController;
+    private final PanZoomController mPanZoomController;
     private final DynamicToolbarAnimator mToolbarAnimator;
-    /* package */ final LayerView mView;
+    private final LayerView mView;
 
     /* This flag is true from the time that browser.js detects a first-paint is about to start,
      * to the time that we receive the first-paint composite notification from the compositor.
      * Note that there is a small race condition with this; if there are two paints that both
      * have the first-paint flag set, and the second paint happens concurrently with the
      * composite for the first paint, then this flag may be set to true prematurely. Fixing this
      * is possible but risky; see https://bugzilla.mozilla.org/show_bug.cgi?id=797615#c751
      */
@@ -106,18 +106,17 @@ class GeckoLayerClient implements LayerV
         // Gecko being ready is one of the two conditions (along with having an available
         // surface) that cause us to create the compositor. So here, now that we know gecko
         // is ready, call updateCompositor() to see if we can actually do the creation.
         // This needs to run on the UI thread so that the surface validity can't change on
         // us while we're in the middle of creating the compositor.
         mView.post(new Runnable() {
             @Override
             public void run() {
-                mPanZoomController.attach();
-                mView.updateCompositor();
+                getView().updateCompositor();
             }
         });
     }
 
     public void destroy() {
         mPanZoomController.destroy();
         mGeckoIsReady = false;
     }
@@ -403,17 +402,17 @@ class GeckoLayerClient implements LayerV
             /*yPrecision*/ 0,
             /*deviceId*/ 0,
             /*edgeFlags*/ 0,
             /*source*/ source,
             /*flags*/ 0);
         mView.post(new Runnable() {
             @Override
             public void run() {
-                mView.dispatchTouchEvent(event);
+                getView().dispatchTouchEvent(event);
             }
         });
 
         // Forget about removed pointers
         if (eventType == MotionEvent.ACTION_POINTER_UP ||
             eventType == MotionEvent.ACTION_UP ||
             eventType == MotionEvent.ACTION_CANCEL ||
             eventType == MotionEvent.ACTION_HOVER_MOVE)
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
@@ -21,17 +21,16 @@ import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
-import android.os.Parcelable;
 import android.support.v4.util.SimpleArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.TextureView;
@@ -60,17 +59,16 @@ public class LayerView extends FrameLayo
     private Listener mListener;
 
     /* This should only be modified on the Java UI thread. */
     private final Overscroll mOverscroll;
 
     private boolean mServerSurfaceValid;
     private int mWidth, mHeight;
 
-    private boolean onAttachedToWindowCalled;
     private int mDefaultClearColor = Color.WHITE;
     /* package */ GetPixelsResult mGetPixelsResult;
     private final List<DrawListener> mDrawListeners;
 
     /* This is written by the Gecko thread and the UI thread, and read by the UI thread. */
     /* package */ volatile boolean mCompositorCreated;
     /* package */ volatile boolean mCompositorControllerOpen;
 
@@ -416,24 +414,16 @@ public class LayerView extends FrameLayo
         }
         if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
             return true;
         }
         return false;
     }
 
     @Override
-    protected void onRestoreInstanceState(final Parcelable state) {
-        if (onAttachedToWindowCalled) {
-            attachCompositor();
-        }
-        super.onRestoreInstanceState(state);
-    }
-
-    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
 
         // We are adding descendants to this LayerView, but we don't want the
         // descendants to affect the way LayerView retains its focus.
         setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
 
         // This check should not be done before the view is attached to a window
@@ -459,25 +449,16 @@ public class LayerView extends FrameLayo
             mSurfaceView.setBackgroundColor(Color.WHITE);
             addView(mSurfaceView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
 
             SurfaceHolder holder = mSurfaceView.getHolder();
             holder.addCallback(new SurfaceListener());
         }
 
         attachCompositor();
-
-        onAttachedToWindowCalled = true;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        onAttachedToWindowCalled = false;
     }
 
     // Don't expose GeckoLayerClient to things outside this package; only expose it as an Object
     GeckoLayerClient getLayerClient() { return mLayerClient; }
 
     /* package */ boolean isGeckoReady() {
         return mLayerClient.isGeckoReady();
     }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -134,17 +134,16 @@ class NativePanZoomController extends JN
         final float y = coords.y;
 
         return handleMouseEvent(event.getActionMasked(), event.getEventTime(), event.getMetaState(), x, y, event.getButtonState());
     }
 
 
     NativePanZoomController(View view) {
         mView = (LayerView) view;
-        mDestroyed = true;
 
         String[] prefs = { "ui.scrolling.negate_wheel_scroll" };
         mPrefsObserver = new PrefsHelper.PrefHandlerBase() {
             @Override public void prefValue(String pref, boolean value) {
                 if (pref.equals("ui.scrolling.negate_wheel_scroll")) {
                     mNegateWheelScroll = value;
                 }
             }
@@ -201,21 +200,16 @@ class NativePanZoomController extends JN
         }
         if (mDestroyed || !mView.isGeckoReady()) {
             return;
         }
         mDestroyed = true;
         disposeNative();
     }
 
-    @Override
-    public void attach() {
-        mDestroyed = false;
-    }
-
     @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority") @Override // JNIObject
     protected native void disposeNative();
 
     @Override
     public void setOverscrollHandler(final Overscroll handler) {
         mOverscroll = handler;
     }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java
@@ -18,17 +18,16 @@ public interface PanZoomController {
 
     static class Factory {
         static PanZoomController create(View view) {
             return new NativePanZoomController(view);
         }
     }
 
     public void destroy();
-    public void attach();
 
     public boolean onTouchEvent(MotionEvent event);
     public boolean onMotionEvent(MotionEvent event);
 
     public void setOverscrollHandler(final Overscroll controller);
 
     public void setIsLongpressEnabled(boolean isLongpressEnabled);
 }
--- a/mozglue/misc/Printf.h
+++ b/mozglue/misc/Printf.h
@@ -249,21 +249,11 @@ SmprintfPolicyPointer<AllocPolicy> Vsmpr
                                                    const char* fmt, va_list ap)
 {
     SprintfState<AllocPolicy> ss(last.release());
     if (!ss.vprint(fmt, ap))
         return nullptr;
     return ss.release();
 }
 
-/*
-** Free the memory allocated, for the caller, by Smprintf.
-*/
-template<typename AllocPolicy = mozilla::MallocAllocPolicy>
-void SmprintfFree(char* mem)
-{
-    AllocPolicy allocator;
-    allocator.free_(mem);
-}
-
 } // namespace mozilla
 
 #endif /* mozilla_Printf_h */
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -7355,31 +7355,48 @@ nsHttpChannel::OnStopRequest(nsIRequest 
         // authentication request over it or use it for an upgrade
         // to another protocol.
         //
         // this code relies on the code in nsHttpTransaction::Close, which
         // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to
         // keep the connection around after the transaction is finished.
         //
         RefPtr<nsAHttpConnection> conn;
-        LOG(("  authRetry=%d, sticky conn cap=%d", authRetry, mCaps & NS_HTTP_STICKY_CONNECTION));
+        LOG(("  mAuthRetryPending=%d, status=%" PRIx32 ", sticky conn cap=%d",
+             mAuthRetryPending, static_cast<uint32_t>(status),
+             mCaps & NS_HTTP_STICKY_CONNECTION));
         // We must check caps for stickinness also on the transaction because it
         // might have been updated by the transaction itself during inspection of
         // the reposnse headers yet on the socket thread (found connection based
         // auth schema).
-        if (authRetry && (mCaps & NS_HTTP_STICKY_CONNECTION ||
-                          mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) {
+        if ((mAuthRetryPending || NS_FAILED(status)) &&
+            (mCaps & NS_HTTP_STICKY_CONNECTION ||
+             mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) {
+
             conn = mTransaction->GetConnectionReference();
             LOG(("  transaction %p provides connection %p", mTransaction.get(), conn.get()));
-            // This is so far a workaround to fix leak when reusing unpersistent
-            // connection for authentication retry. See bug 459620 comment 4
-            // for details.
-            if (conn && !conn->IsPersistent()) {
-                LOG(("  connection is not persistent, not reusing it"));
-                conn = nullptr;
+
+            if (conn) {
+                if (NS_FAILED(status)) {
+                    // Close (don't reuse) the sticky connection if it's in the middle
+                    // of an NTLM negotiation and this channel has been cancelled.
+                    // There are proxy servers known to get confused when we send
+                    // a new request over such a half-stated connection.
+                    if (!mAuthConnectionRestartable) {
+                        LOG(("  not reusing a half-authenticated sticky connection"));
+                        conn->DontReuse();
+                    }
+                    conn = nullptr;
+                } else if (!conn->IsPersistent()) {
+                    // This is so far a workaround to fix leak when reusing unpersistent
+                    // connection for authentication retry. See bug 459620 comment 4
+                    // for details.
+                    LOG(("  connection is not persistent, not reusing it"));
+                    conn = nullptr;
+                }
             }
         }
 
         RefPtr<nsAHttpConnection> stickyConn;
         if (mCaps & NS_HTTP_STICKY_CONNECTION) {
             stickyConn = mTransaction->GetConnectionReference();
         }
 
--- a/parser/htmlparser/nsExpatDriver.cpp
+++ b/parser/htmlparser/nsExpatDriver.cpp
@@ -831,17 +831,17 @@ CreateErrorText(const char16_t* aDescrip
   char16_t *message = nsTextFormatter::smprintf(msg.get(), aDescription,
                                                  aSourceURL, aLineNumber,
                                                  aColNumber);
   if (!message) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   aErrorString.Assign(message);
-  nsTextFormatter::smprintf_free(message);
+  free(message);
 
   return NS_OK;
 }
 
 static nsresult
 AppendErrorPointer(const int32_t aColNumber,
                    const char16_t *aSourceLine,
                    nsString& aSourceString)
@@ -909,30 +909,30 @@ nsExpatDriver::HandleError()
     nsAutoString tagName;
     if (uriEnd && nameEnd) {
       // We have a prefix.
       tagName.Append(nameEnd + 1, pos - nameEnd - 1);
       tagName.Append(char16_t(':'));
     }
     const char16_t *nameStart = uriEnd ? uriEnd + 1 : mismatch;
     tagName.Append(nameStart, (nameEnd ? nameEnd : pos) - nameStart);
-    
+
     nsAutoString msg;
     nsParserMsgUtils::GetLocalizedStringByName(XMLPARSER_PROPERTIES,
                                                "Expected", msg);
 
     // . Expected: </%S>.
     char16_t *message = nsTextFormatter::smprintf(msg.get(), tagName.get());
     if (!message) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     description.Append(message);
 
-    nsTextFormatter::smprintf_free(message);
+    free(message);
   }
 
   // Adjust the column number so that it is one based rather than zero based.
   uint32_t colNumber = XML_GetCurrentColumnNumber(mExpatParser) + 1;
   uint32_t lineNumber = XML_GetCurrentLineNumber(mExpatParser);
 
   nsAutoString errorText;
   CreateErrorText(description.get(), XML_GetBase(mExpatParser), lineNumber,
--- a/python/mozbuild/mozpack/manifests.py
+++ b/python/mozbuild/mozpack/manifests.py
@@ -405,8 +405,15 @@ class InstallManifestNoSymlinks(InstallM
     """
 
     def add_symlink(self, source, dest):
         """A wrapper that accept symlink entries and install file copies.
 
         source will be copied to dest.
         """
         self.add_copy(source, dest)
+
+    def add_pattern_symlink(self, base, pattern, dest):
+        """A wrapper that accepts symlink patterns and installs file copies.
+
+        Files discovered with ``pattern`` will be copied to ``dest``.
+        """
+        self.add_pattern_copy(base, pattern, dest)
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -62,19 +62,55 @@ function readJSON(url) {
         resolve(JSON.parse(text));
       } catch (e) {
         reject(e);
       }
     });
   });
 }
 
+function stripDescriptions(json, stripThis = true) {
+  if (Array.isArray(json)) {
+    for (let i = 0; i < json.length; i++) {
+      if (typeof json[i] === "object" && json[i] !== null) {
+        json[i] = stripDescriptions(json[i]);
+      }
+    }
+    return json;
+  }
+
+  let result = {};
+
+  // Objects are handled much more efficiently, both in terms of memory and
+  // CPU, if they have the same shape as other objects that serve the same
+  // purpose. So, normalize the order of properties to increase the chances
+  // that the majority of schema objects wind up in large shape groups.
+  for (let key of Object.keys(json).sort()) {
+    if (stripThis && key === "description" && typeof json[key] === "string") {
+      continue;
+    }
+
+    if (typeof json[key] === "object" && json[key] !== null) {
+      result[key] = stripDescriptions(json[key], key !== "properties");
+    } else {
+      result[key] = json[key];
+    }
+  }
+
+  return result;
+}
+
 async function readJSONAndBlobbify(url) {
   let json = await readJSON(url);
 
+  // We don't actually use descriptions at runtime, and they make up about a
+  // third of the size of our structured clone data, so strip them before
+  // blobbifying.
+  json = stripDescriptions(json);
+
   return new StructuredCloneHolder(json);
 }
 
 /**
  * Defines a lazy getter for the given property on the given object. Any
  * security wrappers are waived on the object before the property is
  * defined, and the getter and setter methods are wrapped for the target
  * scope.
@@ -2681,19 +2717,31 @@ this.Schemas = {
     this._needFlush = true;
 
     Object.defineProperty(this, "rootNamespace", {
       enumerable: true,
       configurable: true,
       value: new Namespace("", []),
     });
 
-    for (let blob of this.schemaJSON.values()) {
+    for (let [key, schema] of this.schemaJSON.entries()) {
       try {
-        this.loadSchema(blob.deserialize(global));
+        if (typeof schema.deserialize === "function") {
+          schema = schema.deserialize(global);
+
+          // If we're in the parent process, we need to keep the
+          // StructuredCloneHolder blob around in order to send to future child
+          // processes. If we're in a child, we have no further use for it, so
+          // just store the deserialized schema data in its place.
+          if (!isParentProcess) {
+            this.schemaJSON.set(key, schema);
+          }
+        }
+
+        this.loadSchema(schema);
       } catch (e) {
         Cu.reportError(e);
       }
     }
 
     return this.rootNamespace;
   },
 
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js
@@ -623,40 +623,40 @@ add_task(async function() {
   tallied = null;
 
   Assert.throws(() => root.testing.pattern("DEADcow"),
                 /String "DEADcow" must match \/\^\[0-9a-f\]\+\$\/i/,
                 "should throw for non-match");
 
   root.testing.format({hostname: "foo"});
   verify("call", "testing", "format", [{hostname: "foo",
-                                        url: null,
                                         relativeUrl: null,
-                                        strictRelativeUrl: null}]);
+                                        strictRelativeUrl: null,
+                                        url: null}]);
   tallied = null;
 
   for (let invalid of ["", " ", "http://foo", "foo/bar", "foo.com/", "foo?"]) {
     Assert.throws(() => root.testing.format({hostname: invalid}),
                   /Invalid hostname/,
                   "should throw for invalid hostname");
   }
 
   root.testing.format({url: "http://foo/bar",
                        relativeUrl: "http://foo/bar"});
   verify("call", "testing", "format", [{hostname: null,
-                                        url: "http://foo/bar",
                                         relativeUrl: "http://foo/bar",
-                                        strictRelativeUrl: null}]);
+                                        strictRelativeUrl: null,
+                                        url: "http://foo/bar"}]);
   tallied = null;
 
   root.testing.format({relativeUrl: "foo.html", strictRelativeUrl: "foo.html"});
   verify("call", "testing", "format", [{hostname: null,
-                                        url: null,
                                         relativeUrl: `${wrapper.url}foo.html`,
-                                        strictRelativeUrl: `${wrapper.url}foo.html`}]);
+                                        strictRelativeUrl: `${wrapper.url}foo.html`,
+                                        url: null}]);
   tallied = null;
 
   for (let format of ["url", "relativeUrl"]) {
     Assert.throws(() => root.testing.format({[format]: "chrome://foo/content/"}),
                   /Access denied/,
                   "should throw for access denied");
   }
 
@@ -699,40 +699,40 @@ add_task(async function() {
   ];
   badDates.forEach(str => {
     Assert.throws(() => root.testing.formatDate({date: str}),
                   /Invalid date string/,
                   "should throw for invalid iso date string");
   });
 
   root.testing.deep({foo: {bar: [{baz: {required: 12, optional: "42"}}]}});
-  verify("call", "testing", "deep", [{foo: {bar: [{baz: {required: 12, optional: "42"}}]}}]);
+  verify("call", "testing", "deep", [{foo: {bar: [{baz: {optional: "42", required: 12}}]}}]);
   tallied = null;
 
   Assert.throws(() => root.testing.deep({foo: {bar: [{baz: {optional: "42"}}]}}),
                 /Type error for parameter arg \(Error processing foo\.bar\.0\.baz: Property "required" is required\) for testing\.deep/,
                 "should throw with the correct object path");
 
-  Assert.throws(() => root.testing.deep({foo: {bar: [{baz: {required: 12, optional: 42}}]}}),
+  Assert.throws(() => root.testing.deep({foo: {bar: [{baz: {optional: 42, required: 12}}]}}),
                 /Type error for parameter arg \(Error processing foo\.bar\.0\.baz\.optional: Expected string instead of 42\) for testing\.deep/,
                 "should throw with the correct object path");
 
 
   talliedErrors.length = 0;
 
-  root.testing.errors({warn: "0123", ignore: "0123", default: "0123"});
-  verify("call", "testing", "errors", [{warn: "0123", ignore: "0123", default: "0123"}]);
+  root.testing.errors({default: "0123", ignore: "0123", warn: "0123"});
+  verify("call", "testing", "errors", [{default: "0123", ignore: "0123", warn: "0123"}]);
   checkErrors([]);
 
-  root.testing.errors({warn: "0123", ignore: "x123", default: "0123"});
-  verify("call", "testing", "errors", [{warn: "0123", ignore: null, default: "0123"}]);
+  root.testing.errors({default: "0123", ignore: "x123", warn: "0123"});
+  verify("call", "testing", "errors", [{default: "0123", ignore: null,  warn: "0123"}]);
   checkErrors([]);
 
-  root.testing.errors({warn: "x123", ignore: "0123", default: "0123"});
-  verify("call", "testing", "errors", [{warn: null, ignore: "0123", default: "0123"}]);
+  root.testing.errors({default: "0123", ignore: "0123", warn: "x123"});
+  verify("call", "testing", "errors", [{default: "0123", ignore: "0123", warn: null}]);
   checkErrors([
     'String "x123" must match /^\\d+$/',
   ]);
 
 
   root.testing.onFoo.addListener(f);
   do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["addListener", "testing", "onFoo"]));
   do_check_eq(tallied[3][0], f);
@@ -781,17 +781,17 @@ add_task(async function() {
     let stringProxy = new Proxy(stringTarget, {});
     Assert.throws(() => root.testing.quack(stringProxy),
                   /Expected a plain JavaScript object, got a Proxy/,
                   "should throw when passing a Proxy");
   }
 
 
   root.testing.localize({foo: "__MSG_foo__", bar: "__MSG_foo__", url: "__MSG_http://example.com/__"});
-  verify("call", "testing", "localize", [{foo: "FOO", bar: "__MSG_foo__", url: "http://example.com/"}]);
+  verify("call", "testing", "localize", [{bar: "__MSG_foo__", foo: "FOO", url: "http://example.com/"}]);
   tallied = null;
 
 
   Assert.throws(() => root.testing.localize({url: "__MSG_/foo/bar__"}),
                 /\/FOO\/BAR is not a valid URL\./,
                 "should throw for invalid URL");
 
 
--- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm
+++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm
@@ -41,20 +41,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
                                   "resource://gre/modules/DownloadUIHelper.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
-#ifdef MOZ_PLACES
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
-#endif
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gDownloadPlatform",
                                    "@mozilla.org/toolkit/download-platform;1",
                                    "mozIDownloadPlatform");
@@ -62,20 +60,18 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/process/environment;1",
                                    "nsIEnvironment");
 XPCOMUtils.defineLazyServiceGetter(this, "gMIMEService",
                                    "@mozilla.org/mime;1",
                                    "nsIMIMEService");
 XPCOMUtils.defineLazyServiceGetter(this, "gExternalProtocolService",
                                    "@mozilla.org/uriloader/external-protocol-service;1",
                                    "nsIExternalProtocolService");
-#ifdef MOZ_WIDGET_ANDROID
 XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions",
                                   "resource://gre/modules/RuntimePermissions.jsm");
-#endif
 
 XPCOMUtils.defineLazyGetter(this, "gParentalControlsService", function() {
   if ("@mozilla.org/parental-controls-service;1" in Cc) {
     return Cc["@mozilla.org/parental-controls-service;1"]
       .createInstance(Ci.nsIParentalControlsService);
   }
   return null;
 });
@@ -188,20 +184,22 @@ this.DownloadIntegration = {
    */
   async initializePublicDownloadList(list) {
     try {
       await this.loadPublicDownloadListFromStore(list);
     } catch (ex) {
       Cu.reportError(ex);
     }
 
-    // After the list of persistent downloads has been loaded, we can add the
-    // history observers, even if the load operation failed. This object is kept
-    // alive by the history service.
-    new DownloadHistoryObserver(list);
+    if (AppConstants.MOZ_PLACES) {
+      // After the list of persistent downloads has been loaded, we can add the
+      // history observers, even if the load operation failed. This object is kept
+      // alive by the history service.
+      new DownloadHistoryObserver(list);
+    }
   },
 
   /**
    * Called by initializePublicDownloadList to load the list of persistent
    * downloads, before its first use by the host application.  This function may
    * be called only once during the entire lifetime of the application.
    *
    * @param list
@@ -253,81 +251,50 @@ this.DownloadIntegration = {
    *        the save operation will be repeated later.
    *
    * @return True to save the download, false otherwise.
    */
   shouldPersistDownload(aDownload) {
     // On all platforms, we save all the downloads currently in progress, as
     // well as stopped downloads for which we retained partially downloaded
     // data or we have blocked data.
-    if (!aDownload.stopped || aDownload.hasPartialData ||
-        aDownload.hasBlockedData) {
-      return true;
-    }
-#ifdef MOZ_B2G
-    // On B2G we keep a few days of history.
-    let maxTime = Date.now() -
-      Services.prefs.getIntPref("dom.downloads.max_retention_days") * 24 * 60 * 60 * 1000;
-    return aDownload.startTime > maxTime;
-#elif defined(MOZ_WIDGET_ANDROID)
-    // On Android we store all history.
-    return true;
-#else
-    // On Desktop, stopped downloads for which we don't need to track the
-    // presence of a ".part" file are only retained in the browser history.
-    return false;
-#endif
+    // On Android we store all history; on Desktop, stopped downloads for which
+    // we don't need to track the presence of a ".part" file are only retained
+    // in the browser history.
+    return !aDownload.stopped || aDownload.hasPartialData ||
+           aDownload.hasBlockedData || AppConstants.platform == "android";
   },
 
   /**
    * Returns the system downloads directory asynchronously.
    *
    * @return {Promise}
    * @resolves The downloads directory string path.
    */
   async getSystemDownloadsDirectory() {
     if (this._downloadsDirectory) {
       return this._downloadsDirectory;
     }
 
-    let directoryPath = null;
-#ifdef XP_MACOSX
-    directoryPath = this._getDirectory("DfltDwnld");
-#elifdef XP_WIN
-    // For XP/2K, use My Documents/Downloads. Other version uses
-    // the default Downloads directory.
-    let version = parseFloat(Services.sysinfo.getProperty("version"));
-    if (version < 6) {
-      directoryPath = await this._createDownloadsDirectory("Pers");
+    if (AppConstants.platform == "android") {
+      // Android doesn't have a $HOME directory, and by default we only have
+      // write access to /data/data/org.mozilla.{$APP} and /sdcard
+      this._downloadsDirectory = gEnvironment.get("DOWNLOADS_DIRECTORY");
+      if (!this._downloadsDirectory) {
+        throw new Components.Exception("DOWNLOADS_DIRECTORY is not set.",
+                                       Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH);
+      }
     } else {
-      directoryPath = this._getDirectory("DfltDwnld");
+      try {
+        this._downloadsDirectory = this._getDirectory("DfltDwnld");
+      } catch(e) {
+        this._downloadsDirectory = await this._createDownloadsDirectory("Home");
+      }
     }
-#elifdef XP_UNIX
-#ifdef MOZ_WIDGET_ANDROID
-    // Android doesn't have a $HOME directory, and by default we only have
-    // write access to /data/data/org.mozilla.{$APP} and /sdcard
-    directoryPath = gEnvironment.get("DOWNLOADS_DIRECTORY");
-    if (!directoryPath) {
-      throw new Components.Exception("DOWNLOADS_DIRECTORY is not set.",
-                                     Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH);
-    }
-#else
-    // For Linux, use XDG download dir, with a fallback to Home/Downloads
-    // if the XDG user dirs are disabled.
-    try {
-      directoryPath = this._getDirectory("DfltDwnld");
-    } catch(e) {
-      directoryPath = await this._createDownloadsDirectory("Home");
-    }
-#endif
-#else
-    directoryPath = await this._createDownloadsDirectory("Home");
-#endif
 
-    this._downloadsDirectory = directoryPath;
     return this._downloadsDirectory;
   },
   _downloadsDirectory: null,
 
   /**
    * Returns the user downloads directory asynchronously.
    *
    * @return {Promise}
@@ -364,23 +331,23 @@ this.DownloadIntegration = {
   /**
    * Returns the temporary downloads directory asynchronously.
    *
    * @return {Promise}
    * @resolves The downloads directory string path.
    */
   async getTemporaryDownloadsDirectory() {
     let directoryPath = null;
-#ifdef XP_MACOSX
-    directoryPath = await this.getPreferredDownloadsDirectory();
-#elifdef MOZ_WIDGET_ANDROID
-    directoryPath = await this.getSystemDownloadsDirectory();
-#else
-    directoryPath = this._getDirectory("TmpD");
-#endif
+    if (AppConstants.platform == "macosx") {
+      directoryPath = await this.getPreferredDownloadsDirectory();
+    } else if (AppConstants.platform == "android") {
+      directoryPath = await this.getSystemDownloadsDirectory();
+    } else {
+      directoryPath = this._getDirectory("TmpD");
+    }
     return directoryPath;
   },
 
   /**
    * Checks to determine whether to block downloads for parental controls.
    *
    * aParam aDownload
    *        The download object.
@@ -405,23 +372,20 @@ this.DownloadIntegration = {
   },
 
   /**
    * Checks to determine whether to block downloads for not granted runtime permissions.
    *
    * @return {Promise}
    * @resolves The boolean indicates to block downloads or not.
    */
-  shouldBlockForRuntimePermissions() {
-#ifdef MOZ_WIDGET_ANDROID
-    return RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE)
-                             .then(permissionGranted => !permissionGranted);
-#else
-    return Promise.resolve(false);
-#endif
+  async shouldBlockForRuntimePermissions() {
+    return AppConstants.platform == "android" &&
+           !(await RuntimePermissions.waitForPermissions(
+                                      RuntimePermissions.WRITE_EXTERNAL_STORAGE));
   },
 
   /**
    * Checks to determine whether to block downloads because they might be
    * malware, based on application reputation checks.
    *
    * aParam aDownload
    *        The download object.
@@ -473,17 +437,16 @@ this.DownloadIntegration = {
           resolve({
             shouldBlock: aShouldBlock,
             verdict: (aShouldBlock && kVerdictMap[aVerdict]) || "",
           });
         });
     });
   },
 
-#ifdef XP_WIN
   /**
    * Checks whether downloaded files should be marked as coming from
    * Internet Zone.
    *
    * @return true if files should be marked
    */
   _shouldSaveZoneInformation() {
     let key = Cc["@mozilla.org/windows-registry-key;1"]
@@ -497,39 +460,37 @@ this.DownloadIntegration = {
       } finally {
         key.close();
       }
     } catch (ex) {
       // If the key is not present, files should be marked by default.
       return true;
     }
   },
-#endif
 
   /**
    * Performs platform-specific operations when a download is done.
    *
    * aParam aDownload
    *        The Download object.
    *
    * @return {Promise}
    * @resolves When all the operations completed successfully.
    * @rejects JavaScript exception if any of the operations failed.
    */
   async downloadDone(aDownload) {
-#ifdef XP_WIN
     // On Windows, we mark any file saved to the NTFS file system as coming
     // from the Internet security zone unless Group Policy disables the
     // feature.  We do this by writing to the "Zone.Identifier" Alternate
     // Data Stream directly, because the Save method of the
     // IAttachmentExecute interface would trigger operations that may cause
     // the application to hang, or other performance issues.
     // The stream created in this way is forward-compatible with all the
     // current and future versions of Windows.
-    if (this._shouldSaveZoneInformation()) {
+    if (AppConstants.platform == "win" && this._shouldSaveZoneInformation()) {
       let zone;
       try {
         zone = gDownloadPlatform.mapUrlToZone(aDownload.source.url);
       } catch (e) {
         // Default to Internet Zone if mapUrlToZone failed for
         // whatever reason.
         zone = Ci.mozIDownloadPlatform.ZONE_INTERNET;
       }
@@ -555,17 +516,16 @@ this.DownloadIntegration = {
         // occur when working on a file system that does not support
         // Alternate Data Streams, like FAT32, thus we don't report this
         // specific error.
         if (!(ex instanceof OS.File.Error) || ex.winLastError != 123) {
           Cu.reportError(ex);
         }
       }
     }
-#endif
 
     // The file with the partially downloaded data has restrictive permissions
     // that don't allow other users on the system to access it.  Now that the
     // download is completed, we need to adjust permissions based on whether
     // this is a permanently downloaded file or a temporary download to be
     // opened read-only with an external application.
     try {
       // The following logic to determine whether this is a temporary download
@@ -1041,17 +1001,16 @@ this.DownloadObserver = {
   //// nsISupports
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadHistoryObserver
 
-#ifdef MOZ_PLACES
 /**
  * Registers a Places observer so that operations on download history are
  * reflected on the provided list of downloads.
  *
  * You do not need to keep a reference to this object in order to keep it alive,
  * because the history service already keeps a strong reference to it.
  *
  * @param aList
@@ -1088,22 +1047,16 @@ this.DownloadHistoryObserver.prototype =
 
   onTitleChanged: function () {},
   onBeginUpdateBatch: function () {},
   onEndUpdateBatch: function () {},
   onVisit: function () {},
   onPageChanged: function () {},
   onDeleteVisits: function () {},
 };
-#else
-/**
- * Empty implementation when we have no Places support, for example on B2G.
- */
-this.DownloadHistoryObserver = function (aList) {}
-#endif
 
 ////////////////////////////////////////////////////////////////////////////////
 //// DownloadAutoSaveView
 
 /**
  * This view can be added to a DownloadList object to trigger a save operation
  * in the given DownloadStore object when a relevant change occurs.  You should
  * call the "initialize" method in order to register the view and load the
--- a/toolkit/components/jsdownloads/src/moz.build
+++ b/toolkit/components/jsdownloads/src/moz.build
@@ -10,21 +10,18 @@ SOURCES += [
 
 EXTRA_COMPONENTS += [
     'DownloadLegacy.js',
     'Downloads.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'DownloadCore.jsm',
+    'DownloadIntegration.jsm',
     'DownloadList.jsm',
     'Downloads.jsm',
     'DownloadStore.jsm',
     'DownloadUIHelper.jsm',
 ]
 
-EXTRA_PP_JS_MODULES += [
-    'DownloadIntegration.jsm',
-]
-
 FINAL_LIBRARY = 'xul'
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
--- a/toolkit/components/osfile/moz.build
+++ b/toolkit/components/osfile/moz.build
@@ -20,16 +20,16 @@ XPIDL_MODULE = 'toolkit_osfile'
 XPIDL_SOURCES += [
     'nsINativeOSFileInternals.idl',
 ]
 
 EXPORTS.mozilla += [
     'NativeOSFileInternals.h',
 ]
 
-EXTRA_PP_JS_MODULES += [
+EXTRA_JS_MODULES += [
     'osfile.jsm',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 with Files('**'):
     BUG_COMPONENT = ('Toolkit', 'OS.File')
--- a/toolkit/components/osfile/osfile.jsm
+++ b/toolkit/components/osfile/osfile.jsm
@@ -5,28 +5,31 @@
 /**
  * Common front for various implementations of OS.File
  */
 
 if (typeof Components != "undefined") {
   this.EXPORTED_SYMBOLS = ["OS"];
   Components.utils.import("resource://gre/modules/osfile/osfile_async_front.jsm", this);
 } else {
+  importScripts("resource://gre/modules/workers/require.js");
+
+  var SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm");
+
   // At this stage, we need to import all sources at once to avoid
   // a unique failure on tbpl + talos that seems caused by a
   // what looks like a nested event loop bug (see bug 794091).
-#ifdef XP_WIN
-  importScripts(
-    "resource://gre/modules/workers/require.js",
-    "resource://gre/modules/osfile/osfile_win_back.jsm",
-    "resource://gre/modules/osfile/osfile_shared_front.jsm",
-    "resource://gre/modules/osfile/osfile_win_front.jsm"
-  );
-#else
-  importScripts(
-    "resource://gre/modules/workers/require.js",
-    "resource://gre/modules/osfile/osfile_unix_back.jsm",
-    "resource://gre/modules/osfile/osfile_shared_front.jsm",
-    "resource://gre/modules/osfile/osfile_unix_front.jsm"
-  );
-#endif
+  if (SharedAll.Constants.Win) {
+    importScripts(
+      "resource://gre/modules/osfile/osfile_win_back.jsm",
+      "resource://gre/modules/osfile/osfile_shared_front.jsm",
+      "resource://gre/modules/osfile/osfile_win_front.jsm"
+    );
+  } else {
+    importScripts(
+      "resource://gre/modules/osfile/osfile_unix_back.jsm",
+      "resource://gre/modules/osfile/osfile_shared_front.jsm",
+      "resource://gre/modules/osfile/osfile_unix_front.jsm"
+    );
+  }
+
   OS.Path = require("resource://gre/modules/osfile/ospath.jsm");
 }
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
+++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp
@@ -4,25 +4,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsBrowserStatusFilter.h"
 #include "mozilla/SystemGroup.h"
 #include "nsIChannel.h"
 #include "nsITimer.h"
 #include "nsIServiceManager.h"
 #include "nsString.h"
+#include "nsThreadUtils.h"
 
 using namespace mozilla;
 
 //-----------------------------------------------------------------------------
 // nsBrowserStatusFilter <public>
 //-----------------------------------------------------------------------------
 
 nsBrowserStatusFilter::nsBrowserStatusFilter()
-    : mCurProgress(0)
+    : mTarget(GetMainThreadEventTarget())
+    , mCurProgress(0)
     , mMaxProgress(0)
     , mStatusIsDirty(true)
     , mCurrentPercentage(0)
     , mTotalRequests(0)
     , mFinishedRequests(0)
     , mUseRealProgressFlag(false)
     , mDelayedStatus(false)
     , mDelayedProgress(false)
@@ -107,16 +109,31 @@ nsBrowserStatusFilter::GetIsLoadingDocum
 NS_IMETHODIMP
 nsBrowserStatusFilter::GetLoadType(uint32_t *aLoadType)
 {
     *aLoadType = 0;
     NS_NOTREACHED("nsBrowserStatusFilter::GetLoadType");
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP
+nsBrowserStatusFilter::GetTarget(nsIEventTarget** aTarget)
+{
+    nsCOMPtr<nsIEventTarget> target = mTarget;
+    target.forget(aTarget);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBrowserStatusFilter::SetTarget(nsIEventTarget* aTarget)
+{
+    mTarget = aTarget;
+    return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // nsBrowserStatusFilter::nsIWebProgressListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsBrowserStatusFilter::OnStateChange(nsIWebProgress *aWebProgress,
                                      nsIRequest *aRequest,
                                      uint32_t aStateFlags,
@@ -357,19 +374,17 @@ nsresult
 nsBrowserStatusFilter::StartDelayTimer()
 {
     NS_ASSERTION(!DelayInEffect(), "delay should not be in effect");
 
     mTimer = do_CreateInstance("@mozilla.org/timer;1");
     if (!mTimer)
         return NS_ERROR_FAILURE;
 
-    // Use the system group. The browser status filter is always used by chrome
-    // code.
-    mTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::Other));
+    mTimer->SetTarget(mTarget);
     return mTimer->InitWithNamedFuncCallback(
         TimeoutHandler, this, 160, nsITimer::TYPE_ONE_SHOT,
         "nsBrowserStatusFilter::TimeoutHandler");
 }
 
 void
 nsBrowserStatusFilter::ProcessTimeout()
 {
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.h
+++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.h
@@ -41,16 +41,17 @@ private:
     void MaybeSendStatus();
     void ResetMembers();
     bool DelayInEffect() { return mDelayedStatus || mDelayedProgress; }
 
     static void TimeoutHandler(nsITimer *aTimer, void *aClosure);
 
 private:
     nsCOMPtr<nsIWebProgressListener> mListener;
+    nsCOMPtr<nsIEventTarget>         mTarget;
     nsCOMPtr<nsITimer>               mTimer;
 
     // delayed values
     nsString                         mStatusMsg;
     int64_t                          mCurProgress;
     int64_t                          mMaxProgress;
 
     nsString                         mCurrentStatusMsg;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1,33 +1,41 @@
 {
   "A11Y_INSTANTIATED_FLAG": {
-    "record_in_processes": ["main", "content"],
+    "record_in_processes": ["main"],
     "expires_in_version": "never",
     "kind": "flag",
-    "description": "has accessibility support been instantiated"
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1382820],
+    "description": "Flag indicating accessibility support has been instantiated.",
+    "alert_emails": ["accessibility@mozilla.com"]
   },
   "A11Y_CONSUMERS": {
-    "record_in_processes": ["main", "content"],
+    "record_in_processes": ["main"],
     "expires_in_version": "default",
     "kind": "enumerated",
     "n_values": 11,
-    "description": "Accessibility client by enum id"
+    "releaseChannelCollection": "opt-out",
+    "bug_numbers": [1382820],
+    "description": "A list of known accessibility clients that inject into Firefox process space (see https://dxr.mozilla.org/mozilla-central/source/accessible/windows/msaa/Compatibility.h).",
+    "alert_emails": ["accessibility@mozilla.com"]
   },
   "A11Y_ISIMPLEDOM_USAGE_FLAG": {
-    "record_in_processes": ["main", "content"],
+    "record_in_processes": ["main"],
     "expires_in_version": "default",
     "kind": "flag",
-    "description": "have the ISimpleDOM* accessibility interfaces been used"
+    "description": "Flag indicating the ISimpleDOM* accessibility interfaces has been used.",
+    "alert_emails": ["accessibility@mozilla.com"]
   },
   "A11Y_IATABLE_USAGE_FLAG": {
-    "record_in_processes": ["main", "content"],
+    "record_in_processes": ["main"],
     "expires_in_version": "default",
     "kind": "flag",
-    "description": "has the IAccessibleTable accessibility interface been used"
+    "description": "Flag indicating the IAccessibleTable accessibility interface has been used.",
+    "alert_emails": ["accessibility@mozilla.com"]
   },
   "ADDON_CONTENT_POLICY_SHIM_BLOCKING_LOADING_MS": {
     "record_in_processes": ["main", "content"],
     "expires_in_version": "58",
     "kind": "exponential",
     "high": 60000,
     "n_buckets": 20,
     "keyed": true,
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -1,14 +1,10 @@
 {
   "alert_emails": [
-    "A11Y_CONSUMERS",
-    "A11Y_IATABLE_USAGE_FLAG",
-    "A11Y_INSTANTIATED_FLAG",
-    "A11Y_ISIMPLEDOM_USAGE_FLAG",
     "ADDON_SHIM_USAGE",
     "AUDIOSTREAM_FIRST_OPEN_MS",
     "AUDIOSTREAM_LATER_OPEN_MS",
     "AUTO_REJECTED_TRANSLATION_OFFERS",
     "BACKGROUNDFILESAVER_THREAD_COUNT",
     "BAD_FALLBACK_FONT",
     "BROWSERPROVIDER_XUL_IMPORT_BOOKMARKS",
     "BROWSER_IS_ASSIST_DEFAULT",
@@ -611,19 +607,17 @@
     "WEBCRYPTO_EXTRACTABLE_SIG",
     "WEBCRYPTO_METHOD",
     "WEBCRYPTO_RESOLVED",
     "WEBSOCKETS_HANDSHAKE_TYPE",
     "XMLHTTPREQUEST_ASYNC_OR_SYNC",
     "XUL_CACHE_DISABLED"
   ],
   "bug_numbers": [
-    "A11Y_CONSUMERS",
     "A11Y_IATABLE_USAGE_FLAG",
-    "A11Y_INSTANTIATED_FLAG",
     "A11Y_ISIMPLEDOM_USAGE_FLAG",
     "ADDON_SHIM_USAGE",
     "APPLICATION_REPUTATION_COUNT",
     "APPLICATION_REPUTATION_LOCAL",
     "APPLICATION_REPUTATION_SERVER",
     "APPLICATION_REPUTATION_SHOULD_BLOCK",
     "AUDIOSTREAM_FIRST_OPEN_MS",
     "AUDIOSTREAM_LATER_OPEN_MS",
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -28,16 +28,17 @@ if (AppConstants.MOZ_CRASHREPORTER) {
                                      "nsICrashReporter");
 }
 
 var WebProgressListener = {
   init() {
     this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
                      .createInstance(Ci.nsIWebProgress);
     this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
+    this._filter.target = tabEventTarget;
 
     let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebProgress);
     webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
   },
 
   uninit() {
     let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -3574,17 +3574,17 @@ OOPDeinit()
 
   delete dumpMapLock;
   dumpMapLock = nullptr;
 
   delete pidToMinidump;
   pidToMinidump = nullptr;
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
-  mozilla::SmprintfFree(childCrashNotifyPipe);
+  free(childCrashNotifyPipe);
   childCrashNotifyPipe = nullptr;
 #endif
 }
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
 // Parent-side API for children
 const char*
 GetChildNotificationPipe()
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/frame-script.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/frame-script.js
@@ -17,11 +17,12 @@ module.exports = {
     content: false,
     docShell: false,
     privateNoteIntentionalCrash: false,
     processMessageManager: false,
     removeMessageListener: false,
     removeWeakMessageListener: false,
     sendAsyncMessage: false,
     sendSyncMessage: false,
-    sendRpcMessage: false
+    sendRpcMessage: false,
+    tabEventTarget: false
   }
 };
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -1,14 +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 "nspr.h"
+#include "mozilla/dom/TabGroup.h"
 #include "mozilla/Logging.h"
 #include "mozilla/IntegerPrintfMacros.h"
 
 #include "nsDocLoader.h"
 #include "nsCURILoader.h"
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
 #include "nsIWebProgressListener2.h"
@@ -975,16 +976,37 @@ nsDocLoader::GetIsLoadingDocument(bool *
 NS_IMETHODIMP
 nsDocLoader::GetLoadType(uint32_t *aLoadType)
 {
   *aLoadType = 0;
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+NS_IMETHODIMP
+nsDocLoader::GetTarget(nsIEventTarget** aTarget)
+{
+  nsCOMPtr<mozIDOMWindowProxy> window;
+  nsresult rv = GetDOMWindow(getter_AddRefs(window));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsPIDOMWindowOuter> piwindow = nsPIDOMWindowOuter::From(window);
+  NS_ENSURE_STATE(piwindow);
+
+  nsCOMPtr<nsIEventTarget> target = piwindow->TabGroup()->EventTargetFor(mozilla::TaskCategory::Other);
+  target.forget(aTarget);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocLoader::SetTarget(nsIEventTarget* aTarget)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 int64_t nsDocLoader::GetMaxTotalProgress()
 {
   int64_t newMaxTotal = 0;
 
   uint32_t count = mChildList.Length();
   for (uint32_t i=0; i < count; i++)
   {
     int64_t individualProgress = 0;
--- a/uriloader/base/nsIWebProgress.idl
+++ b/uriloader/base/nsIWebProgress.idl
@@ -2,16 +2,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface mozIDOMWindowProxy;
+interface nsIEventTarget;
 interface nsIWebProgressListener;
 
 /**
  * The nsIWebProgress interface is used to add or remove nsIWebProgressListener
  * instances to observe the loading of asynchronous requests (usually in the
  * context of a DOM window).
  *
  * nsIWebProgress instances may be arranged in a parent-child configuration,
@@ -146,9 +147,16 @@ interface nsIWebProgress : nsISupports
    */
   readonly attribute boolean isLoadingDocument;
 
   /**
    * Contains a load type as specified by the load* constants in
    * nsIDocShellLoadInfo.idl.
    */
   readonly attribute unsigned long loadType;
+
+  /**
+   * Main thread event target to which progress updates should be
+   * dispatched. This typically will be a SchedulerEventTarget
+   * corresponding to the tab requesting updates.
+   */
+  attribute nsIEventTarget target;
 };
--- a/widget/android/GeckoEditableSupport.cpp
+++ b/widget/android/GeckoEditableSupport.cpp
@@ -659,17 +659,21 @@ GeckoEditableSupport::FlushIMEChanges(Fl
         if (aFlags != FLUSH_FLAG_RECOVER) {
             // First time seeing an exception; try flushing text.
             env->ExceptionClear();
             __android_log_print(ANDROID_LOG_WARN, "GeckoEditableSupport",
                     "Recovering from IME exception");
             FlushIMEText(FLUSH_FLAG_RECOVER);
         } else {
             // Give up because we've already tried.
+#ifdef RELEASE_OR_BETA
+            env->ExceptionClear();
+#else
             MOZ_CATCH_JNI_EXCEPTION(env);
+#endif
         }
         return true;
     };
 
     // Commit the text change and selection change transaction.
     mIMETextChanges.Clear();
 
     for (const TextRecord& record : textTransaction) {
--- a/widget/android/fennec/ThumbnailHelper.h
+++ b/widget/android/fennec/ThumbnailHelper.h
@@ -189,34 +189,47 @@ public:
     static void Init()
     {
         java::ThumbnailHelper::Natives<ThumbnailHelper>::Init();
     }
 
     template<class Functor>
     static void OnNativeCall(Functor&& aCall)
     {
-        class IdleEvent : public nsAppShell::LambdaEvent<Functor>
+        class IdleEvent : public Runnable
         {
-            using Base = nsAppShell::LambdaEvent<Functor>;
+            Functor mLambda;
+            bool mIdlePass;
 
         public:
             IdleEvent(Functor&& aCall)
-                : Base(Forward<Functor>(aCall))
+                : Runnable("ThumbnailHelperIdle")
+                , mLambda(Move(aCall))
+                , mIdlePass(false)
             {}
 
-            void Run() override
+            NS_IMETHOD Run() override
             {
+                // Because we can only post to the idle queue from the main
+                // queue, we must first post to the main queue and then to the
+                // idle queue. However, we use the same runnable object for
+                // both queues, and use mIdlePass to track our progress.
+                if (mIdlePass) {
+                    mLambda();
+                    return NS_OK;
+                }
+
+                mIdlePass = true;
                 MessageLoop::current()->PostIdleTask(
-                    NS_NewRunnableFunction("OnNativeCall", Move(Base::lambda)));
+                        nsCOMPtr<nsIRunnable>(this).forget());
+                return NS_OK;
             }
         };
 
-        // Invoke RequestThumbnail on the main thread when the thread is idle.
-        nsAppShell::PostEvent(MakeUnique<IdleEvent>(Forward<Functor>(aCall)));
+        NS_DispatchToMainThread(new IdleEvent(Move(aCall)));
     }
 
     static void
     RequestThumbnail(jni::ByteBuffer::Param aData, jni::Object::Param aTab,
                      int32_t aTabId, int32_t aWidth, int32_t aHeight)
     {
         nsCOMPtr<mozIDOMWindowProxy> window = GetWindowForTab(aTabId);
         if (!window || !aData) {
--- a/widget/android/jni/Natives.h
+++ b/widget/android/jni/Natives.h
@@ -347,17 +347,17 @@ struct ProxyArg<Ref<C, T>>
 template<typename C> struct ProxyArg<const C&> : ProxyArg<C> {};
 template<> struct ProxyArg<StringParam> : ProxyArg<String::Ref> {};
 template<class C> struct ProxyArg<LocalRef<C>> : ProxyArg<typename C::Ref> {};
 
 // ProxyNativeCall implements the functor object that is passed to OnNativeCall
 template<class Impl, class Owner, bool IsStatic,
          bool HasThisArg /* has instance/class local ref in the call */,
          typename... Args>
-class ProxyNativeCall : public AbstractCall
+class ProxyNativeCall
 {
     // "this arg" refers to the Class::LocalRef (for static methods) or
     // Owner::LocalRef (for instance methods) that we optionally (as indicated
     // by HasThisArg) pass into the destination C++ function.
     typedef typename mozilla::Conditional<IsStatic,
             Class, Owner>::Type ThisArgClass;
     typedef typename mozilla::Conditional<IsStatic,
             jclass, jobject>::Type ThisArgJNIType;
@@ -470,17 +470,17 @@ public:
     bool IsTarget(NativeCallType call) const { return call == mNativeCall; }
     template<typename T> bool IsTarget(T&&) const { return false; }
 
     // Redirect the call to another function / class member with the same
     // signature as the original target. Crash if given a wrong signature.
     void SetTarget(NativeCallType call) { mNativeCall = call; }
     template<typename T> void SetTarget(T&&) const { MOZ_CRASH(); }
 
-    void operator()() override
+    void operator()()
     {
         JNIEnv* const env = GetEnvForThread();
         typename ThisArgClass::LocalRef thisArg(env, mThisArg);
         Call<IsStatic, HasThisArg>(
                 thisArg, typename IndexSequenceFor<Args...>::Type());
 
         // Clear all saved global refs. We do this after the call is invoked,
         // and not inside the destructor because we already have a JNIEnv here,
@@ -509,35 +509,39 @@ struct Dispatcher
              typename ThisArg, typename... ProxyArgs>
     static typename EnableIf<
             Traits::dispatchTarget == DispatchTarget::GECKO_PRIORITY, void>::Type
     Run(ThisArg thisArg, ProxyArgs&&... args)
     {
         // For a static method, do not forward the "this arg" (i.e. the class
         // local ref) if the implementation does not request it. This saves us
         // a pair of calls to add/delete global ref.
-        DispatchToGeckoPriorityQueue(MakeUnique<ProxyNativeCall<
-                Impl, typename Traits::Owner, IsStatic, HasThisArg,
-                Args...>>(HasThisArg || !IsStatic ? thisArg : nullptr,
-                          Forward<ProxyArgs>(args)...));
+        auto proxy = ProxyNativeCall<Impl, typename Traits::Owner, IsStatic,
+                                     HasThisArg, Args...>(
+                (HasThisArg || !IsStatic) ? thisArg : nullptr,
+                Forward<ProxyArgs>(args)...);
+        DispatchToGeckoPriorityQueue(
+                NS_NewRunnableFunction("PriorityNativeCall", Move(proxy)));
     }
 
     template<class Traits, bool IsStatic = Traits::isStatic,
              typename ThisArg, typename... ProxyArgs>
     static typename EnableIf<
             Traits::dispatchTarget == DispatchTarget::GECKO, void>::Type
     Run(ThisArg thisArg, ProxyArgs&&... args)
     {
         // For a static method, do not forward the "this arg" (i.e. the class
         // local ref) if the implementation does not request it. This saves us
         // a pair of calls to add/delete global ref.
-        NS_DispatchToMainThread(NS_NewRunnableFunction("ProxyNativeCall", ProxyNativeCall<
-                Impl, typename Traits::Owner, IsStatic, HasThisArg,
-                Args...>(HasThisArg || !IsStatic ? thisArg : nullptr,
-                          Forward<ProxyArgs>(args)...)));
+        auto proxy = ProxyNativeCall<Impl, typename Traits::Owner, IsStatic,
+                                     HasThisArg, Args...>(
+                (HasThisArg || !IsStatic) ? thisArg : nullptr,
+                Forward<ProxyArgs>(args)...);
+        NS_DispatchToMainThread(
+                NS_NewRunnableFunction("GeckoNativeCall", Move(proxy)));
     }
 
     template<class Traits, bool IsStatic = false, typename... ProxyArgs>
     static typename EnableIf<
             Traits::dispatchTarget == DispatchTarget::CURRENT, void>::Type
     Run(ProxyArgs&&... args)
     {
         MOZ_CRASH("Unreachable code");
--- a/widget/android/jni/Utils.cpp
+++ b/widget/android/jni/Utils.cpp
@@ -283,34 +283,27 @@ jclass GetClassRef(JNIEnv* aEnv, const c
             "Does the class require a newer API version? "
             "Or did ProGuard optimize away something it shouldn't have?",
             aClassName);
     aEnv->ExceptionDescribe();
     MOZ_CRASH("Cannot find JNI class");
     return nullptr;
 }
 
-void DispatchToGeckoPriorityQueue(UniquePtr<AbstractCall>&& aCall)
+void DispatchToGeckoPriorityQueue(already_AddRefed<nsIRunnable> aCall)
 {
-    class AbstractCallEvent : public nsAppShell::Event
+    class RunnableEvent : public nsAppShell::Event
     {
-        UniquePtr<AbstractCall> mCall;
-
+        nsCOMPtr<nsIRunnable> mCall;
     public:
-        AbstractCallEvent(UniquePtr<AbstractCall>&& aCall)
-            : mCall(Move(aCall))
-        {}
-
-        void Run() override
-        {
-            (*mCall)();
-        }
+        RunnableEvent(already_AddRefed<nsIRunnable> aCall) : mCall(aCall) {}
+        void Run() override { NS_ENSURE_SUCCESS_VOID(mCall->Run()); }
     };
 
-    nsAppShell::PostEvent(MakeUnique<AbstractCallEvent>(Move(aCall)));
+    nsAppShell::PostEvent(MakeUnique<RunnableEvent>(Move(aCall)));
 }
 
 bool IsFennec()
 {
     return sIsFennec;
 }
 
 int GetAPIVersion() {
--- a/widget/android/jni/Utils.h
+++ b/widget/android/jni/Utils.h
@@ -1,13 +1,15 @@
 #ifndef mozilla_jni_Utils_h__
 #define mozilla_jni_Utils_h__
 
 #include <jni.h>
 
+#include "nsIRunnable.h"
+
 #include "mozilla/UniquePtr.h"
 
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
 #define MOZ_CHECK_JNI
 #endif
 
 #ifdef MOZ_CHECK_JNI
 #include <pthread.h>
@@ -127,23 +129,17 @@ bool ReportException(JNIEnv* aEnv, jthro
 
 
 uintptr_t GetNativeHandle(JNIEnv* env, jobject instance);
 
 void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle);
 
 jclass GetClassRef(JNIEnv* aEnv, const char* aClassName);
 
-struct AbstractCall
-{
-    virtual ~AbstractCall() {}
-    virtual void operator()() = 0;
-};
-
-void DispatchToGeckoPriorityQueue(UniquePtr<AbstractCall>&& aCall);
+void DispatchToGeckoPriorityQueue(already_AddRefed<nsIRunnable> aCall);
 
 /**
  * Returns whether Gecko is running in a Fennec environment, as determined by
  * the presence of the GeckoApp class.
  */
 bool IsFennec();
 
 int GetAPIVersion();
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -111,21 +111,18 @@ static nsTArray<nsWindow*> gTopLevelWind
 
 static bool sFailedToCreateGLContext = false;
 
 // Multitouch swipe thresholds in inches
 static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4;
 static const double SWIPE_MIN_DISTANCE_INCHES = 0.6;
 
 template<typename Lambda, bool IsStatic, typename InstanceType, class Impl>
-class nsWindow::WindowEvent : public nsAppShell::LambdaEvent<Lambda>
+class nsWindow::WindowEvent : public Runnable
 {
-    typedef nsAppShell::Event Event;
-    typedef nsAppShell::LambdaEvent<Lambda> Base;
-
     bool IsStaleCall()
     {
         if (IsStatic) {
             // Static calls are never stale.
             return false;
         }
 
         JNIEnv* const env = mozilla::jni::GetEnvForThread();
@@ -137,45 +134,39 @@ class nsWindow::WindowEvent : public nsA
         // The call is stale if the nsWindow has been destroyed on the
         // Gecko side, but the Java object is still attached to it through
         // a weak pointer. Stale calls should be discarded. Note that it's
         // an error if natives is nullptr here; we return false but the
         // native call will throw an error.
         return natives && !natives->get();
     }
 
+    Lambda mLambda;
     const InstanceType mInstance;
-    const Event::Type mEventType;
 
 public:
     WindowEvent(Lambda&& aLambda,
-                InstanceType&& aInstance,
-                Event::Type aEventType = Event::Type::kGeneralActivity)
-        : Base(mozilla::Move(aLambda))
-        , mInstance(mozilla::Move(aInstance))
-        , mEventType(aEventType)
+                InstanceType&& aInstance)
+        : Runnable("nsWindowEvent")
+        , mLambda(mozilla::Move(aLambda))
+        , mInstance(Forward<InstanceType>(aInstance))
     {}
 
-    WindowEvent(Lambda&& aLambda,
-                Event::Type aEventType = Event::Type::kGeneralActivity)
-        : Base(mozilla::Move(aLambda))
-        , mInstance(Base::lambda.GetThisArg())
-        , mEventType(aEventType)
+    WindowEvent(Lambda&& aLambda)
+        : Runnable("nsWindowEvent")
+        , mLambda(mozilla::Move(aLambda))
+        , mInstance(mLambda.GetThisArg())
     {}
 
-    void Run() override
+    NS_IMETHOD Run() override
     {
         if (!IsStaleCall()) {
-            return Base::Run();
+            mLambda();
         }
-    }
-
-    Event::Type ActivityType() const override
-    {
-        return mEventType;
+        return NS_OK;
     }
 };
 
 namespace {
     template<class Instance, class Impl> typename EnableIf<
         jni::detail::NativePtrPicker<Impl>::value ==
         jni::detail::REFPTR, void>::Type
     CallAttachNative(Instance aInstance, Impl* aImpl)
@@ -259,18 +250,17 @@ public:
     {
         if (aCall.IsTarget(&Open) && NS_IsMainThread()) {
             // Gecko state probably just switched to PROFILE_READY, and the
             // event loop is not running yet. Skip the event loop here so we
             // can get a head start on opening our window.
             return aCall();
         }
 
-        nsAppShell::PostEvent(mozilla::MakeUnique<WindowEvent<Functor>>(
-                mozilla::Move(aCall)));
+        NS_DispatchToMainThread(new WindowEvent<Functor>(mozilla::Move(aCall)));
     }
 
     GeckoViewSupport(nsWindow* aWindow,
                      const GeckoView::Window::LocalRef& aInstance)
         : window(*aWindow)
         , mGeckoViewWindow(aInstance)
     {
         Base::AttachNative(aInstance, static_cast<SupportsWeakPtr*>(this));
@@ -358,16 +348,17 @@ class nsWindow::NPZCSupport final
         {
             return nsAppShell::Event::Type::kUIActivity;
         }
     };
 
     template<typename Lambda>
     void PostInputEvent(Lambda&& aLambda)
     {
+        // Use priority queue for input events.
         nsAppShell::PostEvent(MakeUnique<InputEvent<Lambda>>(
                 this, mozilla::Move(aLambda)));
     }
 
 public:
     typedef NativePanZoomController::Natives<NPZCSupport> Base;
 
     NPZCSupport(NativePtr<NPZCSupport>* aPtr, nsWindow* aWindow,
@@ -992,16 +983,17 @@ public:
                         LayerView::Compositor::LocalRef(env, mCompositor))) {
                     MOZ_CATCH_JNI_EXCEPTION(env);
 
                     lvs->OnResumedCompositor();
                 }
             }
         };
 
+        // Use priority queue for timing-sensitive event.
         nsAppShell::PostEvent(MakeUnique<LayerViewEvent>(
                 MakeUnique<OnResumedEvent>(aObj)));
     }
 
     void SyncInvalidateAndScheduleComposite()
     {
         RefPtr<UiCompositorControllerChild> child = GetUiCompositorControllerChild();
         if (!child) {
--- a/xpcom/string/nsTextFormatter.cpp
+++ b/xpcom/string/nsTextFormatter.cpp
@@ -1375,17 +1375,8 @@ nsTextFormatter::vsnprintf(char16_t* aOu
   if ((ss.cur != ss.base) && (*(ss.cur - 1) != '\0')) {
     *(--ss.cur) = '\0';
   }
 
   n = ss.cur - ss.base;
   return n ? n - 1 : n;
 }
 
-/*
- * Free memory allocated, for the caller, by smprintf
- */
-void
-nsTextFormatter::smprintf_free(char16_t* aMem)
-{
-  free(aMem);
-}
-
--- a/xpcom/string/nsTextFormatter.h
+++ b/xpcom/string/nsTextFormatter.h
@@ -49,32 +49,24 @@ public:
    * terminated. Returns the length of the written output, NOT including the
    * null terminator, or (uint32_t)-1 if an error occurs.
    */
   static uint32_t snprintf(char16_t* aOut, uint32_t aOutLen,
                            const char16_t* aFmt, ...);
 
   /*
    * sprintf into a moz_xmalloc'd buffer. Return a pointer to
-   * buffer on success, nullptr on failure.
+   * buffer on success, nullptr on failure. Use free() to free the buffer.
    */
   static char16_t* smprintf(const char16_t* aFmt, ...);
 
   static uint32_t ssprintf(nsAString& aOut, const char16_t* aFmt, ...);
 
   /*
    * va_list forms of the above.
    */
   static uint32_t vsnprintf(char16_t* aOut, uint32_t aOutLen, const char16_t* aFmt,
                             va_list aAp);
   static char16_t* vsmprintf(const char16_t* aFmt, va_list aAp);
   static uint32_t vssprintf(nsAString& aOut, const char16_t* aFmt, va_list aAp);
-
-  /*
-   * Free the memory allocated, for the caller, by smprintf.
-   * -- Deprecated --
-   * Callers can substitute calling smprintf_free with free
-   */
-  static void smprintf_free(char16_t* aMem);
-
 };
 
 #endif /* nsTextFormatter_h___ */
--- a/xpcom/threads/nsEnvironment.cpp
+++ b/xpcom/threads/nsEnvironment.cpp
@@ -149,15 +149,15 @@ nsEnvironment::Set(const nsAString& aNam
                                               nativeName.get(),
                                               nativeVal.get());
   if (!newData) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   PR_SetEnv(newData.get());
   if (entry->mData) {
-    mozilla::SmprintfFree(entry->mData);
+    free(entry->mData);
   }
   entry->mData = newData.release();
   return NS_OK;
 }
 
 
--- a/xpcom/threads/nsIThreadManager.idl
+++ b/xpcom/threads/nsIThreadManager.idl
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 [ptr] native PRThread(PRThread);
 
+interface nsIEventTarget;
 interface nsIRunnable;
 interface nsIThread;
 
 [scriptable, function, uuid(039a227d-0cb7-44a5-a8f9-dbb7071979f2)]
 interface nsINestedEventLoopCondition : nsISupports
 {
   /**
    * Returns true if the current nested event loop should stop spinning.
@@ -121,9 +122,14 @@ interface nsIThreadManager : nsISupports
 
   /**
    * Spin the current thread's event loop until there are no more pending
    * events.  This could be done with spinEventLoopUntil, but that would
    * require access to the current thread from JavaScript, which we are
    * moving away from.
    */
   void spinEventLoopUntilEmpty();
+
+  /**
+   * Return the SchedulerEventTarget for the SystemGroup.
+   */
+  readonly attribute nsIEventTarget systemGroupEventTarget;
 };
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -7,16 +7,17 @@
 #include "nsThreadManager.h"
 #include "nsThread.h"
 #include "nsThreadUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/InputEventStatistics.h"
+#include "mozilla/SystemGroup.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Preferences.h"
 #ifdef MOZ_CANARY
 #include <fcntl.h>
 #include <unistd.h>
 #endif
 
 #include "MainThreadIdlePeriod.h"
@@ -373,16 +374,24 @@ nsThreadManager::SpinEventLoopUntilEmpty
 
   while (NS_HasPendingEvents(thread)) {
     (void)NS_ProcessNextEvent(thread, false);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsThreadManager::GetSystemGroupEventTarget(nsIEventTarget** aTarget)
+{
+  nsCOMPtr<nsIEventTarget> target = SystemGroup::EventTargetFor(TaskCategory::Other);
+  target.forget(aTarget);
+  return NS_OK;
+}
+
 uint32_t
 nsThreadManager::GetHighestNumberOfThreads()
 {
   OffTheBooksMutexAutoLock lock(mLock);
   return mHighestNumberOfThreads;
 }
 
 NS_IMETHODIMP