Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 03 Aug 2017 18:06:53 -0700
changeset 422265 32083f24a1bb2c33050b4c972783f066432194eb
parent 422177 5742919ec43f834cc061a96d87c767af1a1f7f75 (current diff)
parent 422264 33ca8807bddee0b3a17e285c8ed6d2331e6540b1 (diff)
child 422318 e5e994cb9d84ea35d4034c0fd21bb7e870d5f586
child 422414 8e99d37a460d9d10d93e2b79783fef1e364a7398
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone57.0a1
first release with
nightly linux32
32083f24a1bb / 57.0a1 / 20170804100354 / files
nightly linux64
32083f24a1bb / 57.0a1 / 20170804100354 / files
nightly mac
32083f24a1bb / 57.0a1 / 20170804100354 / files
nightly win32
32083f24a1bb / 57.0a1 / 20170804100354 / files
nightly win64
32083f24a1bb / 57.0a1 / 20170804100354 / 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 inbound to central, a=merge MozReview-Commit-ID: DlSLhaepjeU
browser/config/tooltool-manifests/linux64/asan.manifest
browser/config/tooltool-manifests/linux64/clang.manifest
browser/config/tooltool-manifests/linux64/clang.manifest.centos6
browser/config/tooltool-manifests/linux64/fuzzing.manifest
browser/config/tooltool-manifests/linux64/msan.manifest
browser/extensions/onboarding/content/onboarding.css
browser/themes/shared/incontentprefs-old/favicon.ico
browser/themes/shared/incontentprefs/favicon.ico
browser/themes/shared/jar.inc.mn
docshell/base/nsDocShell.cpp
editor/libeditor/SetTextTransaction.cpp
editor/libeditor/SetTextTransaction.h
gfx/2d/DrawingJob.cpp
gfx/2d/DrawingJob.h
gfx/2d/JobScheduler.cpp
gfx/2d/JobScheduler.h
gfx/2d/JobScheduler_posix.cpp
gfx/2d/JobScheduler_posix.h
gfx/2d/JobScheduler_win32.cpp
gfx/2d/JobScheduler_win32.h
gfx/tests/gtest/TestJobScheduler.cpp
js/src/frontend/BytecodeEmitter.cpp
layout/base/nsLayoutUtils.cpp
mobile/android/base/java/org/mozilla/gecko/tabs/TabsPanel.java
netwerk/protocol/http/nsHttpChannel.cpp
testing/web-platform/meta/WebIDL/ecmascript-binding/es-exceptions/DOMException-constructor.html.ini
testing/web-platform/meta/WebIDL/ecmascript-binding/es-exceptions/constructor-object.html.ini
testing/web-platform/meta/WebIDL/ecmascript-binding/es-exceptions/constructor-object.worker.js.ini
testing/web-platform/meta/clear-site-data/navigation.html.ini
testing/web-platform/meta/css/CSS2/positioning/absolute-replaced-width-013.xht.ini
testing/web-platform/meta/css/CSS2/positioning/absolute-replaced-width-027.xht.ini
testing/web-platform/meta/css/css-backgrounds-3/background-clip-content-box-001.html.ini
testing/web-platform/meta/cssom/index-003.html.ini
testing/web-platform/meta/fonts/matching/fixed-stretch-style-over-weight.html.ini
testing/web-platform/meta/fonts/matching/stretch-distance-over-weight-distance.html.ini
testing/web-platform/meta/fonts/matching/style-ranges-over-weight-direction.html.ini
testing/web-platform/meta/payment-request/payment-request-in-iframe.html.ini
testing/web-platform/meta/storage/interfaces.worker.js.ini
testing/web-platform/meta/streams/readable-streams/templated.html.ini
testing/web-platform/meta/wasm/wasm_service_worker_test.html.ini
testing/web-platform/tests/WebIDL/ecmascript-binding/es-exceptions/DOMException-constants.html
testing/web-platform/tests/WebIDL/ecmascript-binding/es-exceptions/DOMException-constructor.html
testing/web-platform/tests/WebIDL/ecmascript-binding/es-exceptions/constructor-object.html
testing/web-platform/tests/WebIDL/ecmascript-binding/es-exceptions/constructor-object.js
testing/web-platform/tests/WebIDL/ecmascript-binding/es-exceptions/constructor-object.worker.js
testing/web-platform/tests/accelerometer/support-iframe.html
testing/web-platform/tests/ambient-light/support-iframe.html
testing/web-platform/tests/check_stability.py
testing/web-platform/tests/ci_built_diff.sh
testing/web-platform/tests/ci_lint.sh
testing/web-platform/tests/ci_stability.sh
testing/web-platform/tests/ci_unittest.sh
testing/web-platform/tests/clear-site-data/navigation.html
testing/web-platform/tests/clear-site-data/support/test_utils.js
testing/web-platform/tests/cssom/index-003.html
testing/web-platform/tests/fonts/matching/README.md
testing/web-platform/tests/fonts/matching/fixed-stretch-style-over-weight-ref.html
testing/web-platform/tests/fonts/matching/fixed-stretch-style-over-weight.html
testing/web-platform/tests/fonts/matching/font-matching.css
testing/web-platform/tests/fonts/matching/resources/variabletest_matching.ttf
testing/web-platform/tests/fonts/matching/stretch-distance-over-weight-distance-ref.html
testing/web-platform/tests/fonts/matching/stretch-distance-over-weight-distance.html
testing/web-platform/tests/fonts/matching/style-ranges-over-weight-direction-ref.html
testing/web-platform/tests/fonts/matching/style-ranges-over-weight-direction.html
testing/web-platform/tests/gyroscope/support-iframe.html
testing/web-platform/tests/html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/moving-documents.html
testing/web-platform/tests/html/semantics/scripting-1/the-script-element/load-event.html
testing/web-platform/tests/magnetometer/support-iframe.html
testing/web-platform/tests/payment-request/payment-request-canmakepayment-method.https.http
testing/web-platform/tests/payment-request/payment-request-in-iframe.html
testing/web-platform/tests/storage/interfaces.worker.js
testing/web-platform/tests/tools/browserutils/__init__.py
testing/web-platform/tests/tools/browserutils/browser.py
testing/web-platform/tests/tools/browserutils/install.py
testing/web-platform/tests/tools/browserutils/requirements.txt
testing/web-platform/tests/tools/browserutils/utils.py
testing/web-platform/tests/tools/browserutils/virtualenv.py
testing/web-platform/tests/tools/wptrun.py
testing/web-platform/tests/tools/wptrunner/wptrunner/executors/reftest-wait.js
testing/web-platform/tests/tools/wptrunner/wptrunner/executors/reftest-wait_servodriver.js
testing/web-platform/tests/uievents/keyboard/key-manual.css
testing/web-platform/tests/uievents/keyboard/key-manual.js
testing/web-platform/tests/wasm/wasm_service_worker_test.html
testing/web-platform/tests/web-share/share-empty-manual.html
--- a/browser/components/preferences/in-content-new/preferences.xul
+++ b/browser/components/preferences/in-content-new/preferences.xul
@@ -90,17 +90,17 @@
       disablefastfind="true"
 #ifdef USE_WIN_TITLE_STYLE
       title="&prefWindow.titleWin;">
 #else
       title="&prefWindow.title;">
 #endif
 
   <html:link rel="shortcut icon"
-              href="chrome://browser/skin/preferences/in-content-new/favicon.ico"/>
+              href="chrome://browser/skin/settings.svg"/>
 
   <script type="application/javascript"
           src="chrome://browser/content/utilityOverlay.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/preferences/in-content-new/preferences.js"/>
   <script src="chrome://browser/content/preferences/in-content-new/findInPage.js"/>
   <script src="chrome://browser/content/preferences/in-content-new/subdialogs.js"/>
 
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -64,17 +64,17 @@
       disablefastfind="true"
 #ifdef USE_WIN_TITLE_STYLE
       title="&prefWindow.titleWin;">
 #else
       title="&prefWindow.title;">
 #endif
 
   <html:link rel="shortcut icon"
-              href="chrome://browser/skin/preferences/in-content/favicon.ico"/>
+              href="chrome://browser/skin/settings.svg"/>
 
   <script type="application/javascript"
           src="chrome://browser/content/utilityOverlay.js"/>
   <script type="application/javascript"
           src="chrome://browser/content/preferences/in-content/preferences.js"/>
   <script src="chrome://browser/content/preferences/in-content/subdialogs.js"/>
 
   <stringbundle id="bundleBrand"
--- a/browser/config/mozconfigs/linux64/debug-static-analysis-clang
+++ b/browser/config/mozconfigs/linux64/debug-static-analysis-clang
@@ -2,19 +2,17 @@ MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 ac_add_options --enable-debug
 ac_add_options --enable-dmd
 
-# Disable stylo until bug 1356926 is fixed and we have >= llvm39 on centos.
-ac_add_options --disable-stylo
-unset LLVM_CONFIG
+. $topsrcdir/build/mozconfig.stylo
 
 # Use Clang as specified in manifest
 export CC="$topsrcdir/clang/bin/clang"
 export CXX="$topsrcdir/clang/bin/clang++"
 
 # Add the static checker
 ac_add_options --enable-clang-plugin
 
--- a/browser/config/mozconfigs/linux64/opt-static-analysis-clang
+++ b/browser/config/mozconfigs/linux64/opt-static-analysis-clang
@@ -1,19 +1,17 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 ac_add_options --enable-dmd
 
-# Disable stylo until bug 1356926 is fixed and we have >= llvm39 on centos.
-ac_add_options --disable-stylo
-unset LLVM_CONFIG
+. $topsrcdir/build/mozconfig.stylo
 
 # Use Clang as specified in manifest
 CC="$topsrcdir/clang/bin/clang"
 CXX="$topsrcdir/clang/bin/clang++"
 
 # Add the static checker
 ac_add_options --enable-clang-plugin
 
deleted file mode 100644
--- a/browser/config/tooltool-manifests/linux64/asan.manifest
+++ /dev/null
@@ -1,18 +0,0 @@
-[
-  {
-    "version": "rustc 1.19.0 (0ade33941 2017-07-17) repack",
-    "size": 161014632,
-    "digest": "65bebcf94fc66ea618c58c9ac33f0f206095ecfe3931cc6edb301f4b40480e3b44b0f39aea7a25fed8eef47e63523e7e670082947a3662cdc04c68ebbe5dfc89",
-    "algorithm": "sha512",
-    "filename": "rustc.tar.xz",
-    "unpack": true
-  },
-  {
-    "size": 12072532,
-    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-    "algorithm": "sha512",
-    "filename": "gtk3.tar.xz",
-    "setup": "setup.sh",
-    "unpack": true
-  }
-]
deleted file mode 100644
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ /dev/null
@@ -1,18 +0,0 @@
-[
-  {
-    "size": 12072532,
-    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-    "algorithm": "sha512",
-    "filename": "gtk3.tar.xz",
-    "setup": "setup.sh",
-    "unpack": true
-  },
-  {
-    "version": "rustc 1.19.0 (0ade33941 2017-07-17) repack",
-    "size": 161014632,
-    "digest": "65bebcf94fc66ea618c58c9ac33f0f206095ecfe3931cc6edb301f4b40480e3b44b0f39aea7a25fed8eef47e63523e7e670082947a3662cdc04c68ebbe5dfc89",
-    "algorithm": "sha512",
-    "filename": "rustc.tar.xz",
-    "unpack": true
-  }
-]
deleted file mode 100644
--- a/browser/config/tooltool-manifests/linux64/clang.manifest.centos6
+++ /dev/null
@@ -1,26 +0,0 @@
-[
-  {
-    "version": "clang 3.8.0, libgcc 4.8.5",
-    "size": 140319580,
-    "digest": "34e219d7e8eaffa81710631c34d21355563d06335b3c00851e94c1f42f9098788fded8463dd0f67dd699f77b47a0245dd7aff754943a7a03fb5fd145a808254f",
-    "algorithm": "sha512",
-    "filename": "clang.tar.xz",
-    "unpack": true
-  },
-  {
-    "size": 12072532,
-    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-    "algorithm": "sha512",
-    "filename": "gtk3.tar.xz",
-    "setup": "setup.sh",
-    "unpack": true
-  },
-  {
-    "version": "rustc 1.19.0 (0ade33941 2017-07-17) repack",
-    "size": 161014632,
-    "digest": "65bebcf94fc66ea618c58c9ac33f0f206095ecfe3931cc6edb301f4b40480e3b44b0f39aea7a25fed8eef47e63523e7e670082947a3662cdc04c68ebbe5dfc89",
-    "algorithm": "sha512",
-    "filename": "rustc.tar.xz",
-    "unpack": true
-  }
-]
deleted file mode 100644
--- a/browser/config/tooltool-manifests/linux64/fuzzing.manifest
+++ /dev/null
@@ -1,18 +0,0 @@
-[
-  {
-    "version": "rustc 1.19.0 (0ade33941 2017-07-17) repack",
-    "size": 161014632,
-    "digest": "65bebcf94fc66ea618c58c9ac33f0f206095ecfe3931cc6edb301f4b40480e3b44b0f39aea7a25fed8eef47e63523e7e670082947a3662cdc04c68ebbe5dfc89",
-    "algorithm": "sha512",
-    "filename": "rustc.tar.xz",
-    "unpack": true
-  },
-  {
-    "size": 12072532,
-    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-    "algorithm": "sha512",
-    "filename": "gtk3.tar.xz",
-    "setup": "setup.sh",
-    "unpack": true
-  }
-]
deleted file mode 100644
--- a/browser/config/tooltool-manifests/linux64/msan.manifest
+++ /dev/null
@@ -1,18 +0,0 @@
-[
-  {
-    "version": "rustc 1.19.0 (0ade33941 2017-07-17) repack",
-    "size": 161014632,
-    "digest": "65bebcf94fc66ea618c58c9ac33f0f206095ecfe3931cc6edb301f4b40480e3b44b0f39aea7a25fed8eef47e63523e7e670082947a3662cdc04c68ebbe5dfc89",
-    "algorithm": "sha512",
-    "filename": "rustc.tar.xz",
-    "unpack": true
-  },
-  {
-    "size": 12072532,
-    "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
-    "algorithm": "sha512",
-    "filename": "gtk3.tar.xz",
-    "setup": "setup.sh",
-    "unpack": true
-  }
-]
--- a/browser/extensions/onboarding/content/onboarding.css
+++ b/browser/extensions/onboarding/content/onboarding.css
@@ -29,16 +29,24 @@
   cursor: pointer;
   top: 34px;
   offset-inline-start: 34px;
   border: none;
   /* Set to none so no grey contrast background in the high-contrast mode */
   background: none;
 }
 
+/* Keyboard focus styling */
+#onboarding-overlay-button:-moz-focusring {
+  outline: solid 2px rgba(0, 0, 0, 0.1);
+  -moz-outline-radius: 5px;
+  outline-offset: 5px;
+  transition: outline-offset 150ms;
+}
+
 #onboarding-overlay-button-icon {
   width: 36px;
   vertical-align: top;
 }
 
 #onboarding-notification-icon::after,
 #onboarding-overlay-button::after {
   background: #5ce6e6;
@@ -303,17 +311,18 @@
   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 {
+.onboarding-tour-action-button::-moz-focus-inner,
+#onboarding-overlay-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;
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -389,23 +389,24 @@ class Onboarding {
   _initUI() {
     if (this.uiInitialized) {
       return;
     }
     this.uiInitialized = true;
     this._tourItems = [];
     this._tourPages = [];
 
+    let { body } = this._window.document;
     this._overlayIcon = this._renderOverlayButton();
     this._overlayIcon.addEventListener("click", this);
-    this._window.document.body.appendChild(this._overlayIcon);
+    body.insertBefore(this._overlayIcon, body.firstChild);
 
     this._overlay = this._renderOverlay();
     this._overlay.addEventListener("click", this);
-    this._window.document.body.appendChild(this._overlay);
+    body.appendChild(this._overlay);
 
     this._loadJS(TOUR_AGENT_JS_URI);
 
     this._initPrefObserver();
     // Doing tour notification takes some effort. Let's do it on idle.
     this._window.requestIdleCallback(() => this._initNotification());
   }
 
@@ -822,18 +823,21 @@ class Onboarding {
 
   _renderOverlayButton() {
     let button = this._window.document.createElement("button");
     let tooltipStringId = this._tourType === "new" ?
       "onboarding.overlay-icon-tooltip" : "onboarding.overlay-icon-tooltip-updated";
     let tooltip = this._bundle.formatStringFromName(tooltipStringId, [BRAND_SHORT_NAME], 1);
     button.setAttribute("aria-label", tooltip);
     button.id = "onboarding-overlay-button";
+    button.setAttribute("aria-haspopup", true);
+    button.setAttribute("aria-controls", "onboarding-overlay-dialog");
     let img = this._window.document.createElement("img");
     img.id = "onboarding-overlay-button-icon";
+    img.setAttribute("role", "presentation");
     img.src = "resource://onboarding/img/overlay-icon.svg";
     button.appendChild(img);
     return button;
   }
 
   _loadTours(tours) {
     let itemsFrag = this._window.document.createDocumentFragment();
     let pagesFrag = this._window.document.createDocumentFragment();
--- a/browser/extensions/onboarding/test/browser/browser.ini
+++ b/browser/extensions/onboarding/test/browser/browser.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 support-files =
   head.js
 
+[browser_onboarding_accessibility.js]
 [browser_onboarding_notification.js]
 [browser_onboarding_notification_2.js]
 [browser_onboarding_notification_3.js]
 [browser_onboarding_notification_4.js]
 [browser_onboarding_tours.js]
 [browser_onboarding_tourset.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_accessibility.js
@@ -0,0 +1,31 @@
+/* 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";
+
+add_task(async function test_onboarding_overlay_button() {
+  resetOnboardingDefaultState();
+
+  info("Wait for onboarding overlay loaded");
+  let tab = await openTab(ABOUT_HOME_URL);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+
+  info("Test accessibility and semantics of the overlay button");
+  await ContentTask.spawn(tab.linkedBrowser, {}, function() {
+    let doc = content.document;
+    let button = doc.body.firstChild;
+    is(button.id, "onboarding-overlay-button",
+      "First child is an overlay button");
+    ok(button.getAttribute("aria-label"),
+      "Onboarding button has an accessible label");
+    is(button.getAttribute("aria-haspopup"), "true",
+      "Onboarding button should indicate that it triggers a popup");
+    is(button.getAttribute("aria-controls"), "onboarding-overlay-dialog",
+      "Onboarding button semantically controls an overlay dialog");
+    is(button.firstChild.getAttribute("role"), "presentation",
+      "Onboarding button icon should have presentation only semantics");
+  });
+
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/browser/installer/allowed-dupes.mn
+++ b/browser/installer/allowed-dupes.mn
@@ -26,23 +26,21 @@ browser/chrome/browser/content/browser/p
 browser/chrome/browser/content/browser/preferences/in-content-new/main.js
 browser/chrome/browser/content/browser/preferences/in-content-new/preferences.js
 browser/chrome/browser/content/browser/preferences/in-content-new/privacy.js
 browser/chrome/browser/content/browser/preferences/in-content-new/search.js
 browser/chrome/browser/content/browser/preferences/in-content-new/security.js
 browser/chrome/browser/content/browser/preferences/in-content-new/sync.js
 browser/chrome/browser/skin/classic/browser/preferences/in-content/search.css
 browser/chrome/browser/skin/classic/browser/preferences/in-content/containers.css
-browser/chrome/browser/skin/classic/browser/preferences/in-content/favicon.ico
 browser/chrome/browser/skin/classic/browser/preferences/in-content/icons.svg
 browser/chrome/browser/skin/classic/browser/preferences/in-content/preferences.css
 browser/chrome/browser/skin/classic/browser/preferences/in-content/dialog.css
 browser/chrome/browser/skin/classic/browser/preferences/in-content-new/search.css
 browser/chrome/browser/skin/classic/browser/preferences/in-content-new/containers.css
-browser/chrome/browser/skin/classic/browser/preferences/in-content-new/favicon.ico
 browser/chrome/browser/skin/classic/browser/preferences/in-content-new/icons.svg
 browser/chrome/browser/skin/classic/browser/preferences/in-content-new/preferences.css
 browser/chrome/browser/skin/classic/browser/preferences/in-content-new/dialog.css
 # browser branding / themes is bug 1313106
 browser/chrome/browser/content/branding/icon128.png
 browser/chrome/browser/content/branding/icon16.png
 browser/chrome/browser/content/branding/icon32.png
 browser/chrome/browser/content/branding/icon48.png
deleted file mode 100644
index 4d07d2b5d47327c5b8854298f782db4c7ba1e162..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 4d07d2b5d47327c5b8854298f782db4c7ba1e162..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -90,23 +90,21 @@
   skin/classic/browser/panel-icon-arrow-right.svg              (../shared/panel-icon-arrow-right.svg)
   skin/classic/browser/panel-icon-cancel.svg                   (../shared/panel-icon-cancel.svg)
 #ifndef XP_MACOSX
   skin/classic/browser/panel-icon-folder.svg                   (../shared/panel-icon-folder.svg)
 #else
   skin/classic/browser/panel-icon-magnifier.svg                (../shared/panel-icon-magnifier.svg)
 #endif
   skin/classic/browser/panel-icon-retry.svg                    (../shared/panel-icon-retry.svg)
-  skin/classic/browser/preferences/in-content-new/favicon.ico      (../shared/incontentprefs/favicon.ico)
   skin/classic/browser/preferences/in-content-new/icons.svg        (../shared/incontentprefs/icons.svg)
   skin/classic/browser/preferences/in-content-new/search-arrow-indicator.svg  (../shared/incontentprefs/search-arrow-indicator.svg)
   skin/classic/browser/preferences/in-content-new/search.css       (../shared/incontentprefs/search.css)
   skin/classic/browser/preferences/in-content-new/siteDataSettings.css (../shared/incontentprefs/siteDataSettings.css)
 * skin/classic/browser/preferences/in-content-new/containers.css   (../shared/incontentprefs/containers.css)
-  skin/classic/browser/preferences/in-content/favicon.ico      (../shared/incontentprefs-old/favicon.ico)
   skin/classic/browser/preferences/in-content/icons.svg        (../shared/incontentprefs-old/icons.svg)
   skin/classic/browser/preferences/in-content/search.css       (../shared/incontentprefs-old/search.css)
 * skin/classic/browser/preferences/in-content/containers.css   (../shared/incontentprefs-old/containers.css)
 * skin/classic/browser/preferences/containers.css              (../shared/preferences/containers.css)
   skin/classic/browser/fxa/default-avatar.svg                  (../shared/fxa/default-avatar.svg)
   skin/classic/browser/fxa/logo.png                            (../shared/fxa/logo.png)
   skin/classic/browser/fxa/logo@2x.png                         (../shared/fxa/logo@2x.png)
   skin/classic/browser/fxa/sync-illustration.svg               (../shared/fxa/sync-illustration.svg)
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -72,16 +72,21 @@
   width: 16px;
   margin-inline-end: 6px;
 }
 
 .tab-icon-image {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
 }
 
+.tab-icon-image[src^="chrome://"] {
+  -moz-context-properties: fill;
+  fill: currentColor;
+}
+
 .tab-icon-image[sharing]:not([selected]),
 .tab-sharing-icon-overlay {
   animation: 3s linear tab-sharing-icon-pulse infinite;
 }
 
 /* This should remain identical to identity-box-sharing-icon-pulse in identity-block.inc.css */
 @keyframes tab-sharing-icon-pulse {
   0%, 16.66%, 83.33%, 100% {
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Tabs.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Tabs.jsm
@@ -4,17 +4,17 @@
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["Tabs"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 const CUST_TAB = "chrome://browser/skin/customizableui/customizeFavicon.ico";
-const PREFS_TAB = "chrome://browser/skin/preferences/in-content-new/favicon.ico";
+const PREFS_TAB = "chrome://browser/skin/settings.svg";
 const DEFAULT_FAVICON_TAB = `data:text/html,<meta charset="utf-8">
 <title>No favicon</title>`;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 
 this.Tabs = {
   init(libDir) {},
--- a/build/unix/stdc++compat/stdc++compat.cpp
+++ b/build/unix/stdc++compat/stdc++compat.cpp
@@ -23,37 +23,38 @@
    GLIBCXX_3.4.19 is from gcc 4.8.1 (199309)
    GLIBCXX_3.4.20 is from gcc 4.9.0 (199307)
    GLIBCXX_3.4.21 is from gcc 5.0 (210290)
 
 This file adds the necessary compatibility tricks to avoid symbols with
 version GLIBCXX_3.4.16 and bigger, keeping binary compatibility with
 libstdc++ 4.6.1.
 
+WARNING: all symbols from this file must be defined weak.
 */
 
 #define GLIBCXX_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c))
 
 #if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 18)
 // Implementation of utility functions for the prime rehash policy used in
 // unordered_map and unordered_set.
 #include <unordered_map>
 #include <tr1/unordered_map>
 namespace std
 {
-  size_t
+  size_t __attribute__((weak))
   __detail::_Prime_rehash_policy::_M_next_bkt(size_t __n) const
   {
     tr1::__detail::_Prime_rehash_policy policy(_M_max_load_factor);
     size_t ret = policy._M_next_bkt(__n);
     _M_next_resize = policy._M_next_resize;
     return ret;
   }
 
-  pair<bool, size_t>
+  pair<bool, size_t> __attribute__((weak))
   __detail::_Prime_rehash_policy::_M_need_rehash(size_t __n_bkt,
                                                  size_t __n_elt,
                                                  size_t __n_ins) const
   {
     tr1::__detail::_Prime_rehash_policy policy(_M_max_load_factor);
     policy._M_next_resize = _M_next_resize;
     pair<bool, size_t> ret = policy._M_need_rehash(__n_bkt, __n_elt, __n_ins);
     _M_next_resize = policy._M_next_resize;
@@ -62,17 +63,17 @@ namespace std
 }
 #endif
 
 #if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 20)
 namespace std {
 
     /* We shouldn't be throwing exceptions at all, but it sadly turns out
        we call STL (inline) functions that do. */
-    void __throw_out_of_range_fmt(char const* fmt, ...)
+    void __attribute__((weak)) __throw_out_of_range_fmt(char const* fmt, ...)
     {
         va_list ap;
         char buf[1024]; // That should be big enough.
 
         va_start(ap, fmt);
         vsnprintf(buf, sizeof(buf), fmt, ap);
         buf[sizeof(buf) - 1] = 0;
         va_end(ap);
@@ -82,27 +83,27 @@ namespace std {
 
 }
 #endif
 
 #if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 20)
 /* Technically, this symbol is not in GLIBCXX_3.4.20, but in CXXABI_1.3.8,
    but that's equivalent, version-wise. Those calls are added by the compiler
    itself on `new Class[n]` calls. */
-extern "C" void
+extern "C" void __attribute__((weak))
 __cxa_throw_bad_array_new_length()
 {
     MOZ_CRASH();
 }
 #endif
 
 #if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 21)
 /* While we generally don't build with exceptions, we have some host tools
  * that do use them. libstdc++ from GCC 5.0 added exception constructors with
  * char const* argument. Older versions only have a constructor with
  * std::string. */
 namespace std {
-    runtime_error::runtime_error(char const* s)
+    __attribute__((weak)) runtime_error::runtime_error(char const* s)
     : runtime_error(std::string(s))
     {
     }
 }
 #endif
--- a/devtools/client/debugger/test/mochitest/browser2.ini
+++ b/devtools/client/debugger/test/mochitest/browser2.ini
@@ -440,17 +440,17 @@ skip-if = e10s && debug
 skip-if = e10s && debug
 [browser_dbg_variables-view-webidl.js]
 skip-if = e10s && debug
 [browser_dbg_watch-expressions-01.js]
 skip-if = e10s && debug
 [browser_dbg_watch-expressions-02.js]
 skip-if = e10s && debug
 [browser_dbg_worker-console-01.js]
-skip-if = e10s && debug
+skip-if = true # bug 1368569
 [browser_dbg_worker-console-02.js]
 skip-if = e10s && debug
 [browser_dbg_worker-console-03.js]
 skip-if = e10s && debug
 [browser_dbg_worker-console-04.js]
 skip-if = e10s && debug
 [browser_dbg_worker-source-map.js]
 skip-if = e10s && debug
--- a/devtools/client/inspector/computed/test/browser.ini
+++ b/devtools/client/inspector/computed/test/browser.ini
@@ -39,9 +39,9 @@ skip-if = (os == 'linux' && bits == 32 &
 [browser_computed_search-filter_noproperties.js]
 [browser_computed_select-and-copy-styles-01.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_computed_select-and-copy-styles-02.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_computed_style-editor-link.js]
-skip-if = os == 'mac' # bug 1307846
+skip-if = true # bug 1307846
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -240,14 +240,14 @@ skip-if = (os == 'linux' && bits == 32 &
 [browser_rules_shapes-toggle_05.js]
 [browser_rules_shapes-toggle_06.js]
 [browser_rules_shorthand-overridden-lists.js]
 [browser_rules_strict-search-filter-computed-list_01.js]
 [browser_rules_strict-search-filter_01.js]
 [browser_rules_strict-search-filter_02.js]
 [browser_rules_strict-search-filter_03.js]
 [browser_rules_style-editor-link.js]
-skip-if = !debug # Bug 1309759
+skip-if = true # Bug 1309759
 [browser_rules_url-click-opens-new-tab.js]
 [browser_rules_urls-clickable.js]
 [browser_rules_user-agent-styles.js]
 [browser_rules_user-agent-styles-uneditable.js]
 [browser_rules_user-property-reset.js]
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -8626,16 +8626,22 @@ public:
 private:
   nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
     mRestorePresentationEvent;
   RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
 };
 
 } // namespace
 
+bool
+nsDocShell::SandboxFlagsImplyCookies(const uint32_t &aSandboxFlags)
+{
+  return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
+}
+
 nsresult
 nsDocShell::RestoreFromHistory()
 {
   MOZ_ASSERT(mRestorePresentationEvent.IsPending());
   PresentationEventForgetter forgetter(mRestorePresentationEvent);
 
   // This section of code follows the same ordering as CreateContentViewer.
   if (!mLSHE) {
@@ -9286,16 +9292,19 @@ nsDocShell::CreateContentViewer(const ns
      * we don't null-out mLSHE in OnStateChange() for
      * all redirected urls
      */
     aOpenedChannel->SetLoadGroup(mLoadGroup);
 
     // Mark the channel as being a document URI...
     aOpenedChannel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
+    if (SandboxFlagsImplyCookies(mSandboxFlags)) {
+      loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
+    }
 
     aOpenedChannel->SetLoadFlags(loadFlags);
 
     mLoadGroup->AddRequest(aRequest, nullptr);
     if (currentLoadGroup) {
       currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
     }
 
@@ -11502,16 +11511,19 @@ nsDocShell::DoChannelLoad(nsIChannel* aC
 {
   nsresult rv;
   // Mark the channel as being a document URI and allow content sniffing...
   nsLoadFlags loadFlags = 0;
   (void)aChannel->GetLoadFlags(&loadFlags);
   loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
                nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
 
+  if (SandboxFlagsImplyCookies(mSandboxFlags)) {
+    loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
+  }
   // Load attributes depend on load type...
   switch (mLoadType) {
     case LOAD_HISTORY: {
       // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
       // push/replaceState (bug 669671).
       bool uriModified = false;
       if (mLSHE) {
         mLSHE->GetURIWasModified(&uriModified);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -252,16 +252,18 @@ public:
   // ForceRefreshURI method on nsIRefreshURI, but makes sure to take
   // the timer involved out of mRefreshURIList if it's there.
   // aTimer must not be null.
   nsresult ForceRefreshURIFromTimer(nsIURI* aURI, int32_t aDelay,
                                     bool aMetaRefresh, nsITimer* aTimer);
 
   friend class OnLinkClickEvent;
 
+  static bool SandboxFlagsImplyCookies(const uint32_t &aSandboxFlags);
+
   // We need dummy OnLocationChange in some cases to update the UI without
   // updating security info.
   void FireDummyOnLocationChange()
   {
     FireOnLocationChange(this, nullptr, mCurrentURI,
                          LOCATION_CHANGE_SAME_DOCUMENT);
   }
 
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -127,58 +127,16 @@ ToChar(SelectionType aSelectionType)
       return "SelectionType::eURLSecondary";
     case SelectionType::eURLStrikeout:
       return "SelectionType::eURLStrikeout";
     default:
       return "Invalid SelectionType";
   }
 }
 
-static bool
-IsValidSelectionType(RawSelectionType aRawSelectionType)
-{
-  switch (static_cast<SelectionType>(aRawSelectionType)) {
-    case SelectionType::eNone:
-    case SelectionType::eNormal:
-    case SelectionType::eSpellCheck:
-    case SelectionType::eIMERawClause:
-    case SelectionType::eIMESelectedRawClause:
-    case SelectionType::eIMEConvertedClause:
-    case SelectionType::eIMESelectedClause:
-    case SelectionType::eAccessibility:
-    case SelectionType::eFind:
-    case SelectionType::eURLSecondary:
-    case SelectionType::eURLStrikeout:
-      return true;
-    default:
-      return false;
-  }
-}
-
-SelectionType
-ToSelectionType(RawSelectionType aRawSelectionType)
-{
-  if (!IsValidSelectionType(aRawSelectionType)) {
-    return SelectionType::eInvalid;
-  }
-  return static_cast<SelectionType>(aRawSelectionType);
-}
-
-RawSelectionType
-ToRawSelectionType(SelectionType aSelectionType)
-{
-  return static_cast<RawSelectionType>(aSelectionType);
-}
-
-bool operator &(SelectionType aSelectionType,
-                RawSelectionType aRawSelectionTypes)
-{
-  return (ToRawSelectionType(aSelectionType) & aRawSelectionTypes) != 0;
-}
-
 } // namespace mozilla
 
 //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
 //#define DEBUG_NAVIGATION
 
 
 //#define DEBUG_TABLE_SELECTION 1
 
--- a/dom/base/Selection.h
+++ b/dom/base/Selection.h
@@ -68,18 +68,16 @@ public:
   Selection();
   explicit Selection(nsFrameSelection *aList);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Selection, nsISelectionPrivate)
   NS_DECL_NSISELECTION
   NS_DECL_NSISELECTIONPRIVATE
 
-  virtual Selection* AsSelection() override { return this; }
-
   nsresult EndBatchChangesInternal(int16_t aReason = nsISelectionListener::NO_REASON);
 
   nsIDocument* GetParentObject() const;
 
   // utility methods for scrolling the selection into view
   nsPresContext* GetPresContext() const;
   nsIPresShell* GetPresShell() const;
   nsFrameSelection* GetFrameSelection() const { return mFrameSelection; }
@@ -512,11 +510,65 @@ public:
   {
     if (mSelection) {
       mSelection->RemoveSelectionChangeBlocker();
     }
   }
 };
 
 } // namespace dom
+
+inline bool
+IsValidSelectionType(RawSelectionType aRawSelectionType)
+{
+  switch (static_cast<SelectionType>(aRawSelectionType)) {
+    case SelectionType::eNone:
+    case SelectionType::eNormal:
+    case SelectionType::eSpellCheck:
+    case SelectionType::eIMERawClause:
+    case SelectionType::eIMESelectedRawClause:
+    case SelectionType::eIMEConvertedClause:
+    case SelectionType::eIMESelectedClause:
+    case SelectionType::eAccessibility:
+    case SelectionType::eFind:
+    case SelectionType::eURLSecondary:
+    case SelectionType::eURLStrikeout:
+      return true;
+    default:
+      return false;
+  }
+}
+
+inline SelectionType
+ToSelectionType(RawSelectionType aRawSelectionType)
+{
+  if (!IsValidSelectionType(aRawSelectionType)) {
+    return SelectionType::eInvalid;
+  }
+  return static_cast<SelectionType>(aRawSelectionType);
+}
+
+inline RawSelectionType
+ToRawSelectionType(SelectionType aSelectionType)
+{
+  return static_cast<RawSelectionType>(aSelectionType);
+}
+
+inline RawSelectionType ToRawSelectionType(TextRangeType aTextRangeType)
+{
+  return ToRawSelectionType(ToSelectionType(aTextRangeType));
+}
+
+inline bool operator &(SelectionType aSelectionType,
+                       RawSelectionType aRawSelectionTypes)
+{
+  return (ToRawSelectionType(aSelectionType) & aRawSelectionTypes) != 0;
+}
+
 } // namespace mozilla
 
+inline mozilla::dom::Selection*
+nsISelection::AsSelection()
+{
+  return static_cast<mozilla::dom::Selection*>(this);
+}
+
 #endif // mozilla_Selection_h__
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2471,16 +2471,27 @@ WarnIfSandboxIneffective(nsIDocShell* aD
                                     NS_LITERAL_CSTRING("Iframe Sandbox"),
                                     parentDocument,
                                     nsContentUtils::eSECURITY_PROPERTIES,
                                     "BothAllowScriptsAndSameOriginPresent",
                                     nullptr, 0, iframeUri);
   }
 }
 
+bool
+nsDocument::IsSynthesized() {
+  nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(mChannel);
+  bool synthesized = false;
+  if (internalChan) {
+    DebugOnly<nsresult> rv = internalChan->GetResponseSynthesized(&synthesized);
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "GetResponseSynthesized shouldn't fail.");
+  }
+  return synthesized;
+}
+
 nsresult
 nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
                               nsILoadGroup* aLoadGroup,
                               nsISupports* aContainer,
                               nsIStreamListener **aDocListener,
                               bool aReset, nsIContentSink* aSink)
 {
   if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
@@ -2541,16 +2552,29 @@ nsDocument::StartDocumentLoad(const char
   if (inStrmChan) {
     bool isSrcdocChannel;
     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
     if (isSrcdocChannel) {
       mIsSrcdocDocument = true;
     }
   }
 
+  if (mChannel) {
+    nsLoadFlags loadFlags;
+    mChannel->GetLoadFlags(&loadFlags);
+    bool isDocument = false;
+    mChannel->GetIsDocument(&isDocument);
+    if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE &&
+        isDocument &&
+        IsSynthesized() &&
+        XRE_IsContentProcess()) {
+      ContentChild::UpdateCookieStatus(mChannel);
+    }
+  }
+
   // If this document is being loaded by a docshell, copy its sandbox flags
   // to the document, and store the fullscreen enabled flag. These are
   // immutable after being set here.
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
 
   // If this is an error page, don't inherit sandbox flags from docshell
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   if (docShell && !(loadInfo && loadInfo->GetLoadErrorPage())) {
@@ -8275,31 +8299,39 @@ void
 nsDocument::AddToRadioGroup(const nsAString& aName,
                             nsIFormControl* aRadio)
 {
   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
   radioGroup->mRadioButtons.AppendObject(aRadio);
 
   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
   NS_ASSERTION(element, "radio controls have to be content elements");
-  if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+
+  HTMLInputElement* input = HTMLInputElement::FromContent(element);
+  NS_ASSERTION(input, "radio controls have to be input elements!");
+
+  if (input->IsRequired()) {
     radioGroup->mRequiredRadioCount++;
   }
 }
 
 void
 nsDocument::RemoveFromRadioGroup(const nsAString& aName,
                                  nsIFormControl* aRadio)
 {
   nsRadioGroupStruct* radioGroup = GetOrCreateRadioGroup(aName);
   radioGroup->mRadioButtons.RemoveObject(aRadio);
 
   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
   NS_ASSERTION(element, "radio controls have to be content elements");
-  if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+
+  HTMLInputElement* input = HTMLInputElement::FromContent(element);
+  NS_ASSERTION(input, "radio controls have to be input elements!");
+
+  if (input->IsRequired()) {
     NS_ASSERTION(radioGroup->mRequiredRadioCount != 0,
                  "mRequiredRadioCount about to wrap below 0!");
     radioGroup->mRequiredRadioCount--;
   }
 }
 
 NS_IMETHODIMP
 nsDocument::WalkRadioGroup(const nsAString& aName,
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -662,16 +662,17 @@ public:
   virtual void ScheduleIntersectionObserverNotification() override;
   virtual void NotifyIntersectionObservers() override;
 
   virtual void NotifyLayerManagerRecreated() override;
 
   virtual void ScheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
   virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
   virtual void ResolveScheduledSVGPresAttrs() override;
+  bool IsSynthesized();
 
 private:
   void AddOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet);
   void SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages);
 
 public:
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE_TO_NSINODE_OVERRIDABLE
--- a/dom/base/nsISelection.idl
+++ b/dom/base/nsISelection.idl
@@ -156,15 +156,16 @@ interface nsISelection : nsISupports
      * "documentboundary".  Returns NS_ERROR_INVALID_ARG if alter, direction,
      * or granularity has an unrecognized value.
      */
     void modify(in DOMString alter, in DOMString direction,
                 in DOMString granularity);
 
 %{C++
     /**
-     * AsSelection() returns a pointer to Selection class if the instance is
-     * derived from it.  Otherwise, nullptr but it should never happen
-     * since Selection is the only class implementing nsISelection.
+     * AsSelection() returns a pointer to Selection class.
+     *
+     * In order to avoid circular dependency issues, this method is defined
+     * in mozilla/dom/Selection.h.  Consumers need to #include that header.
      */
-    virtual mozilla::dom::Selection* AsSelection() = 0;
+    inline mozilla::dom::Selection* AsSelection();
 %}
 };
--- a/dom/base/nsISelectionController.idl
+++ b/dom/base/nsISelectionController.idl
@@ -3,23 +3,30 @@
  * 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 "nsISelectionDisplay.idl"
 
 %{C++
 typedef short SelectionRegion;
+namespace mozilla {
+namespace dom {
+class Selection;
+} // namespace dom
+} // namespace mozilla
 %}
 
 interface nsIContent;
 interface nsIDOMNode;
 interface nsISelection;
 interface nsISelectionDisplay;
 
+[ptr] native SelectionPtr(mozilla::dom::Selection);
+
 [scriptable, uuid(3801c9d4-8e69-4bfc-9edb-b58278621f8f)]
 interface nsISelectionController : nsISelectionDisplay
 {
    // RawSelectionType values:
    const short SELECTION_NONE=0;
    const short SELECTION_NORMAL=1;
    const short SELECTION_SPELLCHECK=2;
    const short SELECTION_IME_RAWINPUT=4;
@@ -59,16 +66,21 @@ interface nsISelectionController : nsISe
    *  shell may implement.
    *
    * @param aType This will hold the type of selection.  This value must be one
    *              of RawSelectionType values.
    * @param _return will hold the return value
    */
     nsISelection getSelection(in short type);
 
+   /**
+   * Return the selection object corresponding to a selection type.
+   */
+    [noscript,nostdcall,notxpcom] SelectionPtr getDOMSelection(in short aType);
+
    const short SCROLL_SYNCHRONOUS = 1<<1;
    const short SCROLL_FIRST_ANCESTOR_ONLY = 1<<2;
    const short SCROLL_CENTER_VERTICALLY = 1<<4;
    const short SCROLL_OVERFLOW_HIDDEN = 1<<5;
    const short SCROLL_FOR_CARET_MOVE = 1<<6;
 
    /**
    * ScrollSelectionIntoView scrolls a region of the selection,
@@ -307,16 +319,17 @@ enum class SelectionType : RawSelectionT
 enum : size_t
 {
   // kSelectionTypeCount is number of SelectionType.
   kSelectionTypeCount = nsISelectionController::NUM_SELECTIONTYPES,
   // kPresentSelectionTypeCount is number of SelectionType except "none".
   kPresentSelectionTypeCount = kSelectionTypeCount - 1
 };
 
+// Please include mozilla/dom/Selection.h for the following APIs.
 const char* ToChar(SelectionType aSelectionType);
-SelectionType ToSelectionType(RawSelectionType aRawSelectionType);
-RawSelectionType ToRawSelectionType(SelectionType aSelectionType);
-bool operator &(SelectionType aSelectionType,
-                RawSelectionType aRawSelectionTypes);
+inline SelectionType ToSelectionType(RawSelectionType aRawSelectionType);
+inline RawSelectionType ToRawSelectionType(SelectionType aSelectionType);
+inline bool operator &(SelectionType aSelectionType,
+                       RawSelectionType aRawSelectionTypes);
 
 } // namespace mozilla
 %}
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug518104.js
@@ -0,0 +1,3 @@
+document.write("<p></p>");
+parent.done();
+document.close();
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -452,16 +452,17 @@ skip-if = toolkit == 'android' #TIMED_OU
 [test_bug503481b.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug505783.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug513194.html]
 [test_bug514487.html]
 [test_bug515401.html]
 [test_bug518104.html]
+support-files = file_bug518104.js
 [test_bug527896.html]
 [test_bug540854.html]
 [test_bug541937.html]
 [test_bug544642.html]
 [test_bug545644.html]
 [test_bug545644.xhtml]
 [test_bug548463.html]
 [test_bug553896.xhtml]
--- a/dom/base/test/test_bug518104.html
+++ b/dom/base/test/test_bug518104.html
@@ -21,17 +21,15 @@ function done() {
   var iframe = document.getElementById("iframe");
   var divs = iframe.contentWindow.document.getElementsByTagName("p").length;
   is(divs, 0, "<p> got written.")
   SimpleTest.finish();
 }
 
 </script>
 <div id="content" style="display: none">
-  <iframe id='iframe' src="data:text/html,
-    <div></div><div></div>
-    <script defer src='data:application/javascript,document.write(%2522<p></p>%2522);parent.done();document.close();'></script>">
+  <iframe id='iframe' srcdoc="<div></div><div></div><script defer src='file_bug518104.js'></script>">
   </iframe>
 </div>
 <pre id="test">
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/file_barewordGetsWindow_frame1.html
@@ -0,0 +1,1 @@
+OLD
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/file_barewordGetsWindow_frame2.html
@@ -0,0 +1,1 @@
+NEW
\ No newline at end of file
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -20,16 +20,19 @@ support-files =
 [test_bug862092.html]
 [test_bug1036214.html]
 skip-if = debug == false
 [test_bug963382.html]
 skip-if = debug == false
 [test_bug1041646.html]
 [test_bug1123875.html]
 [test_barewordGetsWindow.html]
+support-files =
+  file_barewordGetsWindow_frame1.html
+  file_barewordGetsWindow_frame2.html
 [test_callback_across_document_open.html]
 [test_callback_default_thisval.html]
 [test_cloneAndImportNode.html]
 [test_defineProperty.html]
 [test_enums.html]
 [test_exceptionThrowing.html]
 [test_exception_messages.html]
 [test_forOf.html]
--- a/dom/bindings/test/test_barewordGetsWindow.html
+++ b/dom/bindings/test/test_barewordGetsWindow.html
@@ -22,39 +22,39 @@ https://bugzilla.mozilla.org/show_bug.cg
     get = desc.get;
     ok(get, "Couldn't find document getter");
     Object.defineProperty(frames[0], "foo", { get: get, configurable: true });
 
     var barewordFunc = frames[0].eval("(function (count) { var doc; for (var i = 0; i < count; ++i) doc = foo; return doc.documentElement; })");
     var qualifiedFunc = frames[0].eval("(function (count) { var doc; for (var i = 0; i < count; ++i) doc = window.document; return doc.documentElement; })");
     document.querySelector("iframe").onload = function () {
       // interp
-      is(barewordFunc(1).textContent, "OLD", "Bareword should see own inner 1");
-      is(qualifiedFunc(1).textContent, "NEW",
+      is(barewordFunc(1).innerText, "OLD", "Bareword should see own inner 1");
+      is(qualifiedFunc(1).innerText, "NEW",
          "Qualified should see current inner 1");
       // baseline
-      is(barewordFunc(100).textContent, "OLD", "Bareword should see own inner 2");
-      is(qualifiedFunc(100).textContent, "NEW",
+      is(barewordFunc(100).innerText, "OLD", "Bareword should see own inner 2");
+      is(qualifiedFunc(100).innerText, "NEW",
          "Qualified should see current inner 2");
       // ion
-      is(barewordFunc(10000).textContent, "OLD", "Bareword should see own inner 3");
-      is(qualifiedFunc(10000).textContent, "NEW",
+      is(barewordFunc(10000).innerText, "OLD", "Bareword should see own inner 3");
+      is(qualifiedFunc(10000).innerText, "NEW",
          "Qualified should see current inner 2");
       SimpleTest.finish();
     }
-    frames[0].location = "data:text/plain,NEW";
+    frames[0].location = "file_barewordGetsWindow_frame2.html";
   }
 
 
 
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936056">Mozilla Bug 936056</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-<iframe src="data:text/plain,OLD"></iframe>
+<iframe src="file_barewordGetsWindow_frame1.html"></iframe>
 </div>
 <pre id="test">
 </pre>
 </body>
 </html>
--- a/dom/browser-element/mochitest/browserElement_CookiesNotThirdParty.js
+++ b/dom/browser-element/mochitest/browserElement_CookiesNotThirdParty.js
@@ -42,10 +42,11 @@ function runTest() {
   // alert('failure:'), as appropriate.  Finally, the page will
   // alert('finish');
   iframe.src = innerPage;
   document.body.appendChild(iframe);
 }
 
 // Disable third-party cookies for this test.
 addEventListener('testready', function() {
-  SpecialPowers.pushPrefEnv({'set': [['network.cookie.cookieBehavior', 1]]}, runTest);
+  SpecialPowers.pushPrefEnv({'set': [['network.cookie.cookieBehavior', 1],
+                                     ['network.cookie.ipc.sync', true]]}, runTest);
 });
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -485,17 +485,17 @@ EventStateManager::OnStopObservingConten
   NS_ENSURE_TRUE_VOID(mIMEContentObserver == aIMEContentObserver);
   mIMEContentObserver = nullptr;
 }
 
 void
 EventStateManager::TryToFlushPendingNotificationsToIME()
 {
   if (mIMEContentObserver) {
-    mIMEContentObserver->TryToFlushPendingNotifications();
+    mIMEContentObserver->TryToFlushPendingNotifications(true);
   }
 }
 
 static bool
 IsMessageMouseUserActivity(EventMessage aMessage)
 {
   return aMessage == eMouseMove ||
          aMessage == eMouseUp ||
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -325,16 +325,18 @@ private:
 
 #define DIR_ATTR_STATES (NS_EVENT_STATE_HAS_DIR_ATTR |          \
                          NS_EVENT_STATE_DIR_ATTR_LTR |          \
                          NS_EVENT_STATE_DIR_ATTR_RTL |          \
                          NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO)
 
 #define DISABLED_STATES (NS_EVENT_STATE_DISABLED | NS_EVENT_STATE_ENABLED)
 
+#define REQUIRED_STATES (NS_EVENT_STATE_REQUIRED | NS_EVENT_STATE_OPTIONAL)
+
 // Event states that can be added and removed through
 // Element::{Add,Remove}ManuallyManagedStates.
 //
 // Take care when manually managing state bits.  You are responsible for
 // setting or clearing the bit when an Element is added or removed from a
 // document (e.g. in BindToTree and UnbindFromTree), if that is an
 // appropriate thing to do for your state bit.
 #define MANUALLY_MANAGED_STATES (             \
@@ -345,16 +347,17 @@ private:
 // Event states that are managed externally to an element (by the
 // EventStateManager, or by other code).  As opposed to those in
 // INTRINSIC_STATES, which are are computed by the element itself
 // and returned from Element::IntrinsicState.
 #define EXTERNALLY_MANAGED_STATES (           \
   MANUALLY_MANAGED_STATES |                   \
   DIR_ATTR_STATES |                           \
   DISABLED_STATES |                           \
+  REQUIRED_STATES |                           \
   NS_EVENT_STATE_ACTIVE |                     \
   NS_EVENT_STATE_DRAGOVER |                   \
   NS_EVENT_STATE_FOCUS |                      \
   NS_EVENT_STATE_FOCUSRING |                  \
   NS_EVENT_STATE_FOCUS_WITHIN |               \
   NS_EVENT_STATE_FULL_SCREEN |                \
   NS_EVENT_STATE_HOVER |                      \
   NS_EVENT_STATE_UNRESOLVED |                 \
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -1734,20 +1734,20 @@ IMEContentObserver::FlushMergeableNotifi
   mQueuedSender = new IMENotificationSender(this);
   mQueuedSender->Dispatch(mDocShell);
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::FlushMergeableNotifications(), "
      "finished", this));
 }
 
 void
-IMEContentObserver::TryToFlushPendingNotifications()
+IMEContentObserver::TryToFlushPendingNotifications(bool aAllowAsync)
 {
   if (!mQueuedSender || mSendingNotification != NOTIFY_IME_OF_NOTHING ||
-      XRE_IsContentProcess()) {
+      (XRE_IsContentProcess() && aAllowAsync)) {
     return;
   }
 
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::TryToFlushPendingNotifications(), "
      "performing queued IMENotificationSender forcibly", this));
   RefPtr<IMENotificationSender> queuedSender = mQueuedSender;
   queuedSender->Run();
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -150,19 +150,19 @@ public:
   nsPresContext* GetPresContext() const;
   nsresult GetSelectionAndRoot(nsISelection** aSelection,
                                nsIContent** aRoot) const;
 
   /**
    * TryToFlushPendingNotifications() should be called when pending events
    * should be flushed.  This tries to run the queued IMENotificationSender.
    * Doesn't do anything in child processes where flushing happens
-   * asynchronously.
+   * asynchronously unless aAllowAsync is false.
    */
-  void TryToFlushPendingNotifications();
+  void TryToFlushPendingNotifications(bool aAllowAsync);
 
   /**
    * MaybeNotifyCompositionEventHandled() posts composition event handled
    * notification into the pseudo queue.
    */
   void MaybeNotifyCompositionEventHandled();
 
 private:
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -647,17 +647,17 @@ IMEStateManager::OnChangeFocusInternal(n
   // editor.
   if (newState.mEnabled == IMEState::PLUGIN) {
     CreateIMEContentObserver(nullptr);
     if (sActiveIMEContentObserver) {
       MOZ_LOG(sISMLog, LogLevel::Debug,
         ("  OnChangeFocusInternal(), an "
          "IMEContentObserver instance is created for plugin and trying to "
          "flush its pending notifications..."));
-      sActiveIMEContentObserver->TryToFlushPendingNotifications();
+      sActiveIMEContentObserver->TryToFlushPendingNotifications(false);
     }
   }
 
   return NS_OK;
 }
 
 // static
 void
@@ -822,17 +822,17 @@ IMEStateManager::OnFocusInEditor(nsPresC
 
   CreateIMEContentObserver(&aEditorBase);
 
   // Let's flush the focus notification now.
   if (sActiveIMEContentObserver) {
     MOZ_LOG(sISMLog, LogLevel::Debug,
       ("  OnFocusInEditor(), new IMEContentObserver is "
        "created, trying to flush pending notifications..."));
-    sActiveIMEContentObserver->TryToFlushPendingNotifications();
+    sActiveIMEContentObserver->TryToFlushPendingNotifications(false);
   }
 }
 
 // static
 void
 IMEStateManager::OnEditorInitialized(EditorBase& aEditorBase)
 {
   if (!sActiveIMEContentObserver ||
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -2309,34 +2309,40 @@ HTMLFormElement::WalkRadioGroup(const ns
 
 void
 HTMLFormElement::AddToRadioGroup(const nsAString& aName,
                                  nsIFormControl* aRadio)
 {
   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
   NS_ASSERTION(element, "radio controls have to be content elements!");
 
-  if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+  HTMLInputElement* input = HTMLInputElement::FromContent(element);
+  NS_ASSERTION(input, "radio controls have to be input elements!");
+
+  if (input->IsRequired()) {
     auto entry = mRequiredRadioButtonCounts.LookupForAdd(aName);
     if (!entry) {
       entry.OrInsert([]() { return 1; });
     } else {
       ++entry.Data();
     }
   }
 }
 
 void
 HTMLFormElement::RemoveFromRadioGroup(const nsAString& aName,
                                       nsIFormControl* aRadio)
 {
   nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
-  NS_ASSERTION(element, "radio controls have to be content elements!");
+  NS_ASSERTION(element, "radio controls have to be content elements");
 
-  if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+  HTMLInputElement* input = HTMLInputElement::FromContent(element);
+  NS_ASSERTION(input, "radio controls have to be input elements!");
+
+  if (input->IsRequired()) {
     auto entry = mRequiredRadioButtonCounts.Lookup(aName);
     if (!entry) {
       MOZ_ASSERT_UNREACHABLE("At least one radio button has to be required!");
     } else {
       MOZ_ASSERT(entry.Data() >= 1,
                  "At least one radio button has to be required!");
       if (entry.Data() <= 1) {
         entry.Remove();
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1448,16 +1448,23 @@ HTMLInputElement::AfterSetAttr(int32_t a
         aName == nsGkAtoms::readonly) {
       if (aName == nsGkAtoms::disabled) {
         // This *has* to be called *before* validity state check because
         // UpdateBarredFromConstraintValidation and
         // UpdateValueMissingValidityState depend on our disabled state.
         UpdateDisabledState(aNotify);
       }
 
+      if (aName == nsGkAtoms::required && DoesRequiredApply()) {
+        // This *has* to be called *before* UpdateValueMissingValidityState
+        // because UpdateValueMissingValidityState depends on our required
+        // state.
+        UpdateRequiredState(!!aValue, aNotify);
+      }
+
       UpdateValueMissingValidityState();
 
       // This *has* to be called *after* validity has changed.
       if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
         UpdateBarredFromConstraintValidation();
       }
     } else if (aName == nsGkAtoms::maxlength) {
       UpdateTooLongValidityState();
@@ -5000,16 +5007,27 @@ HTMLInputElement::HandleTypeChange(uint8
   // previous type does, we should clear out mFocusedValue.
   if (MayFireChangeOnBlur(mType) && !MayFireChangeOnBlur(oldType)) {
     GetValue(mFocusedValue, CallerType::System);
   } else if (!IsSingleLineTextControl(false, mType) &&
              IsSingleLineTextControl(false, oldType)) {
     mFocusedValue.Truncate();
   }
 
+  // Update or clear our required states since we may have changed from a
+  // required input type to a non-required input type or viceversa.
+  if (DoesRequiredApply()) {
+    bool isRequired = HasAttr(kNameSpaceID_None, nsGkAtoms::required);
+    UpdateRequiredState(isRequired, aNotify);
+  } else if (aNotify) {
+    RemoveStates(REQUIRED_STATES);
+  } else {
+    RemoveStatesSilently(REQUIRED_STATES);
+  }
+
   UpdateHasRange();
 
   // Do not notify, it will be done after if needed.
   UpdateAllValidityStates(false);
 
   UpdateApzAwareFlag();
 
   UpdateBarredFromConstraintValidation();
@@ -6549,22 +6567,16 @@ HTMLInputElement::IntrinsicState() const
     // Check whether we are the default checked element (:default)
     if (DefaultChecked()) {
       state |= NS_EVENT_STATE_DEFAULT;
     }
   } else if (mType == NS_FORM_INPUT_IMAGE) {
     state |= nsImageLoadingContent::ImageState();
   }
 
-  if (DoesRequiredApply() && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
-    state |= NS_EVENT_STATE_REQUIRED;
-  } else {
-    state |= NS_EVENT_STATE_OPTIONAL;
-  }
-
   if (IsCandidateForConstraintValidation()) {
     if (IsValid()) {
       state |= NS_EVENT_STATE_VALID;
     } else {
       state |= NS_EVENT_STATE_INVALID;
 
       if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
           (GetValidityState(VALIDITY_STATE_CUSTOM_ERROR) ||
@@ -7227,42 +7239,45 @@ void
 HTMLInputElement::UpdateTooShortValidityState()
 {
   SetValidityState(VALIDITY_STATE_TOO_SHORT, IsTooShort());
 }
 
 void
 HTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
 {
+  MOZ_ASSERT(mType == NS_FORM_INPUT_RADIO,
+             "This should be called only for radio input types");
+
   bool notify = mDoneCreating;
   nsCOMPtr<nsIDOMHTMLInputElement> selection = GetSelectedRadioButton();
 
   aIgnoreSelf = aIgnoreSelf || !IsMutable();
 
   // If there is no selection, that might mean the radio is not in a group.
   // In that case, we can look for the checked state of the radio.
   bool selected = selection || (!aIgnoreSelf && mChecked);
-  bool required = !aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required);
+  bool required = !aIgnoreSelf && IsRequired();
   bool valueMissing = false;
 
   nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
 
   if (!container) {
     SetValidityState(VALIDITY_STATE_VALUE_MISSING,
                      IsMutable() && required && !selected);
     return;
   }
 
   nsAutoString name;
   GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
 
   // If the current radio is required and not ignored, we can assume the entire
   // group is required.
   if (!required) {
-    required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required))
+    required = (aIgnoreSelf && IsRequired())
                  ? container->GetRequiredRadioCount(name) - 1
                  : container->GetRequiredRadioCount(name);
   }
 
   valueMissing = required && !selected;
 
   if (container->GetValueMissingState(name) != valueMissing) {
     container->SetValueMissingState(name, valueMissing);
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -936,16 +936,32 @@ public:
    * it will return a Decimal for which Decimal::isFinite() will return false.
    */
   static Decimal StringToDecimal(const nsAString& aValue);
 
   void UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories);
 
   static void Shutdown();
 
+  /**
+   * Returns if the required attribute applies for the current type.
+   */
+  bool DoesRequiredApply() const;
+
+  /**
+   * Returns the current required state of the element. This function differs
+   * from Required() in that this function only returns true for input types
+   * that @required attribute applies and the attribute is set; in contrast,
+   * Required() returns true whenever @required attribute is set.
+   */
+  bool IsRequired() const
+  {
+    return State().HasState(NS_EVENT_STATE_REQUIRED);
+  }
+
 protected:
   virtual ~HTMLInputElement();
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // Pull IsSingleLineTextControl into our scope, otherwise it'd be hidden
   // by the nsITextControlElement version.
   using nsGenericHTMLFormElementWithState::IsSingleLineTextControl;
@@ -1133,21 +1149,16 @@ protected:
   bool IsMutable() const;
 
   /**
    * Returns if the readonly attribute applies for the current type.
    */
   bool DoesReadOnlyApply() const;
 
   /**
-   * Returns if the required attribute applies for the current type.
-   */
-  bool DoesRequiredApply() const;
-
-  /**
    * Returns if the min and max attributes apply for the current type.
    */
   bool DoesMinMaxApply() const;
 
   /**
    * Returns if the step attribute apply for the current type.
    */
   bool DoesStepApply() const { return DoesMinMaxApply(); }
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -1083,17 +1083,17 @@ HTMLSelectElement::IsOptionDisabled(int3
   RefPtr<HTMLOptionElement> option = Item(aIndex);
   NS_ENSURE_TRUE(option, NS_ERROR_FAILURE);
 
   *aIsDisabled = IsOptionDisabled(option);
   return NS_OK;
 }
 
 bool
-HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption)
+HTMLSelectElement::IsOptionDisabled(HTMLOptionElement* aOption) const
 {
   MOZ_ASSERT(aOption);
   if (aOption->Disabled()) {
     return true;
   }
 
   // Check for disabled optgroups
   // If there are no artifacts, there are no optgroups
@@ -1328,16 +1328,21 @@ HTMLSelectElement::AfterSetAttr(int32_t 
       // This *has* to be called *before* validity state check because
       // UpdateBarredFromConstraintValidation and
       // UpdateValueMissingValidityState depend on our disabled state.
       UpdateDisabledState(aNotify);
 
       UpdateValueMissingValidityState();
       UpdateBarredFromConstraintValidation();
     } else if (aName == nsGkAtoms::required) {
+      // This *has* to be called *before* UpdateValueMissingValidityState
+      // because UpdateValueMissingValidityState depends on our required
+      // state.
+      UpdateRequiredState(!!aValue, aNotify);
+
       UpdateValueMissingValidityState();
     } else if (aName == nsGkAtoms::autocomplete) {
       // Clear the cached @autocomplete attribute and autocompleteInfo state.
       mAutocompleteAttrState = nsContentUtils::eAutocompleteAttrState_Unknown;
       mAutocompleteInfoState = nsContentUtils::eAutocompleteAttrState_Unknown;
     } else if (aName == nsGkAtoms::multiple) {
       if (!aValue && aNotify) {
         // We might have become a combobox; make sure _something_ gets
@@ -1525,22 +1530,16 @@ HTMLSelectElement::IntrinsicState() cons
     if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
         (mCanShowValidUI && ShouldShowValidityUI() &&
          (IsValid() || (state.HasState(NS_EVENT_STATE_MOZ_UI_INVALID) &&
                         !mCanShowInvalidUI)))) {
       state |= NS_EVENT_STATE_MOZ_UI_VALID;
     }
   }
 
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
-    state |= NS_EVENT_STATE_REQUIRED;
-  } else {
-    state |= NS_EVENT_STATE_OPTIONAL;
-  }
-
   return state;
 }
 
 // nsIFormControl
 
 NS_IMETHODIMP
 HTMLSelectElement::SaveState()
 {
@@ -1776,17 +1775,17 @@ void
 HTMLSelectElement::RebuildOptionsArray(bool aNotify)
 {
   mOptions->Clear();
   AddOptions(this, mOptions);
   FindSelectedIndex(0, aNotify);
 }
 
 bool
-HTMLSelectElement::IsValueMissing()
+HTMLSelectElement::IsValueMissing() const
 {
   if (!Required()) {
     return false;
   }
 
   uint32_t length = Length();
 
   for (uint32_t i = 0; i < length; ++i) {
--- a/dom/html/HTMLSelectElement.h
+++ b/dom/html/HTMLSelectElement.h
@@ -206,17 +206,17 @@ public:
   }
   // Uses XPCOM GetName.
   void SetName(const nsAString& aName, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::name, aName, aRv);
   }
   bool Required() const
   {
-    return GetBoolAttr(nsGkAtoms::required);
+    return State().HasState(NS_EVENT_STATE_REQUIRED);
   }
   void SetRequired(bool aVal, ErrorResult& aRv)
   {
     SetHTMLBoolAttr(nsGkAtoms::required, aVal, aRv);
   }
   uint32_t Size() const
   {
     return GetUnsignedIntAttr(nsGkAtoms::size, 0);
@@ -336,17 +336,17 @@ public:
   /**
    * Checks whether an option is disabled (even if it's part of an optgroup)
    *
    * @param aIndex the index of the option to check
    * @return whether the option is disabled
    */
   NS_IMETHOD IsOptionDisabled(int32_t aIndex,
                               bool* aIsDisabled);
-  bool IsOptionDisabled(HTMLOptionElement* aOption);
+  bool IsOptionDisabled(HTMLOptionElement* aOption) const;
 
   /**
    * Sets multiple options (or just sets startIndex if select is single)
    * and handles notifications and cleanup and everything under the sun.
    * When this method exits, the select will be in a consistent state.  i.e.
    * if you set the last option to false, it will select an option anyway.
    *
    * @param aStartIndex the first index to set
@@ -516,17 +516,17 @@ protected:
    */
   nsresult RemoveOptionsFromList(nsIContent* aOptions,
                                  int32_t aListIndex,
                                  int32_t aDepth,
                                  bool aNotify);
 
   // nsIConstraintValidation
   void UpdateBarredFromConstraintValidation();
-  bool IsValueMissing();
+  bool IsValueMissing() const;
 
   /**
    * Get the index of the first option at, under or following the content in
    * the select, or length of options[] if none are found
    * @param aOptions the content
    * @return the index of the first option
    */
   int32_t GetOptionIndexAt(nsIContent* aOptions);
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -953,22 +953,16 @@ HTMLTextAreaElement::RestoreState(nsPres
   return false;
 }
 
 EventStates
 HTMLTextAreaElement::IntrinsicState() const
 {
   EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
 
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
-    state |= NS_EVENT_STATE_REQUIRED;
-  } else {
-    state |= NS_EVENT_STATE_OPTIONAL;
-  }
-
   if (IsCandidateForConstraintValidation()) {
     if (IsValid()) {
       state |= NS_EVENT_STATE_VALID;
     } else {
       state |= NS_EVENT_STATE_INVALID;
       // :-moz-ui-invalid always apply if the element suffers from a custom
       // error and never applies if novalidate is set on the form owner.
       if ((!mForm || !mForm->HasAttr(kNameSpaceID_None, nsGkAtoms::novalidate)) &&
@@ -1109,16 +1103,23 @@ HTMLTextAreaElement::AfterSetAttr(int32_
         aName == nsGkAtoms::readonly) {
       if (aName == nsGkAtoms::disabled) {
         // This *has* to be called *before* validity state check because
         // UpdateBarredFromConstraintValidation and
         // UpdateValueMissingValidityState depend on our disabled state.
         UpdateDisabledState(aNotify);
       }
 
+      if (aName == nsGkAtoms::required) {
+        // This *has* to be called *before* UpdateValueMissingValidityState
+        // because UpdateValueMissingValidityState depends on our required
+        // state.
+        UpdateRequiredState(!!aValue, aNotify);
+      }
+
       UpdateValueMissingValidityState();
 
       // This *has* to be called *after* validity has changed.
       if (aName == nsGkAtoms::readonly || aName == nsGkAtoms::disabled) {
         UpdateBarredFromConstraintValidation();
       }
     } else if (aName == nsGkAtoms::maxlength) {
       UpdateTooLongValidityState();
@@ -1213,17 +1214,17 @@ HTMLTextAreaElement::IsTooShort()
   GetTextLength(&textLength);
 
   return textLength && textLength < minLength;
 }
 
 bool
 HTMLTextAreaElement::IsValueMissing() const
 {
-  if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || !IsMutable()) {
+  if (!Required() || !IsMutable()) {
     return false;
   }
 
   return IsValueEmpty();
 }
 
 void
 HTMLTextAreaElement::UpdateTooLongValidityState()
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -247,19 +247,19 @@ public:
   bool ReadOnly()
   {
     return GetBoolAttr(nsGkAtoms::readonly);
   }
   void SetReadOnly(bool aReadOnly, ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::readonly, aReadOnly, aError);
   }
-  bool Required()
+  bool Required() const
   {
-    return GetBoolAttr(nsGkAtoms::required);
+    return State().HasState(NS_EVENT_STATE_REQUIRED);
   }
 
   void SetRangeText(const nsAString& aReplacement, ErrorResult& aRv);
 
   void SetRangeText(const nsAString& aReplacement, uint32_t aStart,
                     uint32_t aEnd, SelectionMode aSelectMode,
                     ErrorResult& aRv);
 
@@ -405,16 +405,17 @@ protected:
 
   /**
    * A helper to get the current selection range.  Will throw on the ErrorResult
    * if we have no editor state.
    */
   void GetSelectionRange(uint32_t* aSelectionStart,
                          uint32_t* aSelectionEnd,
                          ErrorResult& aRv);
+
 private:
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     GenericSpecifiedValues* aGenericData);
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/html/input/CheckableInputTypes.cpp
+++ b/dom/html/input/CheckableInputTypes.cpp
@@ -8,17 +8,17 @@
 
 #include "mozilla/dom/HTMLInputElement.h"
 
 /* input type=checkbox */
 
 bool
 CheckboxInputType::IsValueMissing() const
 {
-  if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+  if (!mInputElement->IsRequired()) {
     return false;
   }
 
   if (!IsMutable()) {
     return false;
   }
 
   return !mInputElement->Checked();
--- a/dom/html/input/DateTimeInputTypes.cpp
+++ b/dom/html/input/DateTimeInputTypes.cpp
@@ -36,17 +36,17 @@ DateTimeInputTypeBase::IsMutable() const
 {
   return !mInputElement->IsDisabled() &&
          !mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly);
 }
 
 bool
 DateTimeInputTypeBase::IsValueMissing() const
 {
-  if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+  if (!mInputElement->IsRequired()) {
     return false;
   }
 
   if (!IsMutable()) {
     return false;
   }
 
   return IsValueEmpty();
--- a/dom/html/input/FileInputType.cpp
+++ b/dom/html/input/FileInputType.cpp
@@ -6,17 +6,17 @@
 
 #include "FileInputType.h"
 
 #include "mozilla/dom/HTMLInputElement.h"
 
 bool
 FileInputType::IsValueMissing() const
 {
-  if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+  if (!mInputElement->IsRequired()) {
     return false;
   }
 
   if (!IsMutable()) {
     return false;
   }
 
   return mInputElement->GetFilesOrDirectoriesInternal().IsEmpty();
--- a/dom/html/input/NumericInputTypes.cpp
+++ b/dom/html/input/NumericInputTypes.cpp
@@ -128,17 +128,17 @@ NumericInputTypeBase::ConvertNumberToStr
   return ok;
 }
 
 /* input type=numer */
 
 bool
 NumberInputType::IsValueMissing() const
 {
-  if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+  if (!mInputElement->IsRequired()) {
     return false;
   }
 
   if (!IsMutable()) {
     return false;
   }
 
   return IsValueEmpty();
--- a/dom/html/input/SingleLineTextInputTypes.cpp
+++ b/dom/html/input/SingleLineTextInputTypes.cpp
@@ -53,17 +53,17 @@ SingleLineTextInputTypeBase::IsTooShort(
     mInputElement->InputTextLength(mozilla::dom::CallerType::System);
 
   return textLength && textLength < minLength;
 }
 
 bool
 SingleLineTextInputTypeBase::IsValueMissing() const
 {
-  if (!mInputElement->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
+  if (!mInputElement->IsRequired()) {
     return false;
   }
 
   if (!IsMutable()) {
     return false;
   }
 
   return IsValueEmpty();
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -104,16 +104,17 @@
 #include "nsGlobalWindow.h"
 #include "mozilla/dom/HTMLBodyElement.h"
 #include "imgIContainer.h"
 #include "nsComputedDOMStyle.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
 #include "ReferrerPolicy.h"
 #include "mozilla/dom/HTMLLabelElement.h"
+#include "mozilla/dom/HTMLInputElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 /**
  * nsAutoFocusEvent is used to dispatch a focus event when a
  * nsGenericHTMLFormElement is binded to the tree with the autofocus attribute
  * enabled.
@@ -2421,16 +2422,50 @@ void nsGenericHTMLFormElement::UpdateDis
   EventStates changedStates = disabledStates ^ oldDisabledStates;
 
   if (!changedStates.IsEmpty()) {
     ToggleStates(changedStates, aNotify);
   }
 }
 
 void
+nsGenericHTMLFormElement::UpdateRequiredState(bool aIsRequired, bool aNotify)
+{
+#ifdef DEBUG
+  int32_t type = ControlType();
+#endif
+  MOZ_ASSERT((type & NS_FORM_INPUT_ELEMENT) ||
+              type == NS_FORM_SELECT ||
+              type == NS_FORM_TEXTAREA,
+             "This should be called only on types that @required applies");
+
+#ifdef DEBUG
+  HTMLInputElement* input = HTMLInputElement::FromContent(this);
+  if (input) {
+    MOZ_ASSERT(input->DoesRequiredApply(),
+               "This should be called only on input types that @required applies");
+  }
+#endif
+
+  EventStates requiredStates;
+  if (aIsRequired) {
+    requiredStates |= NS_EVENT_STATE_REQUIRED;
+  } else {
+    requiredStates |= NS_EVENT_STATE_OPTIONAL;
+  }
+
+  EventStates oldRequiredStates = State() & REQUIRED_STATES;
+  EventStates changedStates = requiredStates ^ oldRequiredStates;
+
+  if (!changedStates.IsEmpty()) {
+    ToggleStates(changedStates, aNotify);
+  }
+}
+
+void
 nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify)
 {
   UpdateDisabledState(aNotify);
 }
 
 bool
 nsGenericHTMLFormElement::IsLabelable() const
 {
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -1096,16 +1096,21 @@ public:
   virtual void FieldSetDisabledChanged(bool aNotify);
 
   /**
    * Check our disabled content attribute and fieldset's (if it exists) disabled
    * state to decide whether our disabled flag should be toggled.
    */
   void UpdateDisabledState(bool aNotify);
 
+  /**
+   * Update our required/optional flags to match the given aIsRequired boolean.
+   */
+  void UpdateRequiredState(bool aIsRequired, bool aNotify);
+
   void FieldSetFirstLegendChanged(bool aNotify) {
     UpdateFieldSet(aNotify);
   }
 
   /**
    * This callback is called by a fieldset on all it's elements when it's being
    * destroyed. When called, the elements should check that aFieldset is there
    * first parent fieldset and null mFieldset in that case only.
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2433,16 +2433,19 @@ nsHTMLDocument::CreateAndAddWyciwygChann
   // Use the Parent document's loadgroup to trigger load notifications
   if (loadGroup && channel) {
     rv = channel->SetLoadGroup(loadGroup);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsLoadFlags loadFlags = 0;
     channel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
+    if (nsDocShell::SandboxFlagsImplyCookies(mSandboxFlags)) {
+      loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
+    }
     channel->SetLoadFlags(loadFlags);
 
     channel->SetOriginalURI(wcwgURI);
 
     rv = loadGroup->AddRequest(mWyciwygChannel, nullptr);
     NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to add request to load group.");
   }
 
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -291,16 +291,17 @@ public:
 
   //NSISELECTIONCONTROLLER INTERFACES
   NS_IMETHOD SetDisplaySelection(int16_t toggle) override;
   NS_IMETHOD GetDisplaySelection(int16_t* _retval) override;
   NS_IMETHOD SetSelectionFlags(int16_t aInEnable) override;
   NS_IMETHOD GetSelectionFlags(int16_t *aOutEnable) override;
   NS_IMETHOD GetSelection(RawSelectionType aRawSelectionType,
                           nsISelection** aSelection) override;
+  Selection* GetDOMSelection(RawSelectionType aRawSelectionType) override;
   NS_IMETHOD ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
                                      int16_t aRegion, int16_t aFlags) override;
   NS_IMETHOD RepaintSelection(RawSelectionType aRawSelectionType) override;
   nsresult RepaintSelection(nsPresContext* aPresContext,
                             SelectionType aSelectionType);
   NS_IMETHOD SetCaretEnabled(bool enabled) override;
   NS_IMETHOD SetCaretReadOnly(bool aReadOnly) override;
   NS_IMETHOD GetCaretEnabled(bool* _retval) override;
@@ -427,16 +428,22 @@ nsTextInputSelectionImpl::GetSelection(R
   if (!(*aSelection)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   NS_ADDREF(*aSelection);
   return NS_OK;
 }
 
+Selection*
+nsTextInputSelectionImpl::GetDOMSelection(RawSelectionType aRawSelectionType)
+{
+  return GetSelection(ToSelectionType(aRawSelectionType));
+}
+
 NS_IMETHODIMP
 nsTextInputSelectionImpl::ScrollSelectionIntoView(
                             RawSelectionType aRawSelectionType,
                             int16_t aRegion,
                             int16_t aFlags)
 {
   if (!mFrameSelection)
     return NS_ERROR_FAILURE;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -56,16 +56,17 @@
 #include "mozilla/jsipc/PJavaScript.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/CompositorManagerChild.h"
 #include "mozilla/layers/ContentProcessController.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/loader/ScriptCacheActors.h"
 #include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/CaptivePortalService.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 #include "mozilla/widget/ScreenManager.h"
 #include "mozilla/widget/WidgetMessageUtils.h"
 #include "nsBaseDragService.h"
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/BasePrincipal.h"
@@ -1942,16 +1943,26 @@ ContentChild::GetCPOWManager()
 }
 
 mozilla::ipc::IPCResult
 ContentChild::RecvPTestShellConstructor(PTestShellChild* actor)
 {
   return IPC_OK();
 }
 
+void
+ContentChild::UpdateCookieStatus(nsIChannel   *aChannel)
+{
+  RefPtr<CookieServiceChild> csChild =
+    CookieServiceChild::GetSingleton();
+  NS_ASSERTION(csChild, "Couldn't get CookieServiceChild");
+
+  csChild->TrackCookieLoad(aChannel);
+}
+
 PScriptCacheChild*
 ContentChild::AllocPScriptCacheChild(const FileDescOrError& cacheFile, const bool& wantCacheData)
 {
   return new loader::ScriptCacheChild();
 }
 
 bool
 ContentChild::DeallocPScriptCacheChild(PScriptCacheChild* cache)
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.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 mozilla_dom_ContentChild_h
 #define mozilla_dom_ContentChild_h
 
+#include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/nsIContentChild.h"
 #include "mozilla/dom/PBrowserOrId.h"
 #include "mozilla/dom/PContentChild.h"
 #include "nsAutoPtr.h"
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
@@ -139,16 +140,18 @@ public:
 #endif
 
   bool IsAlive() const;
 
   bool IsShuttingDown() const;
 
   static void AppendProcessId(nsACString& aName);
 
+  static void UpdateCookieStatus(nsIChannel *aChannel);
+
   mozilla::ipc::IPCResult
   RecvInitContentBridgeChild(Endpoint<PContentBridgeChild>&& aEndpoint) override;
 
   mozilla::ipc::IPCResult
   RecvInitGMPService(Endpoint<PGMPServiceChild>&& aGMPService) override;
 
   mozilla::ipc::IPCResult
   RecvInitProfiler(Endpoint<PProfilerChild>&& aEndpoint) override;
@@ -752,17 +755,17 @@ private:
 
   // Hashtable to keep track of the pending file creation.
   // These items are removed when RecvFileCreationResponse is received.
   nsRefPtrHashtable<nsIDHashKey, FileCreatorHelper> mFileCreationPending;
 
 
   nsClassHashtable<nsUint64HashKey, AnonymousTemporaryFileCallback> mPendingAnonymousTemporaryFiles;
 
-  bool mShuttingDown;
+  mozilla::Atomic<bool> mShuttingDown;
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
 };
 
 uint64_t
 NextWindowID();
 
 } // namespace dom
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -22,16 +22,17 @@
 #include "GeckoProfiler.h"
 #include "GMPServiceParent.h"
 #include "HandlerServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #if defined(XP_WIN) && defined(ACCESSIBILITY)
 #include "mozilla/a11y/AccessibleWrap.h"
 #endif
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/DataStorage.h"
 #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
@@ -75,16 +76,18 @@
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/loader/ScriptCacheActors.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/Move.h"
 #include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/CookieServiceParent.h"
+#include "mozilla/net/PCookieServiceParent.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/ScriptPreloader.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
@@ -103,16 +106,17 @@
 #include "nsConsoleService.h"
 #include "nsContentUtils.h"
 #include "nsDebugImpl.h"
 #include "nsFrameLoader.h"
 #include "nsFrameMessageManager.h"
 #include "nsHashPropertyBag.h"
 #include "nsIAlertsService.h"
 #include "nsIClipboard.h"
+#include "nsICookie.h"
 #include "nsContentPermissionHelper.h"
 #include "nsIContentProcess.h"
 #include "nsICycleCollectorListener.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocument.h"
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIDOMGeoPositionError.h"
 #include "nsIDragService.h"
@@ -583,16 +587,18 @@ static const char* sObserverTopics[] = {
   "last-pb-context-exited",
   "file-watcher-update",
 #ifdef ACCESSIBILITY
   "a11y-init-or-shutdown",
 #endif
   "cacheservice:empty-cache",
   "intl:app-locales-changed",
   "intl:requested-locales-changed",
+  "cookie-changed",
+  "private-cookie-changed",
 };
 
 // PreallocateProcess is called by the PreallocatedProcessManager.
 // ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
 /*static*/ already_AddRefed<ContentParent>
 ContentParent::PreallocateProcess()
 {
   RefPtr<ContentParent> process =
@@ -2784,16 +2790,51 @@ ContentParent::Observe(nsISupports* aSub
     LocaleService::GetInstance()->GetAppLocalesAsLangTags(appLocales);
     Unused << SendUpdateAppLocales(appLocales);
   }
   else if (!strcmp(aTopic, "intl:requested-locales-changed")) {
     nsTArray<nsCString> requestedLocales;
     LocaleService::GetInstance()->GetRequestedLocales(requestedLocales);
     Unused << SendUpdateRequestedLocales(requestedLocales);
   }
+  else if (!strcmp(aTopic, "cookie-changed") ||
+           !strcmp(aTopic, "private-cookie-changed")) {
+    if (!aData) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
+    if (!neckoParent) {
+      return NS_OK;
+    }
+    PCookieServiceParent *csParent = LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+    if (!csParent) {
+      return NS_OK;
+    }
+    auto *cs = static_cast<CookieServiceParent*>(csParent);
+    if (!nsCRT::strcmp(aData, u"batch-deleted")) {
+      nsCOMPtr<nsIArray> cookieList = do_QueryInterface(aSubject);
+      NS_ASSERTION(cookieList, "couldn't get cookie list");
+      cs->RemoveBatchDeletedCookies(cookieList);
+      return NS_OK;
+    }
+
+    if (!nsCRT::strcmp(aData, u"cleared")) {
+      cs->RemoveAll();
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsICookie> xpcCookie = do_QueryInterface(aSubject);
+    NS_ASSERTION(xpcCookie, "couldn't get cookie");
+    if (!nsCRT::strcmp(aData, u"deleted")) {
+      cs->RemoveCookie(xpcCookie);
+    } else if ((!nsCRT::strcmp(aData, u"added")) ||
+               (!nsCRT::strcmp(aData, u"changed"))) {
+      cs->AddCookie(xpcCookie);
+    }
+  }
   return NS_OK;
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvInitBackground(Endpoint<PBackgroundParent>&& aEndpoint)
 {
   if (!BackgroundParent::Alloc(this, Move(aEndpoint))) {
     return IPC_FAIL(this, "BackgroundParent::Alloc failed");
@@ -5040,16 +5081,27 @@ void
 ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch)
 {
   if (!mHangMonitorActor) {
     return;
   }
   ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
 }
 
+void
+ContentParent::UpdateCookieStatus(nsIChannel   *aChannel)
+{
+  PNeckoParent *neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
+  PCookieServiceParent *csParent = LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+  if (csParent) {
+    auto *cs = static_cast<CookieServiceParent*>(csParent);
+    cs->TrackCookieLoad(aChannel);
+  }
+}
+
 nsresult
 ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild(nsIChannel* aChannel)
 {
   MOZ_ASSERT(aChannel);
 
   nsresult rv;
   if (!aChannel->IsDocument()) {
     return NS_OK;
@@ -5064,16 +5116,24 @@ ContentParent::AboutToLoadHttpFtpWyciwyg
 
   nsCOMPtr<nsIPrincipal> principal;
   rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = TransmitPermissionsForPrincipal(principal);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsLoadFlags newLoadFlags;
+  aChannel->GetLoadFlags(&newLoadFlags);
+  bool isDocument = false;
+  aChannel->GetIsDocument(&isDocument);
+  if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument) {
+    UpdateCookieStatus(aChannel);
+  }
+
   return NS_OK;
 }
 
 nsresult
 ContentParent::TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal)
 {
 #ifdef MOZ_PERMISSIONS
   // Create the key, and send it down to the content process.
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -341,16 +341,18 @@ public:
   UnregisterRemoteFrame(const TabId& aTabId,
                         const ContentParentId& aCpId,
                         bool aMarkedDestroying);
 
   void ReportChildAlreadyBlocked();
 
   bool RequestRunToCompletion();
 
+  void UpdateCookieStatus(nsIChannel *aChannel);
+
   bool IsAvailable() const
   {
     return mIsAvailable;
   }
   bool IsAlive() const override;
 
   virtual bool IsForBrowser() const override
   {
--- a/dom/media/systemservices/CamerasChild.cpp
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -85,17 +85,17 @@ public:
   NS_IMETHOD Run() override {
     // Try to get the PBackground handle
     ipc::PBackgroundChild* existingBackgroundChild =
       ipc::BackgroundChild::GetForCurrentThread();
     // If it's not spun up yet, block until it is, and retry
     if (!existingBackgroundChild) {
       LOG(("No existingBackgroundChild"));
       existingBackgroundChild =
-        ipc::BackgroundChild::SynchronouslyCreateForCurrentThread();
+        ipc::BackgroundChild::GetOrCreateForCurrentThread();
       LOG(("BackgroundChild: %p", existingBackgroundChild));
       if (!existingBackgroundChild) {
         return NS_ERROR_FAILURE;
       }
     }
 
     // Create CamerasChild
     // We will be returning the resulting pointer (synchronously) to our caller.
@@ -125,17 +125,17 @@ GetCamerasChild() {
     if (NS_FAILED(rv)) {
       LOG(("Error launching IPC Thread"));
       return nullptr;
     }
 
     // At this point we are in the MediaManager thread, and the thread we are
     // dispatching to is the specific Cameras IPC thread that was just made
     // above, so now we will fire off a runnable to run
-    // BackgroundChild::SynchronouslyCreateForCurrentThread there, while we
+    // BackgroundChild::GetOrCreateForCurrentThread there, while we
     // block in this thread.
     // We block until the following happens in the Cameras IPC thread:
     // 1) Creation of PBackground finishes
     // 2) Creation of PCameras finishes by sending a message to the parent
     RefPtr<InitializeIPCThread> runnable = new InitializeIPCThread();
     RefPtr<SyncRunnable> sr = new SyncRunnable(runnable);
     sr->DispatchToThread(CamerasSingleton::Thread());
     CamerasSingleton::Child() = runnable->GetCamerasChild();
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_data_csp_merge.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1386183 - Meta CSP on data: URI iframe should be merged with toplevel CSP</title>
+  <meta charset="utf-8">
+  <meta http-equiv="Content-Security-Policy" content= "img-src https:"/>
+</head>
+<body>
+<iframe id="dataFrame" onload="doCSPMergeCheck()"
+        src="data:text/html,<html><head><meta http-equiv='Content-Security-Policy' content='script-src https:'/></head><body>merge csp</body></html>">
+</iframe>
+
+<script type="application/javascript">
+  function doCSPMergeCheck() {
+    // get the csp in JSON notation from the principal
+    var frame = document.getElementById("dataFrame");
+    var principal = SpecialPowers.wrap(frame).contentDocument.nodePrincipal;
+    var cspOBJ = JSON.parse(principal.cspJSON);
+    // make sure we got >>two<< policies
+    var policies = cspOBJ["csp-policies"];
+    window.parent.postMessage({result: policies.length}, "*");
+  }
+</script>
+
+</body>
+</html>
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -211,16 +211,17 @@ support-files =
   file_websocket_explicit.html
   file_websocket_self_wsh.py
   file_image_nonce.html
   file_image_nonce.html^headers^
   file_ignore_xfo.html
   file_ignore_xfo.html^headers^
   file_ro_ignore_xfo.html
   file_ro_ignore_xfo.html^headers^
+  file_data_csp_merge.html
 
 [test_base-uri.html]
 [test_blob_data_schemes.html]
 [test_connect-src.html]
 [test_CSP.html]
 [test_allow_https_schemes.html]
 [test_bug663567.html]
 [test_bug802872.html]
@@ -302,8 +303,9 @@ tags = mcb
 [test_upgrade_insecure_navigation.html]
 [test_punycode_host_src.html]
 [test_iframe_sandbox_srcdoc.html]
 [test_iframe_srcdoc.html]
 [test_image_nonce.html]
 [test_websocket_self.html]
 skip-if = toolkit == 'android'
 [test_ignore_xfo.html]
+[test_data_csp_merge.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/test_data_csp_merge.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1386183 - Meta CSP on data: URI iframe should be merged with toplevel CSP</title>
+  <!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe style="width:100%;" id="testframe"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* Description of the test:
+ * We load an iframe using a meta CSP which includes another iframe
+ * using a data: URI which also defines a meta CSP. We make sure the
+ * CSP from the including document gets merged with the data: URI
+ * CSP and applies to the data: URI iframe.
+ */
+
+window.addEventListener("message", receiveMessage);
+function receiveMessage(event) {
+  window.removeEventListener("message", receiveMessage);
+  // toplevel CSP + data: URI iframe meta CSP => 2 CSP policies
+  is(event.data.result, 2,
+     "CSP on data: URI iframe gets merged with CSP from including context");
+  SimpleTest.finish();
+}
+
+document.getElementById("testframe").src = "file_data_csp_merge.html";
+
+</script>
+</body>
+</html>
--- a/dom/tests/mochitest/chrome/chrome.ini
+++ b/dom/tests/mochitest/chrome/chrome.ini
@@ -11,16 +11,17 @@ support-files =
   file_MozDomFullscreen.html
   file_bug799299.xul
   file_bug800817.xul
   file_bug830858.xul
   file_bug1224790-1_modal.xul
   file_bug1224790-1_nonmodal.xul
   file_bug1224790-2_modal.xul
   file_bug1224790-2_nonmodal.xul
+  file_popup_blocker_chrome.html
   file_subscript_bindings.js
   focus_frameset.html
   focus_window2.xul
   fullscreen.xul
   queryCaretRectUnix.html
   queryCaretRectWin.html
   selectAtPoint.html
   selectAtPoint-innerframe.html
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/chrome/file_popup_blocker_chrome.html
@@ -0,0 +1,6 @@
+<html>
+<body onload="window.opener.next()">
+foobar
+</script>
+</body>
+</html>
--- a/dom/tests/mochitest/chrome/test_popup_blocker_chrome.xul
+++ b/dom/tests/mochitest/chrome/test_popup_blocker_chrome.xul
@@ -15,40 +15,52 @@ https://bugzilla.mozilla.org/show_bug.cg
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
 
   /** Test for Bug 662519 **/
 
+  let w = null;
+
+  let steps = [
+    function() {
+      w = window.open("file_popup_blocker_chrome.html", "", "width=200,height=200");
+      ok(w, "The window object shouldn't be null");    
+      // next() is called within file_popup_blocker_chrome.html
+    },
+    function() {
+      w.close();
+      ok(true, "The popup appeared");
+      next();
+    },
+    function() {
+      w = window.open("file_popup_blocker_chrome.html", "_blank", "width=200,height=200");
+      ok(w, "The window object shouldn't be null");
+      // next() is called within file_popup_blocker_chrome.html
+    },
+    function() {
+      w.close();
+      ok(true, "The popup appeared");
+      next();
+    },
+  ];
+
+  function next() {
+    if (!steps.length) {
+      SimpleTest.finish();
+      return;
+    }
+    let step = steps.shift();
+    step();
+  }
+
   SimpleTest.waitForExplicitFinish();
 
   // We have to enable dom.disable_open_during_load which is disabled
   // by the test harness.
-  let prefs = Components.classes["@mozilla.org/preferences-service;1"]
-                .getService(Components.interfaces.nsIPrefBranch);
-  var gLastDomLoadValue = prefs.getBoolPref("dom.disable_open_during_load");
-  prefs.setBoolPref("dom.disable_open_during_load", true);
-
-  let w = window.open("data:text/html,foobar", "", "width=200,height=200");
-  ok(w, "The window object shouldn't be null");
-
-  SimpleTest.waitForFocus(function() {
-    w.close();
-    ok(true, "The popup appeared");
-
-    SimpleTest.waitForFocus(function() {
-      let w = window.open("data:text/html,foobar", "", "width=200,height=200");
-      ok(w, "The window object shouldn't be null");
-
-      SimpleTest.waitForFocus(function() {
-        w.close();
-
-        ok(true, "The popup appeared");
-        prefs.setBoolPref("dom.disable_open_during_load", gLastDomLoadValue);
-        SimpleTest.finish();
-      }, w, false);
-    });
-  }, w, false);
+  SpecialPowers.pushPrefEnv({'set': [["dom.disable_open_during_load", true]] }, function() {
+    next();
+  });
   ]]>
   </script>
 </window>
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -2844,20 +2844,20 @@ WorkerThreadPrimaryRunnable::Run()
 
   nsAutoCString threadName;
   threadName.AssignLiteral("DOM Worker '");
   threadName.Append(NS_LossyConvertUTF16toASCII(mWorkerPrivate->ScriptURL()));
   threadName.Append('\'');
 
   profiler_register_thread(threadName.get(), &stackBaseGuess);
 
-  // Note: SynchronouslyCreateForCurrentThread() must be called prior to
+  // Note: GetOrCreateForCurrentThread() must be called prior to
   //       mWorkerPrivate->SetThread() in order to avoid accidentally consuming
   //       worker messages here.
-  if (NS_WARN_IF(!BackgroundChild::SynchronouslyCreateForCurrentThread())) {
+  if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread())) {
     // XXX need to fire an error at parent.
     // Failed in creating BackgroundChild: probably in shutdown. Continue to run
     // without BackgroundChild created.
   }
 
   class MOZ_STACK_CLASS SetThreadHelper final
   {
     // Raw pointer: this class is on the stack.
--- a/dom/workers/WorkerThread.cpp
+++ b/dom/workers/WorkerThread.cpp
@@ -322,17 +322,17 @@ WorkerThread::Observer::OnDispatchedEven
 NS_IMETHODIMP
 WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
                                            bool aMayWait)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   // If the PBackground child is not created yet, then we must permit
   // blocking event processing to support
-  // BackgroundChild::SynchronouslyCreateForCurrentThread(). If this occurs
+  // BackgroundChild::GetOrCreateCreateForCurrentThread(). If this occurs
   // then we are spinning on the event queue at the start of
   // PrimaryWorkerRunnable::Run() and don't want to process the event in
   // mWorkerPrivate yet.
   if (aMayWait) {
     MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth() == 2);
     MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
     return NS_OK;
   }
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -18,17 +18,16 @@
 #include "DeleteRangeTransaction.h"     // for DeleteRangeTransaction
 #include "DeleteTextTransaction.h"      // for DeleteTextTransaction
 #include "EditAggregateTransaction.h"   // for EditAggregateTransaction
 #include "EditorEventListener.h"        // for EditorEventListener
 #include "InsertNodeTransaction.h"      // for InsertNodeTransaction
 #include "InsertTextTransaction.h"      // for InsertTextTransaction
 #include "JoinNodeTransaction.h"        // for JoinNodeTransaction
 #include "PlaceholderTransaction.h"     // for PlaceholderTransaction
-#include "SetTextTransaction.h"         // for SetTextTransaction
 #include "SplitNodeTransaction.h"       // for SplitNodeTransaction
 #include "StyleSheetTransactions.h"     // for AddStyleSheetTransaction, etc.
 #include "TextEditUtils.h"              // for TextEditUtils
 #include "mozInlineSpellChecker.h"      // for mozInlineSpellChecker
 #include "mozilla/CheckedInt.h"         // for CheckedInt
 #include "mozilla/EditorUtils.h"        // for AutoRules, etc.
 #include "mozilla/EditTransactionBase.h" // for EditTransactionBase
 #include "mozilla/FlushType.h"          // for FlushType::Frames
@@ -161,16 +160,18 @@ EditorBase::~EditorBase()
   HideCaret(false);
   mTxnMgr = nullptr;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(EditorBase)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionController)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTxnMgr)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMETextNode)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventListener)
@@ -182,16 +183,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EditorBase)
  nsIDocument* currentDoc =
    tmp->mRootElement ? tmp->mRootElement->GetUncomposedDoc() : nullptr;
  if (currentDoc &&
      nsCCUncollectableMarker::InGeneration(cb, currentDoc->GetMarkedCCGeneration())) {
    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
  }
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionController)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTxnMgr)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMETextNode)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorObservers)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener)
@@ -220,33 +223,32 @@ EditorBase::Init(nsIDOMDocument* aDOMDoc
   MOZ_ASSERT(mAction == EditAction::none,
              "Initializing during an edit action is an error");
   MOZ_ASSERT(aDOMDocument);
   if (!aDOMDocument) {
     return NS_ERROR_NULL_POINTER;
   }
 
   // First only set flags, but other stuff shouldn't be initialized now.
-  // Don't move this call after initializing mDocumentWeak.
+  // Don't move this call after initializing mDocument.
   // SetFlags() can check whether it's called during initialization or not by
   // them.  Note that SetFlags() will be called by PostCreate().
 #ifdef DEBUG
   nsresult rv =
 #endif
   SetFlags(aFlags);
   NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
 
-  nsCOMPtr<nsIDocument> document = do_QueryInterface(aDOMDocument);
-  mDocumentWeak = document.get();
+  mDocument = do_QueryInterface(aDOMDocument);
   // HTML editors currently don't have their own selection controller,
   // so they'll pass null as aSelCon, and we'll get the selection controller
   // off of the presshell.
   nsCOMPtr<nsISelectionController> selectionController;
   if (aSelectionController) {
-    mSelectionControllerWeak = aSelectionController;
+    mSelectionController = aSelectionController;
     selectionController = aSelectionController;
   } else {
     nsCOMPtr<nsIPresShell> presShell = GetPresShell();
     selectionController = do_QueryInterface(presShell);
   }
   MOZ_ASSERT(selectionController,
              "Selection controller should be available at this point");
 
@@ -560,24 +562,24 @@ EditorBase::GetIsDocumentEditable(bool* 
   *aIsDocumentEditable = doc && IsModifiable();
 
   return NS_OK;
 }
 
 already_AddRefed<nsIDocument>
 EditorBase::GetDocument()
 {
-  nsCOMPtr<nsIDocument> document = mDocumentWeak.get();
+  nsCOMPtr<nsIDocument> document = mDocument;
   return document.forget();
 }
 
 already_AddRefed<nsIDOMDocument>
 EditorBase::GetDOMDocument()
 {
-  nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocumentWeak);
+  nsCOMPtr<nsIDOMDocument> domDocument = do_QueryInterface(mDocument);
   return domDocument.forget();
 }
 
 NS_IMETHODIMP
 EditorBase::GetDocument(nsIDOMDocument** aDoc)
 {
   *aDoc = GetDOMDocument().take();
   return *aDoc ? NS_OK : NS_ERROR_NOT_INITIALIZED;
@@ -629,29 +631,16 @@ EditorBase::GetSelectionController(nsISe
   nsCOMPtr<nsISelectionController> selCon = GetSelectionController();
   if (NS_WARN_IF(!selCon)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   selCon.forget(aSel);
   return NS_OK;
 }
 
-already_AddRefed<nsISelectionController>
-EditorBase::GetSelectionController()
-{
-  nsCOMPtr<nsISelectionController> selectionController;
-  if (mSelectionControllerWeak) {
-    selectionController = mSelectionControllerWeak.get();
-  } else {
-    nsCOMPtr<nsIPresShell> presShell = GetPresShell();
-    selectionController = do_QueryInterface(presShell);
-  }
-  return selectionController.forget();
-}
-
 NS_IMETHODIMP
 EditorBase::DeleteSelection(EDirection aAction,
                             EStripWrappers aStripWrappers)
 {
   MOZ_ASSERT(aStripWrappers == eStrip || aStripWrappers == eNoStrip);
   return DeleteSelectionImpl(aAction, aStripWrappers);
 }
 
@@ -662,35 +651,23 @@ EditorBase::GetSelection(nsISelection** 
 }
 
 nsresult
 EditorBase::GetSelection(SelectionType aSelectionType,
                          nsISelection** aSelection)
 {
   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
   *aSelection = nullptr;
-  nsCOMPtr<nsISelectionController> selcon = GetSelectionController();
+  nsISelectionController* selcon = GetSelectionController();
   if (!selcon) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   return selcon->GetSelection(ToRawSelectionType(aSelectionType), aSelection);
 }
 
-Selection*
-EditorBase::GetSelection(SelectionType aSelectionType)
-{
-  nsCOMPtr<nsISelection> sel;
-  nsresult rv = GetSelection(aSelectionType, getter_AddRefs(sel));
-  if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!sel)) {
-    return nullptr;
-  }
-
-  return sel->AsSelection();
-}
-
 NS_IMETHODIMP
 EditorBase::DoTransaction(nsITransaction* aTxn)
 {
   return DoTransaction(nullptr, aTxn);
 }
 
 nsresult
 EditorBase::DoTransaction(Selection* aSelection, nsITransaction* aTxn)
@@ -2373,18 +2350,17 @@ EditorBase::CloneAttributes(Element* aDe
                                true);
     }
   }
 }
 
 nsresult
 EditorBase::ScrollSelectionIntoView(bool aScrollToAnchor)
 {
-  nsCOMPtr<nsISelectionController> selectionController =
-    GetSelectionController();
+  nsISelectionController* selectionController = GetSelectionController();
   if (!selectionController) {
     return NS_OK;
   }
 
   int16_t region = nsISelectionController::SELECTION_FOCUS_REGION;
   if (aScrollToAnchor) {
     region = nsISelectionController::SELECTION_ANCHOR_REGION;
   }
@@ -2717,19 +2693,17 @@ EditorBase::NotifyDocumentListeners(
 
   return rv;
 }
 
 nsresult
 EditorBase::SetTextImpl(Selection& aSelection, const nsAString& aString,
                         Text& aCharData)
 {
-  SetTextTransaction transaction(aCharData, aString, *this, &mRangeUpdater);
-
-  uint32_t length = aCharData.Length();
+  const uint32_t length = aCharData.Length();
 
   AutoRules beginRulesSniffing(this, EditAction::setText,
                                nsIEditor::eNext);
 
   // Let listeners know what's up
   {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
@@ -2744,17 +2718,30 @@ EditorBase::SetTextImpl(Selection& aSele
           aString);
       }
     }
   }
 
   // We don't support undo here, so we don't really need all of the transaction
   // machinery, therefore we can run our transaction directly, breaking all of
   // the rules!
-  nsresult rv = transaction.DoTransaction();
+  nsresult rv = aCharData.SetData(aString);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Only set selection to insertion point if editor gives permission
+  if (GetShouldTxnSetSelection()) {
+    RefPtr<Selection> selection = GetSelection();
+    DebugOnly<nsresult> rv = selection->Collapse(&aCharData, length);
+    NS_ASSERTION(NS_SUCCEEDED(rv),
+                 "Selection could not be collapsed after insert");
+  }
+  mRangeUpdater.SelAdjDeleteText(&aCharData, 0, length);
+  mRangeUpdater.SelAdjInsertText(aCharData, 0, aString);
 
   // Let listeners know what happened
   {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       if (length) {
         listener->DidDeleteText(
           static_cast<nsIDOMCharacterData*>(aCharData.AsDOMNode()), 0,
@@ -5302,18 +5289,17 @@ EditorBase::GetIsInEditAction(bool* aIsI
   return NS_OK;
 }
 
 int32_t
 EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode)
 {
   MOZ_ASSERT(aTextNode, "aTextNode must not be nullptr");
 
-  nsCOMPtr<nsISelectionController> selectionController =
-    GetSelectionController();
+  nsISelectionController* selectionController = GetSelectionController();
   if (NS_WARN_IF(!selectionController)) {
     return -1;
   }
 
   uint32_t minOffset = UINT32_MAX;
   static const SelectionType kIMESelectionTypes[] = {
     SelectionType::eIMERawClause,
     SelectionType::eIMESelectedRawClause,
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -4,19 +4,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_EditorBase_h
 #define mozilla_EditorBase_h
 
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc.
 #include "mozilla/Maybe.h"              // for Maybe
 #include "mozilla/OwningNonNull.h"      // for OwningNonNull
+#include "mozilla/PresShell.h"          // for PresShell
 #include "mozilla/SelectionState.h"     // for RangeUpdater, etc.
 #include "mozilla/StyleSheet.h"         // for StyleSheet
 #include "mozilla/WeakPtr.h"            // for WeakPtr
+#include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Text.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed, nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"                // for nsIDocument
 #include "nsIEditor.h"                  // for nsIEditor, etc.
 #include "nsIObserver.h"                // for NS_DECL_NSIOBSERVER, etc.
 #include "nsIPlaintextEditor.h"         // for nsIPlaintextEditor, etc.
@@ -114,27 +116,25 @@ class EditAggregateTransaction;
 class EditTransactionBase;
 class ErrorResult;
 class HTMLEditor;
 class InsertNodeTransaction;
 class InsertTextTransaction;
 class JoinNodeTransaction;
 class PlaceholderTransaction;
 class RemoveStyleSheetTransaction;
-class SetTextTransaction;
 class SplitNodeTransaction;
 class TextComposition;
 class TextEditor;
 struct EditorDOMPoint;
 
 namespace dom {
 class DataTransfer;
 class Element;
 class EventTarget;
-class Selection;
 class Text;
 } // namespace dom
 
 namespace widget {
 struct IMEState;
 } // namespace widget
 
 /**
@@ -247,17 +247,17 @@ protected:
    * for someone to derive from the EditorBase later? I don't believe so.
    */
   virtual ~EditorBase();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor)
 
-  bool IsInitialized() const { return !!mDocumentWeak; }
+  bool IsInitialized() const { return !!mDocument; }
   already_AddRefed<nsIDOMDocument> GetDOMDocument();
   already_AddRefed<nsIDocument> GetDocument();
   already_AddRefed<nsIPresShell> GetPresShell();
   already_AddRefed<nsIWidget> GetWidget();
   enum NotificationForEditorObservers
   {
     eNotifyEditorObserversOfEnd,
     eNotifyEditorObserversOfBefore,
@@ -562,17 +562,31 @@ protected:
    * the event comes to the editor.
    *
    * @return            true if there is a composition.  Otherwise, for example,
    *                    a composition event handler in web contents moved focus
    *                    for committing the composition, returns false.
    */
   bool EnsureComposition(WidgetCompositionEvent* aCompositionEvent);
 
-  already_AddRefed<nsISelectionController> GetSelectionController();
+  nsISelectionController* GetSelectionController() const
+  {
+    if (mSelectionController) {
+      return mSelectionController;
+    }
+    if (!mDocument) {
+      return nullptr;
+    }
+    nsIPresShell* presShell = mDocument->GetShell();
+    if (!presShell) {
+      return nullptr;
+    }
+    nsISelectionController* sc = static_cast<PresShell*>(presShell);
+    return sc;
+  }
   nsresult GetSelection(SelectionType aSelectionType,
                         nsISelection** aSelection);
 
 public:
   /**
    * All editor operations which alter the doc should be prefaced
    * with a call to StartOperation, naming the action and direction.
    */
@@ -815,17 +829,25 @@ public:
                                       int32_t* outEndOffset);
   static nsresult GetEndNodeAndOffset(Selection* aSelection,
                                       nsINode** aEndContainer,
                                       int32_t* aEndOffset);
 #if DEBUG_JOE
   static void DumpNode(nsIDOMNode* aNode, int32_t indent = 0);
 #endif
   Selection* GetSelection(SelectionType aSelectionType =
-                                          SelectionType::eNormal);
+                                          SelectionType::eNormal)
+  {
+    nsISelectionController* sc = GetSelectionController();
+    if (!sc) {
+      return nullptr;
+    }
+    Selection* selection = sc->GetDOMSelection(ToRawSelectionType(aSelectionType));
+    return selection;
+  }
 
   /**
    * Helpers to add a node to the selection.
    * Used by table cell selection methods.
    */
   nsresult CreateRange(nsIDOMNode* aStartContainer, int32_t aStartOffset,
                        nsIDOMNode* aEndContainer, int32_t aEndOffset,
                        nsRange** aRange);
@@ -975,17 +997,17 @@ public:
   bool IsTabbable() const
   {
     return IsSingleLineEditor() || IsPasswordEditor() || IsFormWidget() ||
            IsInteractionAllowed();
   }
 
   bool HasIndependentSelection() const
   {
-    return !!mSelectionControllerWeak;
+    return !!mSelectionController;
   }
 
   bool IsModifiable() const
   {
     return !IsReadonly();
   }
 
   /**
@@ -1100,22 +1122,18 @@ public:
   /**
    * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent
    * with nsCaret::RemoveForceHide().  This does NOT set visibility of
    * nsCaret.  Therefore, this is stateless.
    */
   void HideCaret(bool aHide);
 
 private:
-  // Weak reference to the nsISelectionController.
-  // Use GetSelectionController() to retrieve actual pointer.
-  CachedWeakPtr<nsISelectionController> mSelectionControllerWeak;
-  // Weak reference to the nsIDocument.
-  // Use GetDocument() to retrieve actual pointer.
-  CachedWeakPtr<nsIDocument> mDocumentWeak;
+  nsCOMPtr<nsISelectionController> mSelectionController;
+  nsCOMPtr<nsIDocument> mDocument;
 
 protected:
   enum Tristate
   {
     eTriUnset,
     eTriFalse,
     eTriTrue
   };
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -4267,18 +4267,17 @@ HTMLEditor::IsVisTextNode(nsIContent* aN
   MOZ_ASSERT(aNode);
   MOZ_ASSERT(aNode->NodeType() == nsIDOMNode::TEXT_NODE);
   MOZ_ASSERT(outIsEmptyNode);
 
   *outIsEmptyNode = true;
 
   uint32_t length = aNode->TextLength();
   if (aSafeToAskFrames) {
-    nsCOMPtr<nsISelectionController> selectionController =
-      GetSelectionController();
+    nsISelectionController* selectionController = GetSelectionController();
     if (NS_WARN_IF(!selectionController)) {
       return NS_ERROR_FAILURE;
     }
     bool isVisible = false;
     // ask the selection controller for information about whether any
     // of the data in the node is really rendered.  This is really
     // something that frames know about, but we aren't supposed to talk to frames.
     // So we put a call in the selection controller interface, since it's already
deleted file mode 100644
--- a/editor/libeditor/SetTextTransaction.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 sw=2 et tw=78: */
-/* 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 "SetTextTransaction.h"
-
-#include "mozilla/DebugOnly.h"          // DebugOnly
-#include "mozilla/EditorBase.h"         // mEditorBase
-#include "mozilla/SelectionState.h"     // RangeUpdater
-#include "mozilla/dom/Selection.h"      // Selection local var
-#include "mozilla/dom/Text.h"           // mTextNode
-#include "nsAString.h"                  // nsAString parameter
-#include "nsDebug.h"                    // for NS_ASSERTION, etc.
-#include "nsError.h"                    // for NS_OK, etc.
-
-namespace mozilla {
-
-using namespace dom;
-
-SetTextTransaction::SetTextTransaction(Text& aTextNode,
-                                       const nsAString& aStringToSet,
-                                       EditorBase& aEditorBase,
-                                       RangeUpdater* aRangeUpdater)
-  : mTextNode(&aTextNode)
-  , mStringToSet(aStringToSet)
-  , mEditorBase(&aEditorBase)
-  , mRangeUpdater(aRangeUpdater)
-{
-}
-
-nsresult
-SetTextTransaction::DoTransaction()
-{
-  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTextNode)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsresult rv = mTextNode->GetData(mPreviousData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = mTextNode->SetData(mStringToSet);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  // Only set selection to insertion point if editor gives permission
-  if (mEditorBase->GetShouldTxnSetSelection()) {
-    RefPtr<Selection> selection = mEditorBase->GetSelection();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return NS_ERROR_NULL_POINTER;
-    }
-    DebugOnly<nsresult> rv =
-      selection->Collapse(mTextNode, mStringToSet.Length());
-    NS_ASSERTION(NS_SUCCEEDED(rv),
-                 "Selection could not be collapsed after insert");
-  }
-  mRangeUpdater->SelAdjDeleteText(mTextNode, 0, mPreviousData.Length());
-  mRangeUpdater->SelAdjInsertText(*mTextNode, 0, mStringToSet);
-
-  return NS_OK;
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/editor/libeditor/SetTextTransaction.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 sw=2 et tw=78: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_SetTextTransaction_h
-#define mozilla_SetTextTransaction_h
-
-#include "mozilla/Attributes.h"         // for MOZ_STACK_CLASS
-#include "nsString.h"                   // nsString members
-#include "nscore.h"                     // NS_IMETHOD, nsAString
-
-namespace mozilla {
-
-class EditorBase;
-class RangeUpdater;
-
-namespace dom {
-class Text;
-} // namespace dom
-
-/**
- * A fake transaction that inserts text into a content node.
- *
- * This class mimics a transaction class but it is not intended to be used as one.
- */
-class MOZ_STACK_CLASS SetTextTransaction final
-{
-public:
-  /**
-   * @param aTextNode       The text content node.
-   * @param aString         The new text to insert.
-   * @param aEditorBase     Used to get and set the selection.
-   * @param aRangeUpdater   The range updater
-   */
-  SetTextTransaction(dom::Text& aTextNode,
-                     const nsAString& aString, EditorBase& aEditorBase,
-                     RangeUpdater* aRangeUpdater);
-
-  nsresult DoTransaction();
-
-private:
-  // The Text node to operate upon.
-  RefPtr<dom::Text> mTextNode;
-
-  // The text to insert into mTextNode at mOffset.
-  nsString mStringToSet;
-
-  // The previous text for undo
-  nsString mPreviousData;
-
-  // The editor, which we'll need to get the selection.
-  EditorBase* MOZ_NON_OWNING_REF mEditorBase;
-
-  RangeUpdater* mRangeUpdater;
-};
-
-} // namespace mozilla
-
-#endif // #ifndef mozilla_SetTextTransaction_h
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -835,17 +835,17 @@ TextEditRules::WillSetText(Selection& aS
 
   if (NS_WARN_IF(!mTextEditor)) {
     return NS_ERROR_FAILURE;
   }
   RefPtr<TextEditor> textEditor = mTextEditor;
 
   if (!IsPlaintextEditor() || textEditor->IsIMEComposing() ||
       aMaxLength != -1) {
-    // SetTextTransaction only supports plain text editor without IME.
+    // SetTextImpl only supports plain text editor without IME.
     return NS_OK;
   }
 
   if (IsPasswordEditor() && LookAndFeel::GetEchoPassword() &&
       !DontEchoPassword()) {
     // Echo password timer will implement on InsertText.
     return NS_OK;
   }
--- a/editor/libeditor/moz.build
+++ b/editor/libeditor/moz.build
@@ -59,17 +59,16 @@ UNIFIED_SOURCES += [
     'HTMLTableEditor.cpp',
     'HTMLURIRefObject.cpp',
     'InsertNodeTransaction.cpp',
     'InsertTextTransaction.cpp',
     'InternetCiter.cpp',
     'JoinNodeTransaction.cpp',
     'PlaceholderTransaction.cpp',
     'SelectionState.cpp',
-    'SetTextTransaction.cpp',
     'SplitNodeTransaction.cpp',
     'StyleSheetTransactions.cpp',
     'TextEditor.cpp',
     'TextEditorDataTransfer.cpp',
     'TextEditorTest.cpp',
     'TextEditRules.cpp',
     'TextEditRulesBidi.cpp',
     'TextEditUtils.cpp',
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -85,17 +85,17 @@ subsuite = clipboard
 [test_bug552782.html]
 [test_bug567213.html]
 [test_bug569988.html]
 skip-if = os == 'android'
 [test_bug570144.html]
 [test_bug578771.html]
 skip-if = android_version == '18' # bug 1147989
 [test_bug586662.html]
-skip-if = toolkit == 'android'
+skip-if = true # bug 1376382
 [test_bug587461.html]
 [test_bug590554.html]
 [test_bug592592.html]
 [test_bug596001.html]
 subsuite = clipboard
 [test_bug596333.html]
 skip-if = toolkit == 'android'
 [test_bug596506.html]
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -108,50 +108,76 @@ PaintThread::ShutdownOnPaintThread()
 PaintThread::IsOnPaintThread()
 {
   return sThreadId == PlatformThread::CurrentId();
 }
 
 void
 PaintThread::PaintContentsAsync(CompositorBridgeChild* aBridge,
                                 gfx::DrawTargetCapture* aCapture,
-                                gfx::DrawTarget* aTarget)
+                                CapturedPaintState* aState,
+                                PrepDrawTargetForPaintingCallback aCallback)
 {
   MOZ_ASSERT(IsOnPaintThread());
+  MOZ_ASSERT(aCapture);
+  MOZ_ASSERT(aState);
+
+  DrawTarget* target = aState->mTarget;
+
+  Matrix oldTransform = target->GetTransform();
+  target->SetTransform(aState->mTargetTransform);
+
+  if (!aCallback(aState)) {
+    return;
+  }
 
   // Draw all the things into the actual dest target.
-  aTarget->DrawCapturedDT(aCapture, Matrix());
+  target->DrawCapturedDT(aCapture, Matrix());
+  target->SetTransform(oldTransform);
+
+  // Textureclient forces a flush once we "end paint", so
+  // users of this texture expect all the drawing to be complete.
+  // Force a flush now.
+  // TODO: This might be a performance bottleneck because
+  // main thread painting only does one flush at the end of all paints
+  // whereas we force a flush after each draw target paint.
+  target->Flush();
 
   if (aBridge) {
     aBridge->NotifyFinishedAsyncPaint();
   }
 }
 
 void
 PaintThread::PaintContents(DrawTargetCapture* aCapture,
-                           DrawTarget* aTarget)
+                           CapturedPaintState* aState,
+                           PrepDrawTargetForPaintingCallback aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aCapture);
+  MOZ_ASSERT(aState);
 
   // If painting asynchronously, we need to acquire the compositor bridge which
   // owns the underlying MessageChannel. Otherwise we leave it null and use
   // synchronous dispatch.
   RefPtr<CompositorBridgeChild> cbc;
   if (!gfxPrefs::LayersOMTPForceSync()) {
     cbc = CompositorBridgeChild::Get();
     cbc->NotifyBeginAsyncPaint();
   }
   RefPtr<DrawTargetCapture> capture(aCapture);
-  RefPtr<DrawTarget> target(aTarget);
+  RefPtr<CapturedPaintState> state(aState);
 
   RefPtr<PaintThread> self = this;
   RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::PaintContents",
-    [self, cbc, capture, target]() -> void
+    [self, cbc, capture, state, aCallback]() -> void
   {
-    self->PaintContentsAsync(cbc, capture, target);
+    self->PaintContentsAsync(cbc, capture,
+                             state,
+                             aCallback);
   });
 
   if (cbc) {
     sThread->Dispatch(task.forget());
   } else {
     SyncRunnable::DispatchToThread(sThread, task);
   }
 }
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -3,40 +3,74 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_LAYERS_PAINTTHREAD_H
 #define MOZILLA_LAYERS_PAINTTHREAD_H
 
 #include "base/platform_thread.h"
+#include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace gfx {
 class DrawTarget;
 class DrawTargetCapture;
 };
 
 namespace layers {
 
+// Holds the key parts from a RotatedBuffer::PaintState
+// required to draw the captured paint state
+class CapturedPaintState {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CapturedPaintState)
+public:
+  CapturedPaintState(nsIntRegion& aRegionToDraw,
+                     gfx::DrawTarget* aTarget,
+                     gfx::DrawTarget* aTargetOnWhite,
+                     gfx::Matrix aTargetTransform,
+                     SurfaceMode aSurfaceMode,
+                     gfxContentType aContentType)
+  : mRegionToDraw(aRegionToDraw)
+  , mTarget(aTarget)
+  , mTargetOnWhite(aTargetOnWhite)
+  , mTargetTransform(aTargetTransform)
+  , mSurfaceMode(aSurfaceMode)
+  , mContentType(aContentType)
+  {}
+
+  nsIntRegion mRegionToDraw;
+  RefPtr<gfx::DrawTarget> mTarget;
+  RefPtr<gfx::DrawTarget> mTargetOnWhite;
+  gfx::Matrix mTargetTransform;
+  SurfaceMode mSurfaceMode;
+  gfxContentType mContentType;
+
+protected:
+  virtual ~CapturedPaintState() {}
+};
+
+typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState* aPaintState);
+
 class CompositorBridgeChild;
 
 class PaintThread final
 {
   friend void DestroyPaintThread(UniquePtr<PaintThread>&& aPaintThread);
 
 public:
   static void Start();
   static void Shutdown();
   static PaintThread* Get();
   void PaintContents(gfx::DrawTargetCapture* aCapture,
-                     gfx::DrawTarget* aTarget);
+                     CapturedPaintState* aState,
+                     PrepDrawTargetForPaintingCallback aCallback);
 
   // Sync Runnables need threads to be ref counted,
   // But this thread lives through the whole process.
   // We're only temporarily using sync runnables so
   // Override release/addref but don't do anything.
   void Release();
   void AddRef();
 
@@ -44,17 +78,18 @@ public:
   static bool IsOnPaintThread();
 
 private:
   bool Init();
   void ShutdownOnPaintThread();
   void InitOnPaintThread();
   void PaintContentsAsync(CompositorBridgeChild* aBridge,
                           gfx::DrawTargetCapture* aCapture,
-                          gfx::DrawTarget* aTarget);
+                          CapturedPaintState* aState,
+                          PrepDrawTargetForPaintingCallback aCallback);
 
   static StaticAutoPtr<PaintThread> sSingleton;
   static StaticRefPtr<nsIThread> sThread;
   static PlatformThreadId sThreadId;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/gfx/Point.h"          // for Point, IntPoint
 #include "mozilla/gfx/Rect.h"           // for Rect, IntRect
 #include "mozilla/gfx/Types.h"          // for ExtendMode::ExtendMode::CLAMP, etc
 #include "mozilla/layers/ShadowLayers.h"  // for ShadowableLayer
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "gfx2DGlue.h"
 #include "nsLayoutUtils.h"              // for invalidation debugging
+#include "PaintThread.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 IntRect
@@ -726,64 +727,116 @@ RotatedContentBuffer::BeginPaint(Painted
   result.mRegionToInvalidate.Or(result.mRegionToInvalidate, invalidate);
   result.mClip = DrawRegionClip::DRAW;
   result.mMode = mode;
 
   return result;
 }
 
 DrawTarget*
+RotatedContentBuffer::BorrowDrawTargetForRecording(PaintState& aPaintState,
+                                                   DrawIterator* aIter /* = nullptr */)
+{
+  if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
+    return nullptr;
+  }
+
+  DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
+                                                         BUFFER_BOTH, aIter);
+  if (!result) {
+    return nullptr;
+  }
+
+  ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
+  return result;
+}
+
+/*static */ bool
+RotatedContentBuffer::PrepareDrawTargetForPainting(CapturedPaintState* aState)
+{
+  RefPtr<DrawTarget> target = aState->mTarget;
+  RefPtr<DrawTarget> whiteTarget = aState->mTargetOnWhite;
+
+  if (aState->mSurfaceMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
+    if (!target || !target->IsValid() ||
+        !aState->mTargetOnWhite || !aState->mTargetOnWhite->IsValid()) {
+      // This can happen in release builds if allocating one of the two buffers
+      // failed. This in turn can happen if unreasonably large textures are
+      // requested.
+      return false;
+    }
+    for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
+      const IntRect& rect = iter.Get();
+      target->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
+                            ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
+      whiteTarget->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
+                                 ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
+    }
+  } else if (aState->mContentType == gfxContentType::COLOR_ALPHA &&
+             target->IsValid()) {
+    // HaveBuffer() => we have an existing buffer that we must clear
+    for (auto iter = aState->mRegionToDraw.RectIter(); !iter.Done(); iter.Next()) {
+      const IntRect& rect = iter.Get();
+      target->ClearRect(Rect(rect.x, rect.y, rect.width, rect.height));
+    }
+  }
+
+  return true;
+}
+
+void
+RotatedContentBuffer::ExpandDrawRegion(PaintState& aPaintState,
+                                       DrawIterator* aIter,
+                                       BackendType aBackendType)
+{
+  nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
+  if (aIter) {
+    // The iterators draw region currently only contains the bounds of the region,
+    // this makes it the precise region.
+    aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
+    drawPtr = &aIter->mDrawRegion;
+  }
+  if (aBackendType == BackendType::DIRECT2D ||
+      aBackendType == BackendType::DIRECT2D1_1) {
+    // Simplify the draw region to avoid hitting expensive drawing paths
+    // for complex regions.
+    drawPtr->SimplifyOutwardByArea(100 * 100);
+  }
+}
+
+DrawTarget*
 RotatedContentBuffer::BorrowDrawTargetForPainting(PaintState& aPaintState,
                                                   DrawIterator* aIter /* = nullptr */)
 {
   if (aPaintState.mMode == SurfaceMode::SURFACE_NONE) {
     return nullptr;
   }
 
   DrawTarget* result = BorrowDrawTargetForQuadrantUpdate(aPaintState.mRegionToDraw.GetBounds(),
                                                          BUFFER_BOTH, aIter);
   if (!result) {
     return nullptr;
   }
 
-  nsIntRegion* drawPtr = &aPaintState.mRegionToDraw;
-  if (aIter) {
-    // The iterators draw region currently only contains the bounds of the region,
-    // this makes it the precise region.
-    aIter->mDrawRegion.And(aIter->mDrawRegion, aPaintState.mRegionToDraw);
-    drawPtr = &aIter->mDrawRegion;
-  }
-  if (result->GetBackendType() == BackendType::DIRECT2D ||
-      result->GetBackendType() == BackendType::DIRECT2D1_1) {
-    // Simplify the draw region to avoid hitting expensive drawing paths
-    // for complex regions.
-    drawPtr->SimplifyOutwardByArea(100 * 100);
-  }
+  ExpandDrawRegion(aPaintState, aIter, result->GetBackendType());
+
+  nsIntRegion regionToDraw = aIter ? aIter->mDrawRegion
+                                   : aPaintState.mRegionToDraw;
 
-  if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-    if (!mDTBuffer || !mDTBuffer->IsValid() ||
-        !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
-      // This can happen in release builds if allocating one of the two buffers
-      // failed. This in turn can happen if unreasonably large textures are
-      // requested.
-      return nullptr;
-    }
-    for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
-      const IntRect& rect = iter.Get();
-      mDTBuffer->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
-                          ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
-      mDTBufferOnWhite->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
-                                 ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
-    }
-  } else if (aPaintState.mContentType == gfxContentType::COLOR_ALPHA && HaveBuffer()) {
-    // HaveBuffer() => we have an existing buffer that we must clear
-    for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
-      const IntRect& rect = iter.Get();
-      result->ClearRect(Rect(rect.x, rect.y, rect.width, rect.height));
-    }
+  // Can't stack allocate refcounted objects.
+  RefPtr<CapturedPaintState> capturedPaintState =
+    MakeAndAddRef<CapturedPaintState>(regionToDraw,
+                                      mDTBuffer,
+                                      mDTBufferOnWhite,
+                                      Matrix(),
+                                      aPaintState.mMode,
+                                      aPaintState.mContentType);
+
+  if (!RotatedContentBuffer::PrepareDrawTargetForPainting(capturedPaintState)) {
+    return nullptr;
   }
 
   return result;
 }
 
 already_AddRefed<SourceSurface>
 RotatedContentBuffer::GetSourceSurface(ContextSource aSource) const
 {
--- a/gfx/layers/RotatedBuffer.h
+++ b/gfx/layers/RotatedBuffer.h
@@ -17,16 +17,20 @@
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsRegion.h"                   // for nsIntRegion
 #include "LayersTypes.h"
 
 namespace mozilla {
 namespace layers {
 
+class CapturedPaintState;
+
+typedef bool (*PrepDrawTargetForPaintingCallback)(CapturedPaintState*);
+
 class TextureClient;
 class PaintedLayer;
 
 /**
  * This is a cairo/Thebes surface, but with a literal twist. Scrolling
  * causes the layer's visible region to move. We want to keep
  * reusing the same surface if the region size hasn't changed, but we don't
  * want to keep moving the contents of the surface around in memory. So
@@ -288,16 +292,24 @@ public:
    *
    * @param aPaintState Paint state data returned by a call to BeginPaint
    * @param aIter Paint state iterator. Only required if PAINT_CAN_DRAW_ROTATED
    * was specified to BeginPaint.
    */
   gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
                                                DrawIterator* aIter = nullptr);
 
+  gfx::DrawTarget* BorrowDrawTargetForRecording(PaintState& aPaintState,
+                                                DrawIterator* aIter = nullptr);
+
+  void ExpandDrawRegion(PaintState& aPaintState,
+                        DrawIterator* aIter,
+                        gfx::BackendType aBackendType);
+
+  static bool PrepareDrawTargetForPainting(CapturedPaintState*);
   enum {
     BUFFER_COMPONENT_ALPHA = 0x02 // Dual buffers should be created for drawing with
                                   // component alpha.
   };
   /**
    * Return a new surface of |aSize| and |aType|.
    *
    * If the created buffer supports azure content, then the result(s) will
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/gfx/Types.h"          // for Float, etc
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/Preferences.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "PaintThread.h"
 #include "ReadbackProcessor.h"
+#include "RotatedBuffer.h"
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 bool
 ClientPaintedLayer::EnsureContentClient()
@@ -183,68 +184,104 @@ ClientPaintedLayer::PaintThebes(nsTArray
 
   mContentClient->EndPaint(aReadbackUpdates);
 
   if (didUpdate) {
     UpdateContentClient(state);
   }
 }
 
+/***
+ * If we can, let's paint this ClientPaintedLayer's contents off the main thread.
+ * The essential idea is that we ask the ContentClient for a DrawTarget and record
+ * the moz2d commands. On the Paint Thread, we replay those commands to the
+ * destination draw target. There are a couple of lifetime issues here though:
+ *
+ * 1) TextureClient owns the underlying buffer and DrawTarget. Because of this
+ *    we have to keep the TextureClient and DrawTarget alive but trick the
+ *    TextureClient into thinking it's already returned the DrawTarget
+ *    since we iterate through different Rects to get DrawTargets*. If
+ *    the TextureClient goes away, the DrawTarget and thus buffer can too.
+ * 2) When ContentClient::EndPaint happens, it flushes the DrawTarget. We have
+ *    to Reflush on the Paint Thread
+ * 3) DrawTarget API is NOT thread safe. We get around this by recording
+ *    on the main thread and painting on the paint thread. Logically,
+ *    ClientLayerManager will force a flushed paint and block the main thread
+ *    if we have another transaction. Thus we have a gap between when the main
+ *    thread records, the paint thread paints, and we block the main thread
+ *    from trying to paint again. The underlying API however is NOT thread safe.
+ *  4) We have both "sync" and "async" OMTP. Sync OMTP means we paint on the main thread
+ *     but block the main thread while the paint thread paints. Async OMTP doesn't block
+ *     the main thread. Sync OMTP is only meant to be used as a debugging tool.
+ */
 bool
 ClientPaintedLayer::PaintOffMainThread()
 {
   mContentClient->BeginAsyncPaint();
 
   uint32_t flags = GetPaintFlags();
 
   PaintState state = mContentClient->BeginPaintBuffer(this, flags);
   if (!UpdatePaintRegion(state)) {
     return false;
   }
 
   bool didUpdate = false;
   RotatedContentBuffer::DrawIterator iter;
-  while (DrawTarget* target = mContentClient->BorrowDrawTargetForPainting(state, &iter)) {
+  // Debug Protip: Change to BorrowDrawTargetForPainting if using sync OMTP.
+  while (DrawTarget* target = mContentClient->BorrowDrawTargetForRecording(state, &iter)) {
     if (!target || !target->IsValid()) {
       if (target) {
         mContentClient->ReturnDrawTargetToBuffer(target);
       }
       continue;
     }
 
-    // We don't clear the rect here like WRPaintedBlobLayers do
-    // because ContentClient already clears the surface for us during BeginPaint.
     RefPtr<DrawTargetCapture> captureDT =
       Factory::CreateCaptureDrawTarget(target->GetBackendType(),
                                        target->GetSize(),
                                        target->GetFormat());
-    captureDT->SetTransform(target->GetTransform());
 
+    Matrix capturedTransform = target->GetTransform();
+    captureDT->SetTransform(capturedTransform);
+
+    // TODO: Capture AA Flags and reset them in PaintThread
     SetAntialiasingFlags(this, captureDT);
     SetAntialiasingFlags(this, target);
 
     RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(captureDT);
     MOZ_ASSERT(ctx); // already checked the target above
 
     ClientManager()->GetPaintedLayerCallback()(this,
                                               ctx,
                                               iter.mDrawRegion,
                                               iter.mDrawRegion,
                                               state.mClip,
                                               state.mRegionToInvalidate,
                                               ClientManager()->GetPaintedLayerCallbackData());
 
     ctx = nullptr;
 
-    PaintThread::Get()->PaintContents(captureDT, target);
+    // TODO: Fixup component alpha
+    DrawTarget* targetOnWhite = nullptr;
+    RefPtr<CapturedPaintState> capturedState
+      = MakeAndAddRef<CapturedPaintState>(state.mRegionToDraw,
+                                          target, targetOnWhite,
+                                          capturedTransform,
+                                          state.mMode,
+                                          state.mContentType);
+
+    PaintThread::Get()->PaintContents(captureDT,
+                                      capturedState,
+                                      RotatedContentBuffer::PrepareDrawTargetForPainting);
 
     mContentClient->ReturnDrawTargetToBuffer(target);
+
     didUpdate = true;
   }
-
   mContentClient->EndPaint(nullptr);
 
   if (didUpdate) {
     UpdateContentClient(state);
   }
   return true;
 }
 
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -91,17 +91,18 @@ public:
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
 
   virtual void Clear() = 0;
   virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer,
                                                             uint32_t aFlags) = 0;
   virtual gfx::DrawTarget* BorrowDrawTargetForPainting(RotatedContentBuffer::PaintState& aPaintState,
                                                        RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
   virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) = 0;
-
+  virtual gfx::DrawTarget* BorrowDrawTargetForRecording(RotatedContentBuffer::PaintState& aPaintState,
+                                                        RotatedContentBuffer::DrawIterator* aIter = nullptr) = 0;
   // Called as part of the layers transation reply. Conveys data about our
   // buffer(s) from the compositor. If appropriate we should swap references
   // to our buffers.
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {}
 
   // call before and after painting into this content client
   virtual void BeginPaint() {}
   virtual void BeginAsyncPaint();
@@ -146,16 +147,21 @@ public:
   {
     return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
   }
   virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
                                                        RotatedContentBuffer::DrawIterator* aIter = nullptr) override
   {
     return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
   }
+  virtual gfx::DrawTarget* BorrowDrawTargetForRecording(PaintState& aPaintState,
+                                                       RotatedContentBuffer::DrawIterator* aIter = nullptr) override
+  {
+    return RotatedContentBuffer::BorrowDrawTargetForRecording(aPaintState, aIter);
+  }
   virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
   {
     BorrowDrawTarget::ReturnDrawTarget(aReturned);
   }
 
   void DrawTo(PaintedLayer* aLayer,
               gfx::DrawTarget* aTarget,
               float aOpacity,
@@ -229,16 +235,21 @@ public:
   {
     return RotatedContentBuffer::BeginPaint(aLayer, aFlags);
   }
   virtual gfx::DrawTarget* BorrowDrawTargetForPainting(PaintState& aPaintState,
                                                        RotatedContentBuffer::DrawIterator* aIter = nullptr) override
   {
     return RotatedContentBuffer::BorrowDrawTargetForPainting(aPaintState, aIter);
   }
+  virtual gfx::DrawTarget* BorrowDrawTargetForRecording(PaintState& aPaintState,
+                                                        RotatedContentBuffer::DrawIterator* aIter = nullptr) override
+  {
+    return RotatedContentBuffer::BorrowDrawTargetForRecording(aPaintState, aIter);
+  }
   virtual void ReturnDrawTargetToBuffer(gfx::DrawTarget*& aReturned) override
   {
     BorrowDrawTarget::ReturnDrawTarget(aReturned);
   }
 
   /**
    * Begin/End Paint map a gfxASurface from the texture client
    * into the buffer of RotatedBuffer. The surface is only
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -213,28 +213,29 @@ CrossProcessCompositorBridgeParent::Allo
   MOZ_ASSERT(sIndirectLayerTrees[layersId].mWrBridge == nullptr);
   WebRenderBridgeParent* parent = nullptr;
   CompositorBridgeParent* cbp = sIndirectLayerTrees[layersId].mParent;
   if (!cbp) {
     // This could happen when this function is called after CompositorBridgeParent destruction.
     // This was observed during Tab move between different windows.
     NS_WARNING("Created child without a matching parent?");
     parent = WebRenderBridgeParent::CreateDestroyed();
+    parent->AddRef(); // IPDL reference
     *aIdNamespace = parent->GetIdNamespace();
     *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     return parent;
   }
   WebRenderBridgeParent* root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge.get();
 
   RefPtr<wr::WebRenderAPI> api = root->GetWebRenderAPI();
   RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager();
   RefPtr<CompositorAnimationStorage> animStorage = cbp->GetAnimationStorage();
   parent = new WebRenderBridgeParent(this, aPipelineId, nullptr, root->CompositorScheduler(), Move(api), Move(holder), Move(animStorage));
+  parent->AddRef(); // IPDL reference
 
-  parent->AddRef(); // IPDL reference
   sIndirectLayerTrees[layersId].mCrossProcessParent = this;
   sIndirectLayerTrees[layersId].mWrBridge = parent;
   *aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier();
   *aIdNamespace = parent->GetIdNamespace();
 
   return parent;
 }
 
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -2,55 +2,29 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/StackingContextHelper.h"
 
 #include "mozilla/layers/WebRenderLayer.h"
 #include "UnitTransforms.h"
+#include "nsDisplayList.h"
 
 namespace mozilla {
 namespace layers {
 
 StackingContextHelper::StackingContextHelper()
   : mBuilder(nullptr)
 {
   // mOrigin remains at 0,0
 }
 
 StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParentSC,
                                              wr::DisplayListBuilder& aBuilder,
-                                             LayerRect aBoundForSC,
-                                             LayerPoint aOrigin,
-                                             uint64_t aAnimationsId,
-                                             float* aOpacityPtr,
-                                             gfx::Matrix4x4* aTransformPtr,
-                                             const nsTArray<wr::WrFilterOp>& aFilters,
-                                             const gfx::CompositionOp& aMixBlendMode)
-  : mBuilder(&aBuilder)
-{
-  wr::LayoutRect scBounds = aParentSC.ToRelativeLayoutRect(aBoundForSC);
-  if (aTransformPtr) {
-    mTransform = *aTransformPtr;
-  }
-
-  mBuilder->PushStackingContext(scBounds,
-                                aAnimationsId,
-                                aOpacityPtr,
-                                aTransformPtr,
-                                wr::TransformStyle::Flat,
-                                wr::ToMixBlendMode(aMixBlendMode),
-                                aFilters);
-
-  mOrigin = aOrigin;
-}
-
-StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParentSC,
-                                             wr::DisplayListBuilder& aBuilder,
                                              WebRenderLayer* aLayer,
                                              const Maybe<gfx::Matrix4x4>& aTransform,
                                              const nsTArray<wr::WrFilterOp>& aFilters)
   : mBuilder(&aBuilder)
 {
   wr::LayoutRect scBounds = aParentSC.ToRelativeLayoutRect(aLayer->BoundsForStackingContext());
   Layer* layer = aLayer->GetLayer();
   mTransform = aTransform.valueOr(layer->GetTransform());
@@ -83,16 +57,57 @@ StackingContextHelper::StackingContextHe
                                 aOpacityPtr,
                                 aTransformPtr,
                                 wr::TransformStyle::Flat,
                                 wr::ToMixBlendMode(aLayer->GetLayer()->GetMixBlendMode()),
                                 aFilters);
   mOrigin = aLayer->Bounds().TopLeft();
 }
 
+StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParentSC,
+                                             wr::DisplayListBuilder& aBuilder,
+                                             nsDisplayListBuilder* aDisplayListBuilder,
+                                             nsDisplayItem* aItem,
+                                             nsDisplayList* aDisplayList,
+                                             gfx::Matrix4x4Typed<LayerPixel, LayerPixel>* aBoundTransform,
+                                             uint64_t aAnimationsId,
+                                             float* aOpacityPtr,
+                                             gfx::Matrix4x4* aTransformPtr,
+                                             const nsTArray<wr::WrFilterOp>& aFilters,
+                                             const gfx::CompositionOp& aMixBlendMode)
+  : mBuilder(&aBuilder)
+{
+  nsRect itemBounds = aDisplayList->GetClippedBoundsWithRespectToASR(aDisplayListBuilder, aItem->GetActiveScrolledRoot());
+  nsRect childrenVisible = aItem->GetVisibleRectForChildren();
+  nsRect visibleRect = itemBounds.Intersect(childrenVisible);
+  float appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
+  LayerRect bounds = ViewAs<LayerPixel>(LayoutDeviceRect::FromAppUnits(visibleRect, appUnitsPerDevPixel),
+                                        PixelCastJustification::WebRenderHasUnitResolution);
+
+  // WR will only apply the 'translate' of the transform, so we need to do the scale/rotation manually.
+  if (aBoundTransform && !aBoundTransform->IsIdentity()) {
+    bounds.MoveTo(aBoundTransform->TransformPoint(bounds.TopLeft()));
+  }
+
+  wr::LayoutRect scBounds = aParentSC.ToRelativeLayoutRect(bounds);
+  if (aTransformPtr) {
+    mTransform = *aTransformPtr;
+  }
+
+  mBuilder->PushStackingContext(scBounds,
+                                aAnimationsId,
+                                aOpacityPtr,
+                                aTransformPtr,
+                                wr::TransformStyle::Flat,
+                                wr::ToMixBlendMode(aMixBlendMode),
+                                aFilters);
+
+  mOrigin = bounds.TopLeft();
+}
+
 StackingContextHelper::~StackingContextHelper()
 {
   if (mBuilder) {
     mBuilder->PopStackingContext();
   }
 }
 
 wr::LayoutRect
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -7,16 +7,20 @@
 #define GFX_STACKINGCONTEXTHELPER_H
 
 #include "mozilla/Attributes.h"
 #include "mozilla/gfx/MatrixFwd.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "Units.h"
 
+class nsDisplayListBuilder;
+class nsDisplayItem;
+class nsDisplayList;
+
 namespace mozilla {
 namespace layers {
 
 class WebRenderLayer;
 
 /**
  * This is a helper class that pushes/pops a stacking context, and manages
  * some of the coordinate space transformations needed.
@@ -40,18 +44,20 @@ public:
                         WebRenderLayer* aLayer,
                         uint64_t aAnimationsId,
                         float* aOpacityPtr,
                         gfx::Matrix4x4* aTransformPtr,
                         const nsTArray<wr::WrFilterOp>& aFilters = nsTArray<wr::WrFilterOp>());
   // The constructor for layers-free mode.
   StackingContextHelper(const StackingContextHelper& aParentSC,
                         wr::DisplayListBuilder& aBuilder,
-                        LayerRect aBoundForSC,
-                        LayerPoint aOrigin,
+                        nsDisplayListBuilder* aDisplayListBuilder,
+                        nsDisplayItem* aItem,
+                        nsDisplayList* aDisplayList,
+                        gfx::Matrix4x4Typed<LayerPixel, LayerPixel>* aBoundTransform,
                         uint64_t aAnimationsId,
                         float* aOpacityPtr,
                         gfx::Matrix4x4* aTransformPtr,
                         const nsTArray<wr::WrFilterOp>& aFilters = nsTArray<wr::WrFilterOp>(),
                         const gfx::CompositionOp& aMixBlendMode = gfx::CompositionOp::OP_OVER);
   // This version of the constructor should only be used at the root level
   // of the tree, so that we have a StackingContextHelper to pass down into
   // the RenderLayer traversal, but don't actually want it to push a stacking
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -1261,33 +1261,37 @@ gfxDWriteFontList::GetStandardFamilyName
     }
 
     return false;
 }
 
 bool
 gfxDWriteFontList::FindAndAddFamilies(const nsAString& aFamily,
                                       nsTArray<gfxFontFamily*>* aOutput,
+                                      bool aDeferOtherFamilyNamesLoading,
                                       gfxFontStyle* aStyle,
                                       gfxFloat aDevToCssSize)
 {
     nsAutoString keyName(aFamily);
     BuildKeyNameFromFontName(keyName);
 
     gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
     if (ff) {
         aOutput->AppendElement(ff);
         return true;
     }
 
     if (mNonExistingFonts.Contains(keyName)) {
         return false;
     }
 
-    return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+    return gfxPlatformFontList::FindAndAddFamilies(aFamily,
+                                                   aOutput,
+                                                   aDeferOtherFamilyNamesLoading,
+                                                   aStyle,
                                                    aDevToCssSize);
 }
 
 void
 gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                           FontListSizes* aSizes) const
 {
     gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -374,16 +374,17 @@ public:
     bool GetStandardFamilyName(const nsAString& aFontName,
                                  nsAString& aFamilyName);
 
     IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
     bool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
+                            bool aDeferOtherFamilyNamesLoading,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     gfxFloat GetForceGDIClassicMaxFontSize() { return mForceGDIClassicMaxFontSize; }
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -1478,16 +1478,17 @@ gfxFcPlatformFontList::MakePlatformFont(
 
     return new gfxFontconfigFontEntry(aFontName, aWeight, aStretch,
                                       aStyle, aFontData, face);
 }
 
 bool
 gfxFcPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
                                           nsTArray<gfxFontFamily*>* aOutput,
+                                          bool aDeferOtherFamilyNamesLoading,
                                           gfxFontStyle* aStyle,
                                           gfxFloat aDevToCssSize)
 {
     nsAutoString familyName(aFamily);
     ToLowerCase(familyName);
     nsIAtom* language = (aStyle ? aStyle->language.get() : nullptr);
 
     // deprecated generic names are explicitly converted to standard generics
@@ -1560,17 +1561,19 @@ gfxFcPlatformFontList::FindAndAddFamilie
                             i, &substName) == FcResultMatch;
          i++)
     {
         NS_ConvertUTF8toUTF16 subst(ToCharPtr(substName));
         if (sentinelFirstFamily &&
             FcStrCmp(substName, sentinelFirstFamily) == 0) {
             break;
         }
-        gfxPlatformFontList::FindAndAddFamilies(subst, &cachedFamilies);
+        gfxPlatformFontList::FindAndAddFamilies(subst,
+                                                &cachedFamilies,
+                                                aDeferOtherFamilyNamesLoading);
     }
 
     // Cache the resulting list, so we don't have to do this again.
     mFcSubstituteCache.Put(familyToFind, cachedFamilies);
 
     if (cachedFamilies.IsEmpty()) {
         return false;
     }
@@ -1842,17 +1845,18 @@ gfxFcPlatformFontList::FindGenericFamili
         FcPattern* font = faces->fonts[i];
         FcChar8* mappedGeneric = nullptr;
 
         FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
         if (mappedGeneric) {
             NS_ConvertUTF8toUTF16 mappedGenericName(ToCharPtr(mappedGeneric));
             AutoTArray<gfxFontFamily*,1> genericFamilies;
             if (gfxPlatformFontList::FindAndAddFamilies(mappedGenericName,
-                                                        &genericFamilies)) {
+                                                        &genericFamilies,
+                                                        true)) {
                 MOZ_ASSERT(genericFamilies.Length() == 1,
                            "expected a single family");
                 if (!prefFonts->Contains(genericFamilies[0])) {
                     prefFonts->AppendElement(genericFamilies[0]);
                     bool foundLang =
                         !fcLang.IsEmpty() &&
                         PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
                     foundFontWithLang = foundFontWithLang || foundLang;
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -246,16 +246,17 @@ public:
     MakePlatformFont(const nsAString& aFontName, uint16_t aWeight,
                      int16_t aStretch,
                      uint8_t aStyle,
                      const uint8_t* aFontData,
                      uint32_t aLength) override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
+                            bool aDeferOtherFamilyNamesLoading,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     bool GetStandardFamilyName(const nsAString& aFontName,
                                nsAString& aFamilyName) override;
 
     FcConfig* GetLastConfig() const { return mLastConfig; }
 
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -907,33 +907,37 @@ gfxGDIFontList::MakePlatformFont(const n
     }
 
     return fe;
 }
 
 bool
 gfxGDIFontList::FindAndAddFamilies(const nsAString& aFamily,
                                    nsTArray<gfxFontFamily*>* aOutput,
+                                   bool aDeferOtherFamilyNamesLoading,
                                    gfxFontStyle* aStyle,
                                    gfxFloat aDevToCssSize)
 {
     nsAutoString keyName(aFamily);
     BuildKeyNameFromFontName(keyName);
 
     gfxFontFamily *ff = mFontSubstitutes.GetWeak(keyName);
     if (ff) {
         aOutput->AppendElement(ff);
         return true;
     }
 
     if (mNonExistingFonts.Contains(keyName)) {
         return false;
     }
 
-    return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+    return gfxPlatformFontList::FindAndAddFamilies(aFamily,
+                                                   aOutput,
+                                                   aDeferOtherFamilyNamesLoading,
+                                                   aStyle,
                                                    aDevToCssSize);
 }
 
 gfxFontFamily*
 gfxGDIFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
 {
     gfxFontFamily *ff = nullptr;
 
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -304,16 +304,17 @@ public:
         return static_cast<gfxGDIFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
     virtual nsresult InitFontListForPlatform() override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
+                            bool aDeferOtherFamilyNamesLoading,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
                                           uint8_t aStyle);
 
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -101,16 +101,17 @@ public:
                                    uint16_t aWeight,
                                    int16_t aStretch,
                                    uint8_t aStyle,
                                    const uint8_t* aFontData,
                                    uint32_t aLength) override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
+                            bool aDeferOtherFamilyNamesLoading,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     // lookup the system font for a particular system font type and set
     // the name and style characteristics
     void LookupSystemFont(mozilla::LookAndFeel::FontID aSystemFontID,
                           nsAString& aSystemFontName,
                           gfxFontStyle &aFontStyle,
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -1248,31 +1248,35 @@ gfxMacPlatformFontList::MakePlatformFont
 
 // Webkit code uses a system font meta name, so mimic that here
 // WebCore/platform/graphics/mac/FontCacheMac.mm
 static const char kSystemFont_system[] = "-apple-system";
 
 bool
 gfxMacPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
                                            nsTArray<gfxFontFamily*>* aOutput,
+                                           bool aDeferOtherFamilyNamesLoading,
                                            gfxFontStyle* aStyle,
                                            gfxFloat aDevToCssSize)
 {
     // search for special system font name, -apple-system
     if (aFamily.EqualsLiteral(kSystemFont_system)) {
         if (mUseSizeSensitiveSystemFont &&
             aStyle && (aStyle->size * aDevToCssSize) >= kTextDisplayCrossover) {
             aOutput->AppendElement(FindSystemFontFamily(mSystemDisplayFontFamilyName));
             return true;
         }
         aOutput->AppendElement(FindSystemFontFamily(mSystemTextFontFamilyName));
         return true;
     }
 
-    return gfxPlatformFontList::FindAndAddFamilies(aFamily, aOutput, aStyle,
+    return gfxPlatformFontList::FindAndAddFamilies(aFamily,
+                                                   aOutput,
+                                                   aDeferOtherFamilyNamesLoading,
+                                                   aStyle,
                                                    aDevToCssSize);
 }
 
 void
 gfxMacPlatformFontList::LookupSystemFont(LookAndFeel::FontID aSystemFontID,
                                          nsAString& aSystemFontName,
                                          gfxFontStyle &aFontStyle,
                                          float aDevPixPerCSSPixel)
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -261,19 +261,21 @@ gfxPlatformFontList::InitFontList()
     gfxFontCache *fontCache = gfxFontCache::GetCache();
     if (fontCache) {
         fontCache->AgeAllGenerations();
         fontCache->FlushShapedWordCaches();
     }
 
     gfxPlatform::PurgeSkiaFontCache();
 
+    CancelInitOtherFamilyNamesTask();
     mFontFamilies.Clear();
     mOtherFamilyNames.Clear();
     mOtherFamilyNamesInitialized = false;
+
     if (mExtraNames) {
         mExtraNames->mFullnames.Clear();
         mExtraNames->mPostscriptNames.Clear();
     }
     mFaceNameListsInitialized = false;
     ClearLangGroupPrefFonts();
     mReplacementCharFallbackFamily = nullptr;
     CancelLoader();
@@ -299,47 +301,30 @@ gfxPlatformFontList::GenerateFontListKey
 {
     aResult = aKeyName;
     ToLowerCase(aResult);
 }
 
 #define OTHERNAMES_TIMEOUT 200
 
 void
-gfxPlatformFontList::InitOtherFamilyNames()
+gfxPlatformFontList::InitOtherFamilyNames(bool aDeferOtherFamilyNamesLoading)
 {
     if (mOtherFamilyNamesInitialized) {
         return;
     }
 
-    TimeStamp start = TimeStamp::Now();
-    bool timedOut = false;
-
-    for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
-        RefPtr<gfxFontFamily>& family = iter.Data();
-        family->ReadOtherFamilyNames(this);
-        TimeDuration elapsed = TimeStamp::Now() - start;
-        if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
-            timedOut = true;
-            break;
+    if (aDeferOtherFamilyNamesLoading) {
+        if (!mPendingOtherFamilyNameTask) {
+            RefPtr<mozilla::CancelableRunnable> task = new InitOtherFamilyNamesRunnable();
+            mPendingOtherFamilyNameTask = task;
+            NS_IdleDispatchToCurrentThread(task.forget());
         }
-    }
-
-    if (!timedOut) {
-        mOtherFamilyNamesInitialized = true;
-    }
-    TimeStamp end = TimeStamp::Now();
-    Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
-                                   start, end);
-
-    if (LOG_FONTINIT_ENABLED()) {
-        TimeDuration elapsed = end - start;
-        LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
-                      elapsed.ToMilliseconds(),
-                      (timedOut ? "timeout" : "")));
+    } else {
+        InitOtherFamilyNamesInternal(false);
     }
 }
 
 // time limit for loading facename lists (ms)
 #define NAMELIST_TIMEOUT  200
 
 gfxFontEntry*
 gfxPlatformFontList::SearchFamiliesForFaceName(const nsAString& aFaceName)
@@ -362,18 +347,18 @@ gfxPlatformFontList::SearchFamiliesForFa
         if (firstChar && ToLowerCase(key.CharAt(0)) != firstChar) {
             continue;
         }
 
         family->ReadFaceNames(this, NeedFullnamePostscriptNames());
 
         TimeDuration elapsed = TimeStamp::Now() - start;
         if (elapsed.ToMilliseconds() > NAMELIST_TIMEOUT) {
-           timedOut = true;
-           break;
+            timedOut = true;
+            break;
         }
     }
 
     lookup = FindFaceName(aFaceName);
 
     TimeStamp end = TimeStamp::Now();
     Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITFACENAMELISTS,
                                    start, end);
@@ -692,19 +677,20 @@ gfxPlatformFontList::CheckFamily(gfxFont
         GenerateFontListKey(aFamily->Name(), key);
         mFontFamilies.Remove(key);
         return nullptr;
     }
 
     return aFamily;
 }
 
-bool 
+bool
 gfxPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
                                         nsTArray<gfxFontFamily*>* aOutput,
+                                        bool aDeferOtherFamilyNamesLoading,
                                         gfxFontStyle* aStyle,
                                         gfxFloat aDevToCssSize)
 {
     nsAutoString key;
     GenerateFontListKey(aFamily, key);
 
     NS_ASSERTION(mFontFamilies.Count() != 0, "system font list was not initialized correctly");
 
@@ -717,17 +703,17 @@ gfxPlatformFontList::FindAndAddFamilies(
     }
 
     // if still not found and other family names not yet fully initialized,
     // initialize the rest of the list and try again.  this is done lazily
     // since reading name table entries is expensive.
     // although ASCII localized family names are possible they don't occur
     // in practice so avoid pulling in names at startup
     if (!familyEntry && !mOtherFamilyNamesInitialized && !IsASCII(aFamily)) {
-        InitOtherFamilyNames();
+        InitOtherFamilyNames(aDeferOtherFamilyNamesLoading);
         familyEntry = mOtherFamilyNames.GetWeak(key);
         if (!familyEntry && !mOtherFamilyNamesInitialized) {
             // localized family names load timed out, add name to list of
             // names to check after localized names are loaded
             if (!mOtherNamesMissed) {
                 mOtherNamesMissed = MakeUnique<nsTHashtable<nsStringHashKey>>(2);
             }
             mOtherNamesMissed->PutEntry(key);
@@ -891,17 +877,17 @@ gfxPlatformFontList::ResolveGenericFontN
     NS_ASSERTION(langGroup, "null lang group for pref lang");
 
     // lookup and add platform fonts uniquely
     for (const nsString& genericFamily : genericFamilies) {
         gfxFontStyle style;
         style.language = langGroup;
         style.systemFont = false;
         AutoTArray<gfxFontFamily*,10> families;
-        FindAndAddFamilies(genericFamily, &families, &style);
+        FindAndAddFamilies(genericFamily, &families, true, &style);
         for (gfxFontFamily* f : families) {
             if (!aGenericFamilies->Contains(f)) {
                 aGenericFamilies->AppendElement(f);
             }
         }
     }
 
 #if 0  // dump out generic mappings
@@ -1490,23 +1476,24 @@ gfxPlatformFontList::LoadFontInfo()
     if (LOG_FONTINIT_ENABLED()) {
         TimeDuration elapsed = TimeStamp::Now() - start;
         LOG_FONTINIT(("(fontinit) fontloader load pass %8.2f ms done %s\n",
                       elapsed.ToMilliseconds(), (done ? "true" : "false")));
     }
 
     if (done) {
         mOtherFamilyNamesInitialized = true;
+        CancelInitOtherFamilyNamesTask();
         mFaceNameListsInitialized = true;
     }
 
     return done;
 }
 
-void 
+void
 gfxPlatformFontList::CleanupLoader()
 {
     mFontFamiliesToLoad.Clear();
     mNumFamilies = 0;
     bool rebuilt = false, forceReflow = false;
 
     // if had missed face names that are now available, force reflow all
     if (mFaceNamesMissed) {
@@ -1517,17 +1504,17 @@ gfxPlatformFontList::CleanupLoader()
                 break;
             }
         }
         mFaceNamesMissed = nullptr;
     }
 
     if (mOtherNamesMissed) {
         for (auto it = mOtherNamesMissed->Iter(); !it.Done(); it.Next()) {
-            if (FindFamily(it.Get()->GetKey())) {
+            if (FindFamily(it.Get()->GetKey(), false)) {
                 forceReflow = true;
                 ForceGlobalReflow();
                 break;
             }
         }
         mOtherNamesMissed = nullptr;
     }
 
@@ -1686,10 +1673,77 @@ gfxPlatformFontList::AddSizeOfIncludingT
 }
 
 bool
 gfxPlatformFontList::IsFontFamilyWhitelistActive()
 {
     return mFontFamilyWhitelistActive;
 }
 
+void
+gfxPlatformFontList::InitOtherFamilyNamesInternal(bool aDeferOtherFamilyNamesLoading)
+{
+    if (mOtherFamilyNamesInitialized) {
+        return;
+    }
+
+    if (aDeferOtherFamilyNamesLoading) {
+        TimeStamp start = TimeStamp::Now();
+        bool timedOut = false;
+
+        for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+            RefPtr<gfxFontFamily>& family = iter.Data();
+            family->ReadOtherFamilyNames(this);
+            TimeDuration elapsed = TimeStamp::Now() - start;
+            if (elapsed.ToMilliseconds() > OTHERNAMES_TIMEOUT) {
+                timedOut = true;
+                break;
+            }
+        }
+
+        if (!timedOut) {
+            mOtherFamilyNamesInitialized = true;
+            CancelInitOtherFamilyNamesTask();
+        }
+        TimeStamp end = TimeStamp::Now();
+        Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES,
+                                       start, end);
+
+        if (LOG_FONTINIT_ENABLED()) {
+            TimeDuration elapsed = end - start;
+            LOG_FONTINIT(("(fontinit) InitOtherFamilyNames took %8.2f ms %s",
+                          elapsed.ToMilliseconds(),
+                          (timedOut ? "timeout" : "")));
+        }
+    } else {
+        TimeStamp start = TimeStamp::Now();
+
+        for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
+            RefPtr<gfxFontFamily>& family = iter.Data();
+            family->ReadOtherFamilyNames(this);
+        }
+
+        mOtherFamilyNamesInitialized = true;
+        CancelInitOtherFamilyNamesTask();
+
+        TimeStamp end = TimeStamp::Now();
+        Telemetry::AccumulateTimeDelta(Telemetry::FONTLIST_INITOTHERFAMILYNAMES_NO_DEFERRING,
+                                       start, end);
+
+        if (LOG_FONTINIT_ENABLED()) {
+            TimeDuration elapsed = end - start;
+            LOG_FONTINIT(("(fontinit) InitOtherFamilyNames without deferring took %8.2f ms",
+                          elapsed.ToMilliseconds()));
+        }
+    }
+}
+
+void
+gfxPlatformFontList::CancelInitOtherFamilyNamesTask()
+{
+    if (mPendingOtherFamilyNameTask) {
+        mPendingOtherFamilyNameTask->Cancel();
+        mPendingOtherFamilyNameTask = nullptr;
+    }
+}
+
 #undef LOG
 #undef LOG_ENABLED
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -85,16 +85,18 @@ struct FontListSizes {
     uint32_t mFontTableCacheSize; // memory used for the gfxFontEntry table caches
     uint32_t mCharMapsSize; // memory used for cmap coverage info
 };
 
 class gfxUserFontSet;
 
 class gfxPlatformFontList : public gfxFontInfoLoader
 {
+    friend class InitOtherFamilyNamesRunnable;
+
 public:
     typedef mozilla::unicode::Script Script;
 
     static gfxPlatformFontList* PlatformFontList() {
         return sPlatformFontList;
     }
 
     static nsresult Init() {
@@ -132,16 +134,17 @@ public:
                           const gfxFontStyle* aStyle);
 
     // Find family(ies) matching aFamily and append to the aOutput array
     // (there may be multiple results in the case of fontconfig aliases, etc).
     // Return true if any match was found and appended, false if none.
     virtual bool
     FindAndAddFamilies(const nsAString& aFamily,
                        nsTArray<gfxFontFamily*>* aOutput,
+                       bool aDeferOtherFamilyNamesLoading,
                        gfxFontStyle* aStyle = nullptr,
                        gfxFloat aDevToCssSize = 1.0);
 
     gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, bool& aNeedsBold);
 
     // name lookup table methods
 
     void AddOtherFamilyName(gfxFontFamily *aFamilyEntry, nsAString& aOtherFamilyName);
@@ -257,16 +260,52 @@ public:
     // Returns true if the font family whitelist is not empty.
     bool IsFontFamilyWhitelistActive();
 
     static void FontWhitelistPrefChanged(const char *aPref, void *aClosure) {
         gfxPlatformFontList::PlatformFontList()->UpdateFontList();
     }
 
 protected:
+    class InitOtherFamilyNamesRunnable : public mozilla::CancelableRunnable
+    {
+    public:
+        InitOtherFamilyNamesRunnable()
+            : CancelableRunnable("gfxPlatformFontList::InitOtherFamilyNamesRunnable")
+            , mIsCanceled(false)
+        {
+        }
+
+        NS_IMETHOD Run() override
+        {
+            if (mIsCanceled) {
+                return NS_OK;
+            }
+
+            gfxPlatformFontList* fontList = gfxPlatformFontList::PlatformFontList();
+            if (!fontList) {
+                return NS_OK;
+            }
+
+            fontList->InitOtherFamilyNamesInternal(true);
+
+            return NS_OK;
+        }
+
+        virtual nsresult Cancel() override
+        {
+            mIsCanceled = true;
+
+            return NS_OK;
+        }
+
+    private:
+        bool mIsCanceled;
+    };
+
     class MemoryReporter final : public nsIMemoryReporter
     {
         ~MemoryReporter() {}
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIMEMORYREPORTER
     };
 
@@ -313,22 +352,27 @@ protected:
 
     explicit gfxPlatformFontList(bool aNeedFullnamePostscriptNames = true);
 
     static gfxPlatformFontList *sPlatformFontList;
 
     // Convenience method to return the first matching family (if any) as found
     // by FindAndAddFamilies().
     gfxFontFamily*
-    FindFamily(const nsAString& aFamily, gfxFontStyle* aStyle = nullptr,
+    FindFamily(const nsAString& aFamily,
+               bool aDeferOtherFamilyNamesLoading = true,
+               gfxFontStyle* aStyle = nullptr,
                gfxFloat aDevToCssSize = 1.0)
     {
         AutoTArray<gfxFontFamily*,1> families;
-        return FindAndAddFamilies(aFamily, &families, aStyle, aDevToCssSize)
-               ? families[0] : nullptr;
+        return FindAndAddFamilies(aFamily,
+                                  &families,
+                                  aDeferOtherFamilyNamesLoading,
+                                  aStyle,
+                                  aDevToCssSize) ? families[0] : nullptr;
     }
 
     // Lookup family name in global family list without substitutions or
     // localized family name lookup. Used for common font fallback families.
     gfxFontFamily* FindFamilyByCanonicalName(const nsAString& aFamily) {
         nsAutoString key;
         gfxFontFamily *familyEntry;
         GenerateFontListKey(aFamily, key);
@@ -369,17 +413,19 @@ protected:
 
     void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen,
                             eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
     // verifies that a family contains a non-zero font count
     gfxFontFamily* CheckFamily(gfxFontFamily *aFamily);
 
     // initialize localized family names
-    void InitOtherFamilyNames();
+    void InitOtherFamilyNames(bool aDeferOtherFamilyNamesLoading);
+    void InitOtherFamilyNamesInternal(bool aDeferOtherFamilyNamesLoading);
+    void CancelInitOtherFamilyNamesTask();
 
     // search through font families, looking for a given name, initializing
     // facename lists along the way. first checks all families with names
     // close to face name, then searchs all families if not found.
     gfxFontEntry* SearchFamiliesForFaceName(const nsAString& aFaceName);
 
     // helper method for finding fullname/postscript names in facename lists
     gfxFontEntry* FindFaceName(const nsAString& aFaceName);
@@ -449,16 +495,19 @@ protected:
 
     // other family name ==> family entry (not unique, can have multiple names per
     // family entry, only names *other* than the canonical names are stored here)
     FontFamilyTable mOtherFamilyNames;
 
     // flag set after InitOtherFamilyNames is called upon first name lookup miss
     bool mOtherFamilyNamesInitialized;
 
+    // The pending InitOtherFamilyNames() task.
+    RefPtr<mozilla::CancelableRunnable> mPendingOtherFamilyNameTask;
+
     // flag set after fullname and Postcript name lists are populated
     bool mFaceNameListsInitialized;
 
     struct ExtraNames {
       ExtraNames() : mFullnames(64), mPostscriptNames(64) {}
 
       // fullname ==> font entry (unique, one name per font entry)
       FontEntryTable mFullnames;
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1868,17 +1868,17 @@ gfxFontGroup::AddPlatformFont(const nsAS
         if (family) {
             aFamilyList.AppendElement(family);
             return;
         }
     }
 
     // Not known in the user font set ==> check system fonts
     gfxPlatformFontList::PlatformFontList()
-        ->FindAndAddFamilies(aName, &aFamilyList, &mStyle, mDevToCssSize);
+        ->FindAndAddFamilies(aName, &aFamilyList, true, &mStyle, mDevToCssSize);
 }
 
 void
 gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily)
 {
     NS_ASSERTION(aFamily, "trying to add a null font family to fontlist");
     AutoTArray<gfxFontEntry*,4> fontEntryList;
     bool needsBold;
--- a/ipc/glue/BackgroundChild.h
+++ b/ipc/glue/BackgroundChild.h
@@ -29,23 +29,21 @@ class PBackgroundChild;
 
 // This class allows access to the PBackground protocol. PBackground allows
 // communication between any thread (in the parent or a child process) and a
 // single background thread in the parent process. Each PBackgroundChild
 // instance is tied to the thread on which it is created and must not be shared
 // across threads. Each PBackgroundChild is unique and valid as long as its
 // designated thread lives.
 //
-// Creation of PBackground is asynchronous. GetForCurrentThread() will return
-// null until the sequence is complete. GetOrCreateForCurrentThread() will start
-// the creation sequence and will call back via the
-// nsIIPCBackgroundChildCreateCallback interface when completed. Thereafter
-// (assuming success) GetForCurrentThread() will return the same actor every
-// time. SynchronouslyCreateForCurrentThread() will spin the event loop until
-// the BackgroundChild until the creation sequence is complete.
+// Creation of PBackground is synchronous. GetOrCreateForCurrentThread will
+// create the actor if it doesn't exist yet. Thereafter (assuming success)
+// GetForCurrentThread() will return the same actor every time.
+// GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback)
+// emulates former asynchronous behavior and might be removed in future.
 //
 // CloseForCurrentThread() will close the current PBackground actor.  Subsequent
 // calls to GetForCurrentThread will return null.  CloseForCurrentThread() may
 // only be called exactly once for each thread-specific actor.  Currently it is
 // illegal to call this before the PBackground actor has been created.
 //
 // The PBackgroundChild actor and all its sub-protocol actors will be
 // automatically destroyed when its designated thread completes.
@@ -63,17 +61,17 @@ public:
   GetForCurrentThread();
 
   // See above.
   static bool
   GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
 
   // See above.
   static PBackgroundChild*
-  SynchronouslyCreateForCurrentThread();
+  GetOrCreateForCurrentThread();
 
   // See above.
   static void
   CloseForCurrentThread();
 
 private:
   // Only called by ContentChild or ContentParent.
   static void
--- a/ipc/glue/BackgroundImpl.cpp
+++ b/ipc/glue/BackgroundImpl.cpp
@@ -60,37 +60,39 @@
   while (0)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace {
 
+class ChildImpl;
+
 // -----------------------------------------------------------------------------
 // Utility Functions
 // -----------------------------------------------------------------------------
 
 
 void
 AssertIsInMainProcess()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 }
 
 void
-AssertIsInChildProcess()
+AssertIsOnMainThread()
 {
-  MOZ_ASSERT(!XRE_IsParentProcess());
+  THREADSAFETY_ASSERT(NS_IsMainThread());
 }
 
 void
-AssertIsOnMainThread()
+AssertIsNotOnMainThread()
 {
-  THREADSAFETY_ASSERT(NS_IsMainThread());
+  THREADSAFETY_ASSERT(!NS_IsMainThread());
 }
 
 // -----------------------------------------------------------------------------
 // ParentImpl Declaration
 // -----------------------------------------------------------------------------
 
 class ParentImpl final : public BackgroundParentImpl
 {
@@ -99,18 +101,18 @@ class ParentImpl final : public Backgrou
 public:
   class CreateCallback;
 
 private:
   class ShutdownObserver;
   class RequestMessageLoopRunnable;
   class ShutdownBackgroundThreadRunnable;
   class ForceCloseBackgroundActorsRunnable;
-  class CreateCallbackRunnable;
   class ConnectActorRunnable;
+  class CreateActorHelper;
 
   struct MOZ_STACK_CLASS TimerCallbackClosure
   {
     nsIThread* mThread;
     nsTArray<ParentImpl*>* mLiveActors;
 
     TimerCallbackClosure(nsIThread* aThread, nsTArray<ParentImpl*>* aLiveActors)
       : mThread(aThread), mLiveActors(aLiveActors)
@@ -153,20 +155,16 @@ private:
   // This is only modified on the main thread. It is true after the shutdown
   // observer is registered and is never unset thereafter.
   static bool sShutdownObserverRegistered;
 
   // This is only modified on the main thread. It prevents us from trying to
   // create the background thread after application shutdown has started.
   static bool sShutdownHasStarted;
 
-  // This is only modified on the main thread. It is a FIFO queue for callbacks
-  // waiting for the background thread to be created.
-  static StaticAutoPtr<nsTArray<RefPtr<CreateCallback>>> sPendingCallbacks;
-
   // Only touched on the main thread, null if this is a same-process actor.
   RefPtr<ContentParent> mContent;
 
   // Set when the actor is opened successfully and used to handle shutdown
   // hangs. Only touched on the background thread.
   nsTArray<ParentImpl*>* mLiveActorArray;
 
   // Set at construction to indicate whether this parent actor corresponds to a
@@ -174,18 +172,18 @@ private:
   // in the same process.
   const bool mIsOtherProcessActor;
 
   // Set after ActorDestroy has been called. Only touched on the background
   // thread.
   bool mActorDestroyed;
 
 public:
-  static bool
-  CreateActorForSameProcess(CreateCallback* aCallback);
+  static already_AddRefed<ChildImpl>
+  CreateActorForSameProcess();
 
   static bool
   IsOnBackgroundThread()
   {
     return PR_GetCurrentThread() == sBackgroundPRThread;
   }
 
   static void
@@ -286,125 +284,122 @@ class ChildImpl final : public Backgroun
 {
   friend class mozilla::ipc::BackgroundChild;
   friend class mozilla::ipc::BackgroundChildImpl;
 
   typedef base::ProcessId ProcessId;
   typedef mozilla::ipc::Transport Transport;
 
   class ShutdownObserver;
-  class CreateActorRunnable;
-  class ParentCreateCallback;
-  class AlreadyCreatedCallbackRunnable;
-  class FailedCreateCallbackRunnable;
-  class OpenChildProcessActorRunnable;
-  class OpenMainProcessActorRunnable;
+  class ActorCreatedRunnable;
 
   // A thread-local index that is not valid.
   static const unsigned int kBadThreadLocalIndex =
     static_cast<unsigned int>(-1);
 
   // This is only modified on the main thread. It is the thread-local index that
   // we use to store the BackgroundChild for each thread.
   static unsigned int sThreadLocalIndex;
 
   struct ThreadLocalInfo
   {
-    explicit ThreadLocalInfo(nsIIPCBackgroundChildCreateCallback* aCallback)
+    ThreadLocalInfo()
 #ifdef DEBUG
       : mClosed(false)
 #endif
     {
-      mCallbacks.AppendElement(aCallback);
     }
 
     RefPtr<ChildImpl> mActor;
-    nsTArray<nsCOMPtr<nsIIPCBackgroundChildCreateCallback>> mCallbacks;
     nsAutoPtr<BackgroundChildImpl::ThreadLocal> mConsumerThreadLocal;
 #ifdef DEBUG
     bool mClosed;
 #endif
   };
 
-  // This is only modified on the main thread. It is a FIFO queue for actors
-  // that are in the process of construction.
-  static StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> sPendingTargets;
-
   // This is only modified on the main thread. It prevents us from trying to
   // create the background thread after application shutdown has started.
   static bool sShutdownHasStarted;
 
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
-  nsISerialEventTarget* mBoundEventTarget;
+  nsISerialEventTarget* mOwningEventTarget;
 #endif
 
 #ifdef DEBUG
+  bool mActorWasAlive;
   bool mActorDestroyed;
 #endif
 
 public:
-  static bool
-  OpenProtocolOnMainThread(nsIEventTarget* aEventTarget);
-
   static void
   Shutdown();
 
   void
-  AssertIsOnBoundThread()
+  AssertIsOnOwningThread()
   {
-    THREADSAFETY_ASSERT(mBoundEventTarget);
+    THREADSAFETY_ASSERT(mOwningEventTarget);
 
 #ifdef RELEASE_OR_BETA
     DebugOnly<bool> current;
 #else
     bool current;
 #endif
     THREADSAFETY_ASSERT(
-      NS_SUCCEEDED(mBoundEventTarget->IsOnCurrentThread(&current)));
+      NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)));
     THREADSAFETY_ASSERT(current);
   }
 
   void
   AssertActorDestroyed()
   {
     MOZ_ASSERT(mActorDestroyed, "ChildImpl::ActorDestroy not called in time");
   }
 
-  ChildImpl()
+  explicit ChildImpl()
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
-  : mBoundEventTarget(nullptr)
+  : mOwningEventTarget(GetCurrentThreadSerialEventTarget())
 #endif
 #ifdef DEBUG
+  , mActorWasAlive(false)
   , mActorDestroyed(false)
 #endif
   {
-    AssertIsOnMainThread();
+    AssertIsOnOwningThread();
+  }
+
+  void
+  SetActorAlive()
+  {
+    AssertIsOnOwningThread();
+    MOZ_ASSERT(!mActorWasAlive);
+    MOZ_ASSERT(!mActorDestroyed);
+
+#ifdef DEBUG
+    mActorWasAlive = true;
+#endif
   }
 
   NS_INLINE_DECL_REFCOUNTING(ChildImpl)
 
 private:
   // Forwarded from BackgroundChild.
   static void
   Startup();
 
-  static void
-  Alloc(Endpoint<PBackgroundChild>&& aEndpoint);
-
   // Forwarded from BackgroundChild.
   static PBackgroundChild*
   GetForCurrentThread();
 
   // Forwarded from BackgroundChild.
   static bool
   GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);
 
   // Forwarded from BackgroundChild.
   static PBackgroundChild*
-  SynchronouslyCreateForCurrentThread();
+  GetOrCreateForCurrentThread();
 
   // Forwarded from BackgroundChild.
   static void
   CloseForCurrentThread();
 
   // Forwarded from BackgroundChildImpl.
   static BackgroundChildImpl::ThreadLocal*
   GetThreadLocalForCurrentThread();
@@ -415,59 +410,30 @@ private:
     auto threadLocalInfo = static_cast<ThreadLocalInfo*>(aThreadLocal);
 
     if (threadLocalInfo) {
       MOZ_ASSERT(threadLocalInfo->mClosed);
 
       if (threadLocalInfo->mActor) {
         threadLocalInfo->mActor->Close();
         threadLocalInfo->mActor->AssertActorDestroyed();
-
-        // Since the actor is created on the main thread it must only
-        // be released on the main thread as well.
-        if (!NS_IsMainThread()) {
-          ChildImpl* actor;
-          threadLocalInfo->mActor.forget(&actor);
-
-          MOZ_ALWAYS_SUCCEEDS(
-            NS_DispatchToMainThread(NewNonOwningRunnableMethod("ChildImpl::Release",
-                                                               actor, &ChildImpl::Release)));
-        }
       }
       delete threadLocalInfo;
     }
   }
 
-  static void
-  DispatchFailureCallback(nsIEventTarget* aEventTarget);
-
   // This class is reference counted.
   ~ChildImpl()
   {
-    AssertActorDestroyed();
-  }
-
-  void
-  SetBoundThread()
-  {
-    THREADSAFETY_ASSERT(!mBoundEventTarget);
-
-#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
-    mBoundEventTarget = GetCurrentThreadSerialEventTarget();
-#endif
-
-    THREADSAFETY_ASSERT(mBoundEventTarget);
+    MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
   }
 
   // Only called by IPDL.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
-
-  static already_AddRefed<nsIIPCBackgroundChildCreateCallback>
-  GetNextCallback();
 };
 
 // -----------------------------------------------------------------------------
 // ParentImpl Helper Declarations
 // -----------------------------------------------------------------------------
 
 class ParentImpl::ShutdownObserver final : public nsIObserver
 {
@@ -543,37 +509,16 @@ public:
 
 private:
   ~ForceCloseBackgroundActorsRunnable()
   { }
 
   NS_DECL_NSIRUNNABLE
 };
 
-class ParentImpl::CreateCallbackRunnable final : public Runnable
-{
-  RefPtr<CreateCallback> mCallback;
-
-public:
-  explicit CreateCallbackRunnable(CreateCallback* aCallback)
-    : Runnable("Background::ParentImpl::CreateCallbackRunnable")
-    , mCallback(aCallback)
-  {
-    AssertIsInMainProcess();
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aCallback);
-  }
-
-private:
-  ~CreateCallbackRunnable()
-  { }
-
-  NS_DECL_NSIRUNNABLE
-};
-
 class ParentImpl::ConnectActorRunnable final : public Runnable
 {
   RefPtr<ParentImpl> mActor;
   Endpoint<PBackgroundParent> mEndpoint;
   nsTArray<ParentImpl*>* mLiveActorArray;
 
 public:
   ConnectActorRunnable(ParentImpl* aActor,
@@ -594,16 +539,51 @@ private:
   ~ConnectActorRunnable()
   {
     AssertIsInMainProcess();
   }
 
   NS_DECL_NSIRUNNABLE
 };
 
+class ParentImpl::CreateActorHelper final : public Runnable
+{
+  mozilla::Monitor mMonitor;
+  RefPtr<ParentImpl> mParentActor;
+  nsCOMPtr<nsIThread> mThread;
+  nsresult mMainThreadResultCode;
+  bool mWaiting;
+
+public:
+  explicit CreateActorHelper()
+    : Runnable("Background::ParentImpl::CreateActorHelper")
+    , mMonitor("CreateActorHelper::mMonitor")
+    , mMainThreadResultCode(NS_OK)
+    , mWaiting(true)
+  {
+    AssertIsInMainProcess();
+    AssertIsNotOnMainThread();
+  }
+
+  nsresult
+  BlockAndGetResults(RefPtr<ParentImpl>& aParentActor,
+                     nsCOMPtr<nsIThread>& aThread);
+
+private:
+  ~CreateActorHelper()
+  {
+    AssertIsInMainProcess();
+  }
+
+  nsresult
+  RunOnMainThread();
+
+  NS_DECL_NSIRUNNABLE
+};
+
 class NS_NO_VTABLE ParentImpl::CreateCallback
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(CreateCallback)
 
   virtual void
   Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop) = 0;
 
@@ -632,148 +612,43 @@ public:
 
 private:
   ~ShutdownObserver()
   {
     AssertIsOnMainThread();
   }
 };
 
-class ChildImpl::CreateActorRunnable final : public Runnable
+// Must be cancelable in order to dispatch on active worker threads
+class ChildImpl::ActorCreatedRunnable final :
+  public CancelableRunnable
 {
-  nsCOMPtr<nsIEventTarget> mEventTarget;
-
-public:
-  CreateActorRunnable()
-    : Runnable("Background::ChildImpl::CreateActorRunnable")
-    , mEventTarget(NS_GetCurrentThread())
-  {
-    MOZ_ASSERT(mEventTarget);
-  }
-
-private:
-  ~CreateActorRunnable()
-  { }
-
-  NS_DECL_NSIRUNNABLE
-};
-
-class ChildImpl::ParentCreateCallback final :
-  public ParentImpl::CreateCallback
-{
-  nsCOMPtr<nsIEventTarget> mEventTarget;
+  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> mCallback;
+  RefPtr<ChildImpl> mActor;
 
 public:
-  explicit ParentCreateCallback(nsIEventTarget* aEventTarget)
-  : mEventTarget(aEventTarget)
-  {
-    AssertIsInMainProcess();
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aEventTarget);
-  }
-
-private:
-  ~ParentCreateCallback()
-  { }
-
-  virtual void
-  Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop)
-          override;
-
-  virtual void
-  Failure() override;
-};
-
-// Must be cancelable in order to dispatch on active worker threads
-class ChildImpl::AlreadyCreatedCallbackRunnable final :
-  public CancelableRunnable
-{
-public:
-  AlreadyCreatedCallbackRunnable()
-    : CancelableRunnable("Background::ChildImpl::AlreadyCreatedCallbackRunnable")
+  ActorCreatedRunnable(nsIIPCBackgroundChildCreateCallback* aCallback,
+                       ChildImpl* aActor)
+    : CancelableRunnable("Background::ChildImpl::ActorCreatedRunnable")
+    , mCallback(aCallback)
+    , mActor(aActor)
   {
     // May be created on any thread!
+    MOZ_ASSERT(aCallback);
+    MOZ_ASSERT(aActor);
   }
 
 protected:
-  virtual ~AlreadyCreatedCallbackRunnable()
+  virtual ~ActorCreatedRunnable()
   { }
 
   NS_DECL_NSIRUNNABLE
-  nsresult Cancel() override;
-};
 
-class ChildImpl::FailedCreateCallbackRunnable final : public Runnable
-{
-public:
-  FailedCreateCallbackRunnable()
-    : Runnable("Background::ChildImpl::FailedCreateCallbackRunnable")
-  {
-    // May be created on any thread!
-  }
-
-protected:
-  virtual ~FailedCreateCallbackRunnable()
-  { }
-
-  NS_DECL_NSIRUNNABLE
-};
-
-class ChildImpl::OpenChildProcessActorRunnable final : public Runnable
-{
-  RefPtr<ChildImpl> mActor;
-  Endpoint<PBackgroundChild> mEndpoint;
-
-public:
-  OpenChildProcessActorRunnable(already_AddRefed<ChildImpl>&& aActor,
-                                Endpoint<PBackgroundChild>&& aEndpoint)
-    : Runnable("Background::ChildImpl::OpenChildProcessActorRunnable")
-    , mActor(aActor)
-    , mEndpoint(Move(aEndpoint))
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(mActor);
-    MOZ_ASSERT(mEndpoint.IsValid());
-  }
-
-private:
-  ~OpenChildProcessActorRunnable()
-  {
-    if (mEndpoint.IsValid()) {
-      CRASH_IN_CHILD_PROCESS("Leaking endpoint!");
-    }
-  }
-
-  NS_DECL_NSIRUNNABLE
-};
-
-class ChildImpl::OpenMainProcessActorRunnable final : public Runnable
-{
-  RefPtr<ChildImpl> mActor;
-  RefPtr<ParentImpl> mParentActor;
-  MessageLoop* mParentMessageLoop;
-
-public:
-  OpenMainProcessActorRunnable(already_AddRefed<ChildImpl>&& aChildActor,
-                               already_AddRefed<ParentImpl> aParentActor,
-                               MessageLoop* aParentMessageLoop)
-  : Runnable("ChildImpl::OpenMainProcessActorRunnable"),
-    mActor(aChildActor), mParentActor(aParentActor),
-    mParentMessageLoop(aParentMessageLoop)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(mParentActor);
-    MOZ_ASSERT(aParentMessageLoop);
-  }
-
-private:
-  ~OpenMainProcessActorRunnable()
-  { }
-
-  NS_DECL_NSIRUNNABLE
+  nsresult
+  Cancel() override;
 };
 
 } // namespace
 
 namespace mozilla {
 namespace ipc {
 
 bool
@@ -859,19 +734,19 @@ bool
 BackgroundChild::GetOrCreateForCurrentThread(
                                  nsIIPCBackgroundChildCreateCallback* aCallback)
 {
   return ChildImpl::GetOrCreateForCurrentThread(aCallback);
 }
 
 // static
 PBackgroundChild*
-BackgroundChild::SynchronouslyCreateForCurrentThread()
+BackgroundChild::GetOrCreateForCurrentThread()
 {
-  return ChildImpl::SynchronouslyCreateForCurrentThread();
+  return ChildImpl::GetOrCreateForCurrentThread();
 }
 
 // static
 void
 BackgroundChild::CloseForCurrentThread()
 {
   ChildImpl::CloseForCurrentThread();
 }
@@ -902,27 +777,22 @@ Atomic<PRThread*> ParentImpl::sBackgroun
 MessageLoop* ParentImpl::sBackgroundThreadMessageLoop = nullptr;
 
 uint64_t ParentImpl::sLiveActorCount = 0;
 
 bool ParentImpl::sShutdownObserverRegistered = false;
 
 bool ParentImpl::sShutdownHasStarted = false;
 
-StaticAutoPtr<nsTArray<RefPtr<ParentImpl::CreateCallback>>>
-  ParentImpl::sPendingCallbacks;
-
 // -----------------------------------------------------------------------------
 // ChildImpl Static Members
 // -----------------------------------------------------------------------------
 
 unsigned int ChildImpl::sThreadLocalIndex = kBadThreadLocalIndex;
 
-StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> ChildImpl::sPendingTargets;
-
 bool ChildImpl::sShutdownHasStarted = false;
 
 // -----------------------------------------------------------------------------
 // ParentImpl Implementation
 // -----------------------------------------------------------------------------
 
 // static
 bool
@@ -1034,45 +904,71 @@ ParentImpl::Alloc(ContentParent* aConten
 
     return false;
   }
 
   return true;
 }
 
 // static
-bool
-ParentImpl::CreateActorForSameProcess(CreateCallback* aCallback)
+already_AddRefed<ChildImpl>
+ParentImpl::CreateActorForSameProcess()
 {
   AssertIsInMainProcess();
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aCallback);
+
+  RefPtr<ParentImpl> parentActor;
+  nsCOMPtr<nsIThread> backgroundThread;
+
+  if (NS_IsMainThread()) {
+    if (!sBackgroundThread && !CreateBackgroundThread()) {
+      NS_WARNING("Failed to create background thread!");
+      return nullptr;
+    }
+
+    MOZ_ASSERT(!sShutdownHasStarted);
 
-  if (!sBackgroundThread && !CreateBackgroundThread()) {
-    NS_WARNING("Failed to create background thread!");
-    return false;
+    sLiveActorCount++;
+
+    parentActor = new ParentImpl();
+    backgroundThread = sBackgroundThread.get();
+  } else {
+    RefPtr<CreateActorHelper> helper = new CreateActorHelper();
+
+    nsresult rv = helper->BlockAndGetResults(parentActor, backgroundThread);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
   }
 
-  MOZ_ASSERT(!sShutdownHasStarted);
+  RefPtr<ChildImpl> childActor = new ChildImpl();
 
-  sLiveActorCount++;
+  MessageChannel* parentChannel = parentActor->GetIPCChannel();
+  MOZ_ASSERT(parentChannel);
+
+  if (!childActor->Open(parentChannel, backgroundThread, ChildSide)) {
+    NS_WARNING("Failed to open ChildImpl!");
 
-  if (sBackgroundThreadMessageLoop) {
-    nsCOMPtr<nsIRunnable> callbackRunnable =
-      new CreateCallbackRunnable(aCallback);
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(callbackRunnable));
-    return true;
+    // Can't release it here, we will release this reference in Destroy.
+    ParentImpl* actor;
+    parentActor.forget(&actor);
+
+    actor->Destroy();
+
+    return nullptr;
   }
 
-  if (!sPendingCallbacks) {
-    sPendingCallbacks = new nsTArray<RefPtr<CreateCallback>>();
-  }
+  childActor->SetActorAlive();
+
+  // Make sure the parent knows it is same process.
+  parentActor->SetOtherProcessId(base::GetCurrentProcId());
 
-  sPendingCallbacks->AppendElement(aCallback);
-  return true;
+  // Now that Open() has succeeded transfer the ownership of the actors to IPDL.
+  Unused << parentActor.forget();
+
+  return childActor.forget();
 }
 
 // static
 bool
 ParentImpl::CreateBackgroundThread()
 {
   AssertIsInMainProcess();
   AssertIsOnMainThread();
@@ -1137,50 +1033,30 @@ ParentImpl::CreateBackgroundThread()
 }
 
 // static
 void
 ParentImpl::ShutdownBackgroundThread()
 {
   AssertIsInMainProcess();
   AssertIsOnMainThread();
-  MOZ_ASSERT_IF(!sBackgroundThread, !sBackgroundThreadMessageLoop);
   MOZ_ASSERT(sShutdownHasStarted);
   MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount);
   MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer);
 
-  if (sPendingCallbacks) {
-    if (!sPendingCallbacks->IsEmpty()) {
-      nsTArray<RefPtr<CreateCallback>> callbacks;
-      sPendingCallbacks->SwapElements(callbacks);
-
-      for (uint32_t index = 0; index < callbacks.Length(); index++) {
-        RefPtr<CreateCallback> callback;
-        callbacks[index].swap(callback);
-        MOZ_ASSERT(callback);
-
-        callback->Failure();
-      }
-    }
-
-    sPendingCallbacks = nullptr;
-  }
-
   nsCOMPtr<nsITimer> shutdownTimer = sShutdownTimer.get();
   sShutdownTimer = nullptr;
 
   if (sBackgroundThread) {
     nsCOMPtr<nsIThread> thread = sBackgroundThread.get();
     sBackgroundThread = nullptr;
 
     nsAutoPtr<nsTArray<ParentImpl*>> liveActors(sLiveActorsForBackgroundThread);
     sLiveActorsForBackgroundThread = nullptr;
 
-    sBackgroundThreadMessageLoop = nullptr;
-
     MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount);
 
     if (sLiveActorCount) {
       // We need to spin the event loop while we wait for all the actors to be
       // cleaned up. We also set a timeout to force-kill any hanging actors.
       TimerCallbackClosure closure(thread, liveActors);
 
       MOZ_ALWAYS_SUCCEEDS(
@@ -1322,31 +1198,16 @@ ParentImpl::RequestMessageLoopRunnable::
     if (!sBackgroundThread ||
         !SameCOMIdentity(mTargetThread.get(), sBackgroundThread.get())) {
       return NS_OK;
     }
 
     MOZ_ASSERT(!sBackgroundThreadMessageLoop);
     sBackgroundThreadMessageLoop = mMessageLoop;
 
-    if (sPendingCallbacks && !sPendingCallbacks->IsEmpty()) {
-      nsTArray<RefPtr<CreateCallback>> callbacks;
-      sPendingCallbacks->SwapElements(callbacks);
-
-      for (uint32_t index = 0; index < callbacks.Length(); index++) {
-        MOZ_ASSERT(callbacks[index]);
-
-        nsCOMPtr<nsIRunnable> callbackRunnable =
-          new CreateCallbackRunnable(callbacks[index]);
-        if (NS_FAILED(NS_DispatchToCurrentThread(callbackRunnable))) {
-          NS_WARNING("Failed to dispatch callback runnable!");
-        }
-      }
-    }
-
     return NS_OK;
   }
 
 #ifdef DEBUG
   {
     bool correctThread;
     MOZ_ASSERT(NS_SUCCEEDED(mTargetThread->IsOnCurrentThread(&correctThread)));
     MOZ_ASSERT(correctThread);
@@ -1409,34 +1270,16 @@ ParentImpl::ForceCloseBackgroundActorsRu
   }
 
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-ParentImpl::CreateCallbackRunnable::Run()
-{
-  AssertIsInMainProcess();
-  AssertIsOnMainThread();
-  MOZ_ASSERT(sBackgroundThreadMessageLoop);
-  MOZ_ASSERT(mCallback);
-
-  RefPtr<CreateCallback> callback;
-  mCallback.swap(callback);
-
-  RefPtr<ParentImpl> actor = new ParentImpl();
-
-  callback->Success(actor.forget(), sBackgroundThreadMessageLoop);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 ParentImpl::ConnectActorRunnable::Run()
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   // Transfer ownership to this thread. If Open() fails then we will release
   // this reference in Destroy.
   ParentImpl* actor;
@@ -1449,16 +1292,78 @@ ParentImpl::ConnectActorRunnable::Run()
     return NS_ERROR_FAILURE;
   }
 
   actor->SetLiveActorArray(mLiveActorArray);
 
   return NS_OK;
 }
 
+nsresult
+ParentImpl::
+CreateActorHelper::BlockAndGetResults(RefPtr<ParentImpl>& aParentActor,
+                                      nsCOMPtr<nsIThread>& aThread)
+{
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
+
+  mozilla::MonitorAutoLock lock(mMonitor);
+  while (mWaiting) {
+    lock.Wait();
+  }
+
+  if (NS_WARN_IF(NS_FAILED(mMainThreadResultCode))) {
+    return mMainThreadResultCode;
+  }
+
+  aParentActor = Move(mParentActor);
+  aThread = Move(mThread);
+  return NS_OK;
+}
+
+nsresult
+ParentImpl::
+CreateActorHelper::RunOnMainThread()
+{
+  AssertIsOnMainThread();
+
+  if (!sBackgroundThread && !CreateBackgroundThread()) {
+    NS_WARNING("Failed to create background thread!");
+    return NS_ERROR_FAILURE;
+  }
+
+  MOZ_ASSERT(!sShutdownHasStarted);
+
+  sLiveActorCount++;
+
+  mParentActor = new ParentImpl();
+  mThread = sBackgroundThread;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ParentImpl::
+CreateActorHelper::Run()
+{
+  AssertIsOnMainThread();
+
+  nsresult rv = RunOnMainThread();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mMainThreadResultCode = rv;
+  }
+
+  mozilla::MonitorAutoLock lock(mMonitor);
+  MOZ_ASSERT(mWaiting);
+
+  mWaiting = false;
+  lock.Notify();
+
+  return NS_OK;
+}
+
 // -----------------------------------------------------------------------------
 // ChildImpl Implementation
 // -----------------------------------------------------------------------------
 
 // static
 void
 ChildImpl::Startup()
 {
@@ -1512,40 +1417,16 @@ ChildImpl::Shutdown()
   }
 #endif
 
   DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
   MOZ_ASSERT(status == PR_SUCCESS);
 }
 
 // static
-void
-ChildImpl::Alloc(Endpoint<PBackgroundChild>&& aEndpoint)
-{
-  AssertIsInChildProcess();
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aEndpoint.IsValid());
-  MOZ_ASSERT(sPendingTargets);
-  MOZ_ASSERT(!sPendingTargets->IsEmpty());
-
-  nsCOMPtr<nsIEventTarget> eventTarget;
-  sPendingTargets->ElementAt(0).swap(eventTarget);
-
-  sPendingTargets->RemoveElementAt(0);
-
-  RefPtr<ChildImpl> actor = new ChildImpl();
-
-  nsCOMPtr<nsIRunnable> openRunnable =
-    new OpenChildProcessActorRunnable(actor.forget(), Move(aEndpoint));
-  if (NS_FAILED(eventTarget->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) {
-    MOZ_CRASH("Failed to dispatch OpenActorRunnable!");
-  }
-}
-
-// static
 PBackgroundChild*
 ChildImpl::GetForCurrentThread()
 {
   MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
 
   auto threadLocalInfo =
     static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
 
@@ -1557,125 +1438,115 @@ ChildImpl::GetForCurrentThread()
 }
 
 // static
 bool
 ChildImpl::GetOrCreateForCurrentThread(
                                  nsIIPCBackgroundChildCreateCallback* aCallback)
 {
   MOZ_ASSERT(aCallback);
-  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
-             "BackgroundChild::Startup() was never called!");
 
-  bool created = false;
-
-  auto threadLocalInfo =
-    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
-
-  if (threadLocalInfo) {
-    threadLocalInfo->mCallbacks.AppendElement(aCallback);
-  } else {
-    nsAutoPtr<ThreadLocalInfo> newInfo(new ThreadLocalInfo(aCallback));
-
-    if (PR_SetThreadPrivate(sThreadLocalIndex, newInfo) != PR_SUCCESS) {
-      CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!");
-      return false;
-    }
-
-    created = true;
-    threadLocalInfo = newInfo.forget();
+  RefPtr<ChildImpl> actor =
+    static_cast<ChildImpl*>(GetOrCreateForCurrentThread());
+  if (NS_WARN_IF(!actor)) {
+    return false;
   }
 
-  if (threadLocalInfo->mActor) {
-    // Runnable will use GetForCurrentThread() to retrieve actor again.  This
-    // allows us to avoid addref'ing on the wrong thread.
-    nsCOMPtr<nsIRunnable> runnable = new AlreadyCreatedCallbackRunnable();
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
-
-    return true;
-  }
-
-  if (!created) {
-    // We have already started the sequence for opening the actor so there's
-    // nothing else we need to do here. This callback will be called after the
-    // first callback in the schedule runnable.
-    return true;
-  }
-
-  if (NS_IsMainThread()) {
-    if (NS_WARN_IF(!OpenProtocolOnMainThread(NS_GetCurrentThread()))) {
-      return false;
-    }
-
-    return true;
-  }
-
-  RefPtr<CreateActorRunnable> runnable = new CreateActorRunnable();
-  if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
-    CRASH_IN_CHILD_PROCESS("Failed to dispatch to main thread!");
-    return false;
-  }
+  nsCOMPtr<nsIRunnable> runnable = new ActorCreatedRunnable(aCallback, actor);
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
 
   return true;
 }
 
-namespace {
+/* static */
+PBackgroundChild*
+ChildImpl::GetOrCreateForCurrentThread()
+{
+  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
+             "BackgroundChild::Startup() was never called!");
 
-class Callback final : public nsIIPCBackgroundChildCreateCallback
-{
-  bool* mDone;
+  auto threadLocalInfo =
+    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
 
-public:
-  explicit Callback(bool* aDone)
-    : mDone(aDone)
-  {
-    MOZ_ASSERT(mDone);
+  if (!threadLocalInfo) {
+    nsAutoPtr<ThreadLocalInfo> newInfo(new ThreadLocalInfo());
+
+    if (PR_SetThreadPrivate(sThreadLocalIndex, newInfo) != PR_SUCCESS) {
+      CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!");
+      return nullptr;
+    }
+
+    threadLocalInfo = newInfo.forget();
   }
 
-  NS_DECL_ISUPPORTS
-
-private:
-  ~Callback()
-  { }
-
-  virtual void
-  ActorCreated(PBackgroundChild* aActor) override
-  {
-    *mDone = true;
+  if (threadLocalInfo->mActor) {
+    return threadLocalInfo->mActor;
   }
 
-  virtual void
-  ActorFailed() override
-  {
-    *mDone = true;
-  }
-};
+  if (XRE_IsParentProcess()) {
+    RefPtr<ChildImpl> strongActor = ParentImpl::CreateActorForSameProcess();
+    if (NS_WARN_IF(!strongActor)) {
+      return nullptr;
+    }
 
-NS_IMPL_ISUPPORTS(Callback, nsIIPCBackgroundChildCreateCallback)
-
-} // anonymous namespace
+    RefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
+    strongActor.swap(actor);
 
-/* static */
-PBackgroundChild*
-ChildImpl::SynchronouslyCreateForCurrentThread()
-{
-  MOZ_ASSERT(!GetForCurrentThread());
+    return actor;
+  }
 
-  bool done = false;
-  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = new Callback(&done);
+  RefPtr<ContentChild> content = ContentChild::GetSingleton();
+  MOZ_ASSERT(content);
 
-  if (NS_WARN_IF(!GetOrCreateForCurrentThread(callback))) {
+  if (content->IsShuttingDown()) {
+    // The transport for ContentChild is shut down and can't be used to open
+    // PBackground.
     return nullptr;
   }
 
-  if (NS_WARN_IF(!SpinEventLoopUntil([&]() { return done; }))) {
+  Endpoint<PBackgroundParent> parent;
+  Endpoint<PBackgroundChild> child;
+  nsresult rv;
+  rv = PBackground::CreateEndpoints(content->OtherPid(),
+                                    base::GetCurrentProcId(),
+                                    &parent, &child);
+  if (NS_FAILED(rv)) {
+    MOZ_CRASH("Failed to create top level actor!");
+    return nullptr;
+  }
+
+  RefPtr<ChildImpl> strongActor = new ChildImpl();
+
+  if (!child.Bind(strongActor)) {
+    CRASH_IN_CHILD_PROCESS("Failed to bind ChildImpl!");
+
     return nullptr;
   }
 
-  return GetForCurrentThread();
+  strongActor->SetActorAlive();
+
+  if (NS_IsMainThread()) {
+    if (!content->SendInitBackground(Move(parent))) {
+      MOZ_CRASH("Failed to create top level actor!");
+      return nullptr;
+    }
+  } else {
+    nsCOMPtr<nsIRunnable> runnable =
+      NewRunnableMethod<Endpoint<PBackgroundParent>&&>(
+        "dom::ContentChild::SendInitBackground",
+        content,
+        &ContentChild::SendInitBackground,
+        Move(parent));
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+  }
+
+  RefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
+  strongActor.swap(actor);
+
+  return actor;
 }
 
 // static
 void
 ChildImpl::CloseForCurrentThread()
 {
   if (sThreadLocalIndex == kBadThreadLocalIndex) {
     return;
@@ -1715,332 +1586,41 @@ ChildImpl::GetThreadLocalForCurrentThrea
   if (!threadLocalInfo->mConsumerThreadLocal) {
     threadLocalInfo->mConsumerThreadLocal =
       new BackgroundChildImpl::ThreadLocal();
   }
 
   return threadLocalInfo->mConsumerThreadLocal;
 }
 
-// static
-already_AddRefed<nsIIPCBackgroundChildCreateCallback>
-ChildImpl::GetNextCallback()
+NS_IMETHODIMP
+ChildImpl::ActorCreatedRunnable::Run()
 {
   // May run on any thread!
 
-  auto threadLocalInfo =
-    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
-  MOZ_ASSERT(threadLocalInfo);
-
-  if (threadLocalInfo->mCallbacks.IsEmpty()) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback;
-  threadLocalInfo->mCallbacks[0].swap(callback);
-
-  threadLocalInfo->mCallbacks.RemoveElementAt(0);
-
-  return callback.forget();
-}
+  MOZ_ASSERT(mCallback);
+  MOZ_ASSERT(mActor);
 
-NS_IMETHODIMP
-ChildImpl::AlreadyCreatedCallbackRunnable::Run()
-{
-  // May run on any thread!
-
-  // Report the current actor back in the callback.
-  PBackgroundChild* actor = ChildImpl::GetForCurrentThread();
-
-  // If the current actor is null, do not create a new actor here.  This likely
-  // means we are in the process of cleaning up a worker thread and do not want
-  // a new actor created.  Unfortunately we cannot report back to the callback
-  // because the thread local is gone at this point.  Instead simply do nothing
-  // and return.
-  if (NS_WARN_IF(!actor)) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
-    ChildImpl::GetNextCallback();
-  while (callback) {
-    callback->ActorCreated(actor);
-    callback = ChildImpl::GetNextCallback();
-  }
+  mCallback->ActorCreated(mActor);
 
   return NS_OK;
 }
 
 nsresult
-ChildImpl::AlreadyCreatedCallbackRunnable::Cancel()
+ChildImpl::ActorCreatedRunnable::Cancel()
 {
   // These are IPC infrastructure objects and need to run unconditionally.
   Run();
   return NS_OK;
 }
 
-NS_IMETHODIMP
-ChildImpl::FailedCreateCallbackRunnable::Run()
-{
-  // May run on any thread!
-
-  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
-    ChildImpl::GetNextCallback();
-  while (callback) {
-    callback->ActorFailed();
-    callback = ChildImpl::GetNextCallback();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ChildImpl::OpenChildProcessActorRunnable::Run()
-{
-  // May be run on any thread!
-
-  AssertIsInChildProcess();
-  MOZ_ASSERT(mActor);
-  MOZ_ASSERT(mEndpoint.IsValid());
-
-  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
-    ChildImpl::GetNextCallback();
-  MOZ_ASSERT(callback,
-             "There should be at least one callback when first creating the "
-             "actor!");
-
-  RefPtr<ChildImpl> strongActor;
-  mActor.swap(strongActor);
-  Endpoint<PBackgroundChild> endpoint = Move(mEndpoint);
-
-  if (!endpoint.Bind(strongActor)) {
-    CRASH_IN_CHILD_PROCESS("Failed to bind ChildImpl!");
-
-    while (callback) {
-      callback->ActorFailed();
-      callback = ChildImpl::GetNextCallback();
-    }
-
-    return NS_OK;
-  }
-
-  // Now that Open() has succeeded transfer the ownership of the actor to IPDL.
-  auto threadLocalInfo =
-    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
-
-  MOZ_ASSERT(threadLocalInfo);
-  MOZ_ASSERT(!threadLocalInfo->mActor);
-
-  RefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
-  strongActor.swap(actor);
-
-  actor->SetBoundThread();
-
-  while (callback) {
-    callback->ActorCreated(actor);
-    callback = ChildImpl::GetNextCallback();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ChildImpl::OpenMainProcessActorRunnable::Run()
-{
-  // May run on any thread!
-
-  AssertIsInMainProcess();
-  MOZ_ASSERT(mActor);
-  MOZ_ASSERT(mParentActor);
-  MOZ_ASSERT(mParentMessageLoop);
-
-  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
-    ChildImpl::GetNextCallback();
-  MOZ_ASSERT(callback,
-             "There should be at least one callback when first creating the "
-             "actor!");
-
-  RefPtr<ChildImpl> strongChildActor;
-  mActor.swap(strongChildActor);
-
-  RefPtr<ParentImpl> parentActor;
-  mParentActor.swap(parentActor);
-
-  MessageChannel* parentChannel = parentActor->GetIPCChannel();
-  MOZ_ASSERT(parentChannel);
-
-  if (!strongChildActor->Open(parentChannel, mParentMessageLoop, ChildSide)) {
-    NS_WARNING("Failed to open ChildImpl!");
-
-    parentActor->Destroy();
-
-    while (callback) {
-      callback->ActorFailed();
-      callback = ChildImpl::GetNextCallback();
-    }
-
-    return NS_OK;
-  }
-
-  // Make sure the parent knows it is same process.
-  parentActor->SetOtherProcessId(base::GetCurrentProcId());
-
-  // Now that Open() has succeeded transfer the ownership of the actors to IPDL.
-  Unused << parentActor.forget();
-
-  auto threadLocalInfo =
-    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
-
-  MOZ_ASSERT(threadLocalInfo);
-  MOZ_ASSERT(!threadLocalInfo->mActor);
-
-  RefPtr<ChildImpl>& childActor = threadLocalInfo->mActor;
-  strongChildActor.swap(childActor);
-
-  childActor->SetBoundThread();
-
-  while (callback) {
-    callback->ActorCreated(childActor);
-    callback = ChildImpl::GetNextCallback();
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-ChildImpl::CreateActorRunnable::Run()
-{
-  AssertIsOnMainThread();
-
-  if (!OpenProtocolOnMainThread(mEventTarget)) {
-    NS_WARNING("OpenProtocolOnMainThread failed!");
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-void
-ChildImpl::ParentCreateCallback::Success(
-                                      already_AddRefed<ParentImpl> aParentActor,
-                                      MessageLoop* aParentMessageLoop)
-{
-  AssertIsInMainProcess();
-  AssertIsOnMainThread();
-
-  RefPtr<ParentImpl> parentActor = aParentActor;
-  MOZ_ASSERT(parentActor);
-  MOZ_ASSERT(aParentMessageLoop);
-  MOZ_ASSERT(mEventTarget);
-
-  RefPtr<ChildImpl> childActor = new ChildImpl();
-
-  nsCOMPtr<nsIEventTarget> target;
-  mEventTarget.swap(target);
-
-  nsCOMPtr<nsIRunnable> openRunnable =
-    new OpenMainProcessActorRunnable(childActor.forget(), parentActor.forget(),
-                                     aParentMessageLoop);
-  if (NS_FAILED(target->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) {
-    NS_WARNING("Failed to dispatch open runnable!");
-  }
-}
-
-void
-ChildImpl::ParentCreateCallback::Failure()
-{
-  AssertIsInMainProcess();
-  AssertIsOnMainThread();
-  MOZ_ASSERT(mEventTarget);
-
-  nsCOMPtr<nsIEventTarget> target;
-  mEventTarget.swap(target);
-
-  DispatchFailureCallback(target);
-}
-
-// static
-bool
-ChildImpl::OpenProtocolOnMainThread(nsIEventTarget* aEventTarget)
-{
-  AssertIsOnMainThread();
-  MOZ_ASSERT(aEventTarget);
-
-  if (sShutdownHasStarted) {
-    MOZ_CRASH("Called BackgroundChild::GetOrCreateForCurrentThread after "
-              "shutdown has started!");
-  }
-
-  if (XRE_IsParentProcess()) {
-    RefPtr<ParentImpl::CreateCallback> parentCallback =
-      new ParentCreateCallback(aEventTarget);
-
-    if (!ParentImpl::CreateActorForSameProcess(parentCallback)) {
-      NS_WARNING("BackgroundParent::CreateActor() failed!");
-      DispatchFailureCallback(aEventTarget);
-      return false;
-    }
-
-    return true;
-  }
-
-  ContentChild* content = ContentChild::GetSingleton();
-  MOZ_ASSERT(content);
-
-  if (content->IsShuttingDown()) {
-    // The transport for ContentChild is shut down and can't be used to open
-    // PBackground.
-    DispatchFailureCallback(aEventTarget);
-    return false;
-  }
-
-  Endpoint<PBackgroundParent> parent;
-  Endpoint<PBackgroundChild> child;
-  nsresult rv;
-  rv = PBackground::CreateEndpoints(content->OtherPid(),
-                                    base::GetCurrentProcId(),
-                                    &parent, &child);
-  if (NS_FAILED(rv)) {
-    MOZ_CRASH("Failed to create top level actor!");
-    return false;
-  }
-
-  if (!content->SendInitBackground(Move(parent))) {
-    MOZ_CRASH("Failed to create top level actor!");
-    return false;
-  }
-
-  if (!sPendingTargets) {
-    sPendingTargets = new nsTArray<nsCOMPtr<nsIEventTarget>>(1);
-    ClearOnShutdown(&sPendingTargets);
-  }
-
-  sPendingTargets->AppendElement(aEventTarget);
-
-  Alloc(Move(child));
-
-  return true;
-}
-
-// static
-void
-ChildImpl::DispatchFailureCallback(nsIEventTarget* aEventTarget)
-{
-  MOZ_ASSERT(aEventTarget);
-
-  nsCOMPtr<nsIRunnable> callbackRunnable = new FailedCreateCallbackRunnable();
-  if (NS_FAILED(aEventTarget->Dispatch(callbackRunnable, NS_DISPATCH_NORMAL))) {
-    NS_WARNING("Failed to dispatch CreateCallbackRunnable!");
-  }
-}
-
 void
 ChildImpl::ActorDestroy(ActorDestroyReason aWhy)
 {
-  AssertIsOnBoundThread();
+  AssertIsOnOwningThread();
 
 #ifdef DEBUG
   MOZ_ASSERT(!mActorDestroyed);
   mActorDestroyed = true;
 #endif
 
   BackgroundChildImpl::ActorDestroy(aWhy);
 }
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -763,17 +763,17 @@ MessageChannel::Open(Transport* aTranspo
 
     ProcessLink *link = new ProcessLink(this);
     link->Open(aTransport, aIOLoop, aSide); // :TODO: n.b.: sets mChild
     mLink = link;
     return true;
 }
 
 bool
-MessageChannel::Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side aSide)
+MessageChannel::Open(MessageChannel *aTargetChan, nsIEventTarget *aEventTarget, Side aSide)
 {
     // Opens a connection to another thread in the same process.
 
     //  This handshake proceeds as follows:
     //  - Let A be the thread initiating the process (either child or parent)
     //    and B be the other thread.
     //  - A spawns thread for B, obtaining B's message loop
     //  - A creates ProtocolChild and ProtocolParent instances.
@@ -796,22 +796,22 @@ MessageChannel::Open(MessageChannel *aTa
       case ParentSide: oppSide = ChildSide; break;
       case UnknownSide: break;
     }
 
     mMonitor = new RefCountedMonitor();
 
     MonitorAutoLock lock(*mMonitor);
     mChannelState = ChannelOpening;
-    aTargetLoop->PostTask(NewNonOwningRunnableMethod<MessageChannel*, Side>(
+    MOZ_ALWAYS_SUCCEEDS(aEventTarget->Dispatch(NewNonOwningRunnableMethod<MessageChannel*, Side>(
       "ipc::MessageChannel::OnOpenAsSlave",
       aTargetChan,
       &MessageChannel::OnOpenAsSlave,
       this,
-      oppSide));
+      oppSide)));
 
     while (ChannelOpening == mChannelState)
         mMonitor->Wait();
     MOZ_RELEASE_ASSERT(ChannelConnected == mChannelState, "not connected when awoken");
     return (ChannelConnected == mChannelState);
 }
 
 void
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -30,16 +30,18 @@
 
 #include <deque>
 #include <functional>
 #include <map>
 #include <math.h>
 #include <stack>
 #include <vector>
 
+class nsIEventTarget;
+
 namespace mozilla {
 namespace ipc {
 
 class MessageChannel;
 class IToplevelProtocol;
 
 class RefCountedMonitor : public Monitor
 {
@@ -141,17 +143,17 @@ class MessageChannel : HasResultCodes, M
     // "Open" a connection to another thread in the same process.
     //
     // Returns true if the transport layer was successfully connected,
     // i.e., mChannelState == ChannelConnected.
     //
     // For more details on the process of opening a channel between
     // threads, see the extended comment on this function
     // in MessageChannel.cpp.
-    bool Open(MessageChannel *aTargetChan, MessageLoop *aTargetLoop, Side aSide);
+    bool Open(MessageChannel *aTargetChan, nsIEventTarget *aEventTarget, Side aSide);
 
     // Close the underlying transport channel.
     void Close();
 
     // Force the channel to behave as if a channel error occurred. Valid
     // for process links only, not thread links.
     void CloseWithError();
 
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -639,17 +639,26 @@ IToplevelProtocol::Open(mozilla::ipc::Tr
 }
 
 bool
 IToplevelProtocol::Open(MessageChannel* aChannel,
                         MessageLoop* aMessageLoop,
                         mozilla::ipc::Side aSide)
 {
   SetOtherProcessId(base::GetCurrentProcId());
-  return GetIPCChannel()->Open(aChannel, aMessageLoop, aSide);
+  return GetIPCChannel()->Open(aChannel, aMessageLoop->SerialEventTarget(), aSide);
+}
+
+bool
+IToplevelProtocol::Open(MessageChannel* aChannel,
+                        nsIEventTarget* aEventTarget,
+                        mozilla::ipc::Side aSide)
+{
+  SetOtherProcessId(base::GetCurrentProcId());
+  return GetIPCChannel()->Open(aChannel, aEventTarget, aSide);
 }
 
 void
 IToplevelProtocol::Close()
 {
   GetIPCChannel()->Close();
 }
 
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -289,16 +289,20 @@ public:
               base::ProcessId aOtherPid,
               MessageLoop* aThread = nullptr,
               mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
 
     bool Open(MessageChannel* aChannel,
               MessageLoop* aMessageLoop,
               mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
 
+    bool Open(MessageChannel* aChannel,
+              nsIEventTarget* aEventTarget,
+              mozilla::ipc::Side aSide = mozilla::ipc::UnknownSide);
+
     void Close();
 
     void SetReplyTimeoutMs(int32_t aTimeoutMs);
 
     virtual int32_t Register(IProtocol*);
     virtual int32_t RegisterID(IProtocol*, int32_t);
     virtual IProtocol* Lookup(int32_t);
     virtual void Unregister(int32_t);
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -3818,36 +3818,34 @@ HasLanguageDependentCasing(JSLinearStrin
 bool
 js::intl_toLocaleLowerCase(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(args[0].isString());
     MOZ_ASSERT(args[1].isString());
 
-    RootedLinearString linear(cx, args[0].toString()->ensureLinear(cx));
-    if (!linear)
-        return false;
+    RootedString string(cx, args[0].toString());
 
     RootedLinearString locale(cx, args[1].toString()->ensureLinear(cx));
     if (!locale)
         return false;
 
     // Call String.prototype.toLowerCase() for language independent casing.
     if (!HasLanguageDependentCasing(locale)) {
-        JSString* str = js::StringToLowerCase(cx, linear);
+        JSString* str = js::StringToLowerCase(cx, string);
         if (!str)
             return false;
 
         args.rval().setString(str);
         return true;
     }
 
     AutoStableStringChars inputChars(cx);
-    if (!inputChars.initTwoByte(cx, linear))
+    if (!inputChars.initTwoByte(cx, string))
         return false;
     mozilla::Range<const char16_t> input = inputChars.twoByteRange();
 
     // Maximum case mapping length is three characters.
     static_assert(JSString::MAX_LENGTH < INT32_MAX / 3,
                   "Case conversion doesn't overflow int32_t indices");
 
     JSString* str = Call(cx, [&input, &locale](UChar* chars, int32_t size, UErrorCode* status) {
@@ -3864,36 +3862,34 @@ js::intl_toLocaleLowerCase(JSContext* cx
 bool
 js::intl_toLocaleUpperCase(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(args[0].isString());
     MOZ_ASSERT(args[1].isString());
 
-    RootedLinearString linear(cx, args[0].toString()->ensureLinear(cx));
-    if (!linear)
-        return false;
+    RootedString string(cx, args[0].toString());
 
     RootedLinearString locale(cx, args[1].toString()->ensureLinear(cx));
     if (!locale)
         return false;
 
     // Call String.prototype.toUpperCase() for language independent casing.
     if (!HasLanguageDependentCasing(locale)) {
-        JSString* str = js::StringToUpperCase(cx, linear);
+        JSString* str = js::StringToUpperCase(cx, string);
         if (!str)
             return false;
 
         args.rval().setString(str);
         return true;
     }
 
     AutoStableStringChars inputChars(cx);
-    if (!inputChars.initTwoByte(cx, linear))
+    if (!inputChars.initTwoByte(cx, string))
         return false;
     mozilla::Range<const char16_t> input = inputChars.twoByteRange();
 
     // Maximum case mapping length is three characters.
     static_assert(JSString::MAX_LENGTH < INT32_MAX / 3,
                   "Case conversion doesn't overflow int32_t indices");
 
     JSString* str = Call(cx, [&input, &locale](UChar* chars, int32_t size, UErrorCode* status) {
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2293,18 +2293,18 @@ BytecodeEmitter::emitCheck(ptrdiff_t del
     return true;
 }
 
 void
 BytecodeEmitter::updateDepth(ptrdiff_t target)
 {
     jsbytecode* pc = code(target);
 
-    int nuses = StackUses(nullptr, pc);
-    int ndefs = StackDefs(nullptr, pc);
+    int nuses = StackUses(pc);
+    int ndefs = StackDefs(pc);
 
     stackDepth -= nuses;
     MOZ_ASSERT(stackDepth >= 0);
     stackDepth += ndefs;
 
     if ((uint32_t)stackDepth > maxStackDepth)
         maxStackDepth = stackDepth;
 }
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1086,17 +1086,17 @@ InitFromBailout(JSContext* cx, HandleScr
             // JSOP_NEWOBJECT, which always returns the same type for a
             // particular script/pc location.
             BaselineICEntry& icEntry = baselineScript->icEntryFromPCOffset(pcOff);
             ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
             if (fallbackStub->isMonitoredFallback())
                 enterMonitorChain = true;
         }
 
-        uint32_t numUses = js::StackUses(script, pc);
+        uint32_t numUses = js::StackUses(pc);
 
         if (resumeAfter && !enterMonitorChain)
             pc = GetNextPc(pc);
 
         builder.setResumePC(pc);
         builder.setResumeFramePtr(prevFramePtr);
 
         if (enterMonitorChain) {
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -73,18 +73,18 @@ BytecodeAnalysis::init(TempAllocator& al
             continue;
 
         unsigned stackDepth = infos_[offset].stackDepth;
 #ifdef DEBUG
         for (jsbytecode* chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
             MOZ_ASSERT(!infos_[script_->pcToOffset(chkpc)].initialized);
 #endif
 
-        unsigned nuses = GetUseCount(script_, offset);
-        unsigned ndefs = GetDefCount(script_, offset);
+        unsigned nuses = GetUseCount(pc);
+        unsigned ndefs = GetDefCount(pc);
 
         MOZ_ASSERT(stackDepth >= nuses);
         stackDepth -= nuses;
         stackDepth += ndefs;
 
         // If stack depth exceeds max allowed by analysis, fail fast.
         MOZ_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH);
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -966,17 +966,17 @@ CodeGenerator::visitObjectGroupDispatch(
         masm.jump(lastBlock->label());
 }
 
 void
 CodeGenerator::visitBooleanToString(LBooleanToString* lir)
 {
     Register input = ToRegister(lir->input());
     Register output = ToRegister(lir->output());
-    const JSAtomState& names = GetJitContext()->runtime->names();
+    const JSAtomState& names = gen->runtime->names();
     Label true_, done;
 
     masm.branchTest32(Assembler::NonZero, input, input, &true_);
     masm.movePtr(ImmGCPtr(names.false_), output);
     masm.jump(&done);
 
     masm.bind(&true_);
     masm.movePtr(ImmGCPtr(names.true_), output);
@@ -985,17 +985,17 @@ CodeGenerator::visitBooleanToString(LBoo
 }
 
 void
 CodeGenerator::emitIntToString(Register input, Register output, Label* ool)
 {
     masm.branch32(Assembler::AboveOrEqual, input, Imm32(StaticStrings::INT_STATIC_LIMIT), ool);
 
     // Fast path for small integers.
-    masm.movePtr(ImmPtr(&GetJitContext()->runtime->staticStrings().intStaticTable), output);
+    masm.movePtr(ImmPtr(&gen->runtime->staticStrings().intStaticTable), output);
     masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
 }
 
 typedef JSFlatString* (*IntToStringFn)(JSContext*, int);
 static const VMFunction IntToStringInfo =
     FunctionInfo<IntToStringFn>(Int32ToString<CanGC>, "Int32ToString");
 
 void
@@ -1043,17 +1043,17 @@ CodeGenerator::visitValueToString(LValue
     ValueOperand input = ToValue(lir, LValueToString::Input);
     Register output = ToRegister(lir->output());
 
     OutOfLineCode* ool = oolCallVM(PrimitiveToStringInfo, lir, ArgList(input),
                                    StoreRegisterTo(output));
 
     Label done;
     Register tag = masm.splitTagForTest(input);
-    const JSAtomState& names = GetJitContext()->runtime->names();
+    const JSAtomState& names = gen->runtime->names();
 
     // String
     if (lir->mir()->input()->mightBeType(MIRType::String)) {
         Label notString;
         masm.branchTestString(Assembler::NotEqual, tag, &notString);
         masm.unboxString(input, output);
         masm.jump(&done);
         masm.bind(&notString);
@@ -3704,34 +3704,34 @@ EmitStoreBufferCheckForConstant(MacroAss
     // Add the cell to the set.
     masm.or32(Imm32(mask), Address(cells, offset));
     masm.jump(exit);
 
     regs.add(temp);
 }
 
 static void
-EmitPostWriteBarrier(MacroAssembler& masm, Register objreg, JSObject* maybeConstant, bool isGlobal,
-                     AllocatableGeneralRegisterSet& regs)
+EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime, Register objreg,
+                     JSObject* maybeConstant, bool isGlobal, AllocatableGeneralRegisterSet& regs)
 {
     MOZ_ASSERT_IF(isGlobal, maybeConstant);
 
     Label callVM;
     Label exit;
 
     // We already have a fast path to check whether a global is in the store
     // buffer.
     if (!isGlobal && maybeConstant)
         EmitStoreBufferCheckForConstant(masm, maybeConstant, regs, &exit, &callVM);
 
     // Call into the VM to barrier the write.
     masm.bind(&callVM);
 
     Register runtimereg = regs.takeAny();
-    masm.mov(ImmPtr(GetJitContext()->runtime), runtimereg);
+    masm.mov(ImmPtr(runtime), runtimereg);
 
     void (*fun)(JSRuntime*, JSObject*) = isGlobal ? PostGlobalWriteBarrier : PostWriteBarrier;
     masm.setupUnalignedABICall(regs.takeAny());
     masm.passABIArg(runtimereg);
     masm.passABIArg(objreg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, fun));
 
     masm.bind(&exit);
@@ -3750,25 +3750,25 @@ CodeGenerator::emitPostWriteBarrier(cons
         isGlobal = isGlobalObject(object);
         objreg = regs.takeAny();
         masm.movePtr(ImmGCPtr(object), objreg);
     } else {
         objreg = ToRegister(obj);
         regs.takeUnchecked(objreg);
     }
 
-    EmitPostWriteBarrier(masm, objreg, object, isGlobal, regs);
+    EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs);
 }
 
 void
 CodeGenerator::emitPostWriteBarrier(Register objreg)
 {
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
     regs.takeUnchecked(objreg);
-    EmitPostWriteBarrier(masm, objreg, nullptr, false, regs);
+    EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs);
 }
 
 void
 CodeGenerator::visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier* ool)
 {
     saveLiveVolatile(ool->lir());
     const LAllocation* obj = ool->object();
     emitPostWriteBarrier(obj);
@@ -3911,17 +3911,17 @@ CodeGenerator::visitOutOfLineCallPostWri
         objreg = regs.takeAny();
         masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg);
     } else {
         regs.takeUnchecked(objreg);
     }
 
     Register runtimereg = regs.takeAny();
     masm.setupUnalignedABICall(runtimereg);
-    masm.mov(ImmPtr(GetJitContext()->runtime), runtimereg);
+    masm.mov(ImmPtr(gen->runtime), runtimereg);
     masm.passABIArg(runtimereg);
     masm.passABIArg(objreg);
     masm.passABIArg(indexreg);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, (PostWriteElementBarrier<IndexInBounds::Maybe>)));
 
     restoreLiveVolatile(ool->lir());
 
     masm.jump(ool->rejoin());
@@ -4923,17 +4923,17 @@ CodeGenerator::visitCheckOverRecursed(LC
     // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
 
     CheckOverRecursedFailure* ool = new(alloc()) CheckOverRecursedFailure(lir);
     addOutOfLineCode(ool, lir->mir());
 
     Register temp = ToRegister(lir->temp());
 
     // Conditional forward (unlikely) branch to failure.
-    const void* contextAddr = GetJitContext()->compartment->zone()->addressOfJSContext();
+    const void* contextAddr = gen->compartment->zone()->addressOfJSContext();
     masm.loadPtr(AbsoluteAddress(contextAddr), temp);
     masm.branchStackPtrRhs(Assembler::AboveOrEqual,
                            Address(temp, offsetof(JSContext, jitStackLimit)), ool->entry());
     masm.bind(ool->rejoin());
 }
 
 typedef bool (*DefVarFn)(JSContext*, HandlePropertyName, unsigned, HandleObject);
 static const VMFunction DefVarInfo = FunctionInfo<DefVarFn>(DefVar, "DefVar");
@@ -5002,17 +5002,17 @@ CodeGenerator::visitCheckOverRecursedFai
 }
 
 IonScriptCounts*
 CodeGenerator::maybeCreateScriptCounts()
 {
     // If scripts are being profiled, create a new IonScriptCounts for the
     // profiling data, which will be attached to the associated JSScript or
     // wasm module after code generation finishes.
-    if (!GetJitContext()->hasProfilingScripts())
+    if (!gen->hasProfilingScripts())
         return nullptr;
 
     // This test inhibits IonScriptCount creation for wasm code which is
     // currently incompatible with wasm codegen for two reasons: (1) wasm code
     // must be serializable and script count codegen bakes in absolute
     // addresses, (2) wasm code does not have a JSScript with which to associate
     // code coverage data.
     JSScript* script = gen->info().script();
@@ -5305,17 +5305,17 @@ CodeGenerator::emitDebugForceBailing(LIn
     if (!lir->snapshot())
         return;
     if (lir->isStart())
         return;
     if (lir->isOsiPoint())
         return;
 
     masm.comment("emitDebugForceBailing");
-    const void* bailAfterAddr = GetJitContext()->compartment->zone()->addressOfIonBailAfter();
+    const void* bailAfterAddr = gen->compartment->zone()->addressOfIonBailAfter();
 
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
 
     Label done, notBail, bail;
     masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterAddr), Imm32(0), &done);
     {
         Register temp = regs.takeAny();
 
@@ -7700,17 +7700,17 @@ CodeGenerator::visitSubstr(LSubstr* lir)
     OutOfLineCode* ool = oolCallVM(SubstringKernelInfo, lir,
                                    ArgList(string, begin, length),
                                    StoreRegisterTo(output));
     Label* slowPath = ool->entry();
     Label* done = ool->rejoin();
 
     // Zero length, return emptystring.
     masm.branchTest32(Assembler::NonZero, length, length, &nonZero);
-    const JSAtomState& names = GetJitContext()->runtime->names();
+    const JSAtomState& names = gen->runtime->names();
     masm.movePtr(ImmGCPtr(names.empty), output);
     masm.jump(done);
 
     // Use slow path for ropes.
     masm.bind(&nonZero);
     masm.branchIfRopeOrExternal(string, temp, slowPath);
 
     // Handle inlined strings by creating a FatInlineString.
@@ -8081,17 +8081,17 @@ CodeGenerator::visitFromCharCode(LFromCh
     Register output = ToRegister(lir->output());
 
     OutOfLineCode* ool = oolCallVM(StringFromCharCodeInfo, lir, ArgList(code), StoreRegisterTo(output));
 
     // OOL path if code >= UNIT_STATIC_LIMIT.
     masm.branch32(Assembler::AboveOrEqual, code, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
                   ool->entry());
 
-    masm.movePtr(ImmPtr(&GetJitContext()->runtime->staticStrings().unitStaticTable), output);
+    masm.movePtr(ImmPtr(&gen->runtime->staticStrings().unitStaticTable), output);
     masm.loadPtr(BaseIndex(output, code, ScalePointer), output);
 
     masm.bind(ool->rejoin());
 }
 
 typedef JSString* (*StringFromCodePointFn)(JSContext*, int32_t);
 static const VMFunction StringFromCodePointInfo =
     FunctionInfo<StringFromCodePointFn>(jit::StringFromCodePoint, "StringFromCodePoint");
@@ -8112,17 +8112,17 @@ CodeGenerator::visitFromCodePoint(LFromC
     Label isTwoByte;
     Label* done = ool->rejoin();
 
     static_assert(StaticStrings::UNIT_STATIC_LIMIT -1 == JSString::MAX_LATIN1_CHAR,
                   "Latin-1 strings can be loaded from static strings");
     masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
                   &isTwoByte);
     {
-        masm.movePtr(ImmPtr(&GetJitContext()->runtime->staticStrings().unitStaticTable), output);
+        masm.movePtr(ImmPtr(&gen->runtime->staticStrings().unitStaticTable), output);
         masm.loadPtr(BaseIndex(output, codePoint, ScalePointer), output);
         masm.jump(done);
     }
     masm.bind(&isTwoByte);
     {
         // Use a bailout if the input is not a valid code point, because
         // MFromCodePoint is movable and it'd be observable when a moved
         // fromCodePoint throws an exception before its actual call site.
@@ -8182,16 +8182,35 @@ CodeGenerator::visitFromCodePoint(LFromC
             // Null-terminate.
             masm.store16(Imm32(0), Address(temp1, 2 * sizeof(char16_t)));
         }
     }
 
     masm.bind(done);
 }
 
+typedef JSString* (*StringToLowerCaseFn)(JSContext*, HandleString);
+static const VMFunction StringToLowerCaseInfo =
+    FunctionInfo<StringToLowerCaseFn>(js::StringToLowerCase, "StringToLowerCase");
+
+typedef JSString* (*StringToUpperCaseFn)(JSContext*, HandleString);
+static const VMFunction StringToUpperCaseInfo =
+    FunctionInfo<StringToUpperCaseFn>(js::StringToUpperCase, "StringToUpperCase");
+
+void
+CodeGenerator::visitStringConvertCase(LStringConvertCase* lir)
+{
+    pushArg(ToRegister(lir->string()));
+
+    if (lir->mir()->mode() == MStringConvertCase::LowerCase)
+        callVM(StringToLowerCaseInfo, lir);
+    else
+        callVM(StringToUpperCaseInfo, lir);
+}
+
 void
 CodeGenerator::visitSinCos(LSinCos *lir)
 {
     Register temp = ToRegister(lir->temp());
     Register params = ToRegister(lir->temp2());
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister outputSin = ToFloatRegister(lir->outputSin());
     FloatRegister outputCos = ToFloatRegister(lir->outputCos());
@@ -10579,17 +10598,17 @@ class OutOfLineTypeOfV : public OutOfLin
 
 void
 CodeGenerator::visitTypeOfV(LTypeOfV* lir)
 {
     const ValueOperand value = ToValue(lir, LTypeOfV::Input);
     Register output = ToRegister(lir->output());
     Register tag = masm.splitTagForTest(value);
 
-    const JSAtomState& names = GetJitContext()->runtime->names();
+    const JSAtomState& names = gen->runtime->names();
     Label done;
 
     MDefinition* input = lir->mir()->input();
 
     bool testObject = input->mightBeType(MIRType::Object);
     bool testNumber = input->mightBeType(MIRType::Int32) || input->mightBeType(MIRType::Double);
     bool testBoolean = input->mightBeType(MIRType::Boolean);
     bool testUndefined = input->mightBeType(MIRType::Undefined);
@@ -10700,17 +10719,17 @@ CodeGenerator::visitTypeOfV(LTypeOfV* li
     if (ool)
         masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool)
 {
     LTypeOfV* ins = ool->ins();
-    const JSAtomState& names = GetJitContext()->runtime->names();
+    const JSAtomState& names = gen->runtime->names();
 
     ValueOperand input = ToValue(ins, LTypeOfV::Input);
     Register temp = ToTempUnboxRegister(ins->tempToUnbox());
     Register output = ToRegister(ins->output());
 
     Register obj = masm.extractObject(input, temp);
 
     Label slowCheck, isObject, isCallable, isUndefined, done;
@@ -10728,17 +10747,17 @@ CodeGenerator::visitOutOfLineTypeOfV(Out
     masm.movePtr(ImmGCPtr(names.object), output);
     masm.jump(ool->rejoin());
 
     masm.bind(&slowCheck);
 
     saveVolatile(output);
     masm.setupUnalignedABICall(output);
     masm.passABIArg(obj);
-    masm.movePtr(ImmPtr(GetJitContext()->runtime), output);
+    masm.movePtr(ImmPtr(gen->runtime), output);
     masm.passABIArg(output);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, TypeOfObject));
     masm.storeCallWordResult(output);
     restoreVolatile(output);
 
     masm.jump(ool->rejoin());
 }
 
@@ -12394,17 +12413,17 @@ CodeGenerator::visitInterruptCheck(LInte
         masm.bind(ool->rejoin());
         return;
     }
 
     OutOfLineCode* ool = oolCallVM(InterruptCheckInfo, lir, ArgList(), StoreNothing());
 
     Register temp = ToRegister(lir->temp());
 
-    const void* contextAddr = GetJitContext()->compartment->zone()->addressOfJSContext();
+    const void* contextAddr = gen->compartment->zone()->addressOfJSContext();
     masm.loadPtr(AbsoluteAddress(contextAddr), temp);
     masm.branch32(Assembler::NotEqual, Address(temp, offsetof(JSContext, interrupt_)),
                   Imm32(0), ool->entry());
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitWasmTrap(LWasmTrap* lir)
@@ -12860,17 +12879,17 @@ CodeGenerator::visitFinishBoundFunctionI
         masm.bind(&guessed);
 
         // Unnamed class expression don't have a name property. To avoid
         // looking it up from the prototype chain, we take the slow path here.
         masm.branchFunctionKind(Assembler::Equal, JSFunction::ClassConstructor, target, temp2,
                                 slowPath);
 
         // An absent name property defaults to the empty string.
-        const JSAtomState& names = GetJitContext()->runtime->names();
+        const JSAtomState& names = gen->runtime->names();
         masm.movePtr(ImmGCPtr(names.empty), temp2);
     }
     masm.bind(&hasName);
 
     // Store the target's name atom in the bound function as is.
     masm.storePtr(temp2, Address(bound, JSFunction::offsetOfAtom()));
 
     // Set the BOUND_FN flag and, if the target is a constructor, the
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -287,16 +287,17 @@ class CodeGenerator final : public CodeG
     void visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir);
     void visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBranchV* lir);
     void visitIsNullOrLikeUndefinedAndBranchT(LIsNullOrLikeUndefinedAndBranchT* lir);
     void emitConcat(LInstruction* lir, Register lhs, Register rhs, Register output);
     void visitConcat(LConcat* lir);
     void visitCharCodeAt(LCharCodeAt* lir);
     void visitFromCharCode(LFromCharCode* lir);
     void visitFromCodePoint(LFromCodePoint* lir);
+    void visitStringConvertCase(LStringConvertCase* lir);
     void visitSinCos(LSinCos *lir);
     void visitStringSplit(LStringSplit* lir);
     void visitFunctionEnvironment(LFunctionEnvironment* lir);
     void visitNewLexicalEnvironmentObject(LNewLexicalEnvironmentObject* lir);
     void visitCopyLexicalEnvironmentObject(LCopyLexicalEnvironmentObject* lir);
     void visitCallGetProperty(LCallGetProperty* lir);
     void visitCallGetElement(LCallGetElement* lir);
     void visitCallSetElement(LCallSetElement* lir);
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -192,17 +192,17 @@ enum AnalysisMode {
      */
     Analysis_ArgumentsUsage
 };
 
 // Contains information about the compilation source for IR being generated.
 class CompileInfo
 {
   public:
-    CompileInfo(JSScript* script, JSFunction* fun, jsbytecode* osrPc,
+    CompileInfo(CompileRuntime* runtime, JSScript* script, JSFunction* fun, jsbytecode* osrPc,
                 AnalysisMode analysisMode, bool scriptNeedsArgsObj,
                 InlineScriptTree* inlineScriptTree)
       : script_(script), fun_(fun), osrPc_(osrPc),
         analysisMode_(analysisMode), scriptNeedsArgsObj_(scriptNeedsArgsObj),
         hadOverflowBailout_(script->hadOverflowBailout()),
         mayReadFrameArgsDirectly_(script->mayReadFrameArgsDirectly()),
         inlineScriptTree_(inlineScriptTree)
     {
@@ -229,17 +229,16 @@ class CompileInfo
         nstack_ = Max<unsigned>(script->nslots() - script->nfixed(), MinJITStackSize) + extra;
         nslots_ = nimplicit_ + nargs_ + nlocals_ + nstack_;
 
         // For derived class constructors, find and cache the frame slot for
         // the .this binding. This slot is assumed to be always
         // observable. See isObservableFrameSlot.
         if (script->isDerivedClassConstructor()) {
             MOZ_ASSERT(script->functionHasThisBinding());
-            CompileRuntime* runtime = GetJitContext()->runtime;
             for (BindingIter bi(script); bi; bi++) {
                 if (bi.name() != runtime->names().dotThis)
                     continue;
                 BindingLocation loc = bi.location();
                 if (loc.kind() == BindingLocation::Kind::Frame) {
                     thisSlotForDerivedClassConstructor_ = mozilla::Some(localSlot(loc.slot()));
                     break;
                 }
--- a/js/src/jit/EagerSimdUnbox.cpp
+++ b/js/src/jit/EagerSimdUnbox.cpp
@@ -95,17 +95,17 @@ UnboxSimdPhi(const JitCompartment* jitCo
 
         use->replaceProducer(box);
     }
 }
 
 bool
 EagerSimdUnbox(MIRGenerator* mir, MIRGraph& graph)
 {
-    const JitCompartment* jitCompartment = GetJitContext()->compartment->jitCompartment();
+    const JitCompartment* jitCompartment = mir->compartment->jitCompartment();
     for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
         if (mir->shouldCancel("Eager Simd Unbox"))
             return false;
 
         for (MInstructionReverseIterator ins = block->rbegin(); ins != block->rend(); ins++) {
             if (!ins->isSimdUnbox())
                 continue;
 
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -80,16 +80,18 @@
     _(RegExpInstanceOptimizable)    \
     _(GetFirstDollarIndex)          \
                                     \
     _(String)                       \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
     _(StringFromCodePoint)          \
     _(StringCharAt)                 \
+    _(StringToLowerCase)            \
+    _(StringToUpperCase)            \
                                     \
     _(IntrinsicStringReplaceString) \
     _(IntrinsicStringSplitString)   \
                                     \
     _(ObjectCreate)                 \
     _(ObjectToString)               \
                                     \
     _(SimdInt32x4)                  \
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2127,17 +2127,18 @@ IonCompile(JSContext* cx, JSScript* scri
     MIRGraph* graph = alloc->new_<MIRGraph>(temp);
     if (!graph)
         return AbortReason::Alloc;
 
     InlineScriptTree* inlineScriptTree = InlineScriptTree::New(temp, nullptr, nullptr, script);
     if (!inlineScriptTree)
         return AbortReason::Alloc;
 
-    CompileInfo* info = alloc->new_<CompileInfo>(script, script->functionNonDelazifying(), osrPc,
+    CompileInfo* info = alloc->new_<CompileInfo>(CompileRuntime::get(cx->runtime()),
+                                                 script, script->functionNonDelazifying(), osrPc,
                                                  Analysis_None,
                                                  script->needsArgsObj(), inlineScriptTree);
     if (!info)
         return AbortReason::Alloc;
 
     BaselineInspector* inspector = alloc->new_<BaselineInspector>(script);
     if (!inspector)
         return AbortReason::Alloc;
@@ -2478,32 +2479,30 @@ jit::OffThreadCompilationAvailable(JSCon
         && CanUseExtraThreads();
 }
 
 MethodStatus
 jit::CanEnter(JSContext* cx, RunState& state)
 {
     MOZ_ASSERT(jit::IsIonEnabled(cx));
 
-    JSScript* script = state.script();
+    HandleScript script = state.script();
 
     // Skip if the script has been disabled.
     if (!script->canIonCompile())
         return Method_Skipped;
 
     // Skip if the script is being compiled off thread.
     if (script->isIonCompilingOffThread())
         return Method_Skipped;
 
     // Skip if the code is expected to result in a bailout.
     if (script->hasIonScript() && script->ionScript()->bailoutExpected())
         return Method_Skipped;
 
-    RootedScript rscript(cx, script);
-
     // If constructing, allocate a new |this| object before building Ion.
     // Creating |this| is done before building Ion because it may change the
     // type information and invalidate compilation results.
     if (state.isInvoke()) {
         InvokeState& invoke = *state.asInvoke();
 
         if (TooManyActualArguments(invoke.args().length())) {
             TrackAndSpewIonAbort(cx, script, "too many actual args");
@@ -2523,33 +2522,33 @@ jit::CanEnter(JSContext* cx, RunState& s
                 return Method_Skipped;
             }
             return Method_Error;
         }
     }
 
     // If --ion-eager is used, compile with Baseline first, so that we
     // can directly enter IonMonkey.
-    if (JitOptions.eagerCompilation && !rscript->hasBaselineScript()) {
+    if (JitOptions.eagerCompilation && !script->hasBaselineScript()) {
         MethodStatus status = CanEnterBaselineMethod(cx, state);
         if (status != Method_Compiled)
             return status;
     }
 
     // Skip if the script is being compiled off thread or can't be
     // Ion-compiled (again). MaybeCreateThisForConstructor could have
     // started an Ion compilation or marked the script as uncompilable.
-    if (rscript->isIonCompilingOffThread() || !rscript->canIonCompile())
+    if (script->isIonCompilingOffThread() || !script->canIonCompile())
         return Method_Skipped;
 
     // Attempt compilation. Returns Method_Compiled if already compiled.
-    MethodStatus status = Compile(cx, rscript, nullptr, nullptr);
+    MethodStatus status = Compile(cx, script, nullptr, nullptr);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
-            ForbidCompilation(cx, rscript);
+            ForbidCompilation(cx, script);
         return status;
     }
 
     if (state.script()->baselineScript()->hasPendingIonBuilder()) {
         LinkIonScript(cx, state.script());
         if (!state.script()->hasIonScript())
             return jit::Method_Skipped;
     }
@@ -2629,18 +2628,17 @@ BaselineCanEnterAtBranch(JSContext* cx, 
         force = true;
     }
 
     // Attempt compilation.
     // - Returns Method_Compiled if the right ionscript is present
     //   (Meaning it was present or a sequantial compile finished)
     // - Returns Method_Skipped if pc doesn't match
     //   (This means a background thread compilation with that pc could have started or not.)
-    RootedScript rscript(cx, script);
-    MethodStatus status = Compile(cx, rscript, osrFrame, pc, force);
+    MethodStatus status = Compile(cx, script, osrFrame, pc, force);
     if (status != Method_Compiled) {
         if (status == Method_CantCompile)
             ForbidCompilation(cx, script);
         return status;
     }
 
     // Return the compilation was skipped when the osr pc wasn't adjusted.
     // This can happen when there was still an IonScript available and a
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -71,20 +71,16 @@ class JitContext
     // Allocator for temporary memory during compilation.
     TempAllocator* temp;
 
     // Wrappers with information about the current runtime/compartment for use
     // during compilation.
     CompileRuntime* runtime;
     CompileCompartment* compartment;
 
-    bool hasProfilingScripts() const {
-        return runtime && !!runtime->profilingScripts();
-    }
-
     int getNextAssemblerId() {
         return assemblerCount_++;
     }
   private:
     JitContext* prev_;
     int assemblerCount_;
 };
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -2043,17 +2043,17 @@ IsExclusiveThisArg(MCall* call, MDefinit
 
 static size_t
 IsExclusiveFirstArg(MCall* call, MDefinition* def)
 {
     return IsExclusiveNthOperand(call, MCall::IndexOfArgument(0), def);
 }
 
 static bool
-IsRegExpHoistableCall(MCall* call, MDefinition* def)
+IsRegExpHoistableCall(CompileRuntime* runtime, MCall* call, MDefinition* def)
 {
     if (call->isConstructing())
         return false;
 
     JSAtom* name;
     if (WrappedFunction* fun = call->getSingleTarget()) {
         if (!fun->isSelfHostedBuiltin())
             return false;
@@ -2066,17 +2066,16 @@ IsRegExpHoistableCall(MCall* call, MDefi
             funDef = funDef->toTypeBarrier()->input();
 
         if (!funDef->isCallGetIntrinsicValue())
             return false;
         name = funDef->toCallGetIntrinsicValue()->name();
     }
 
     // Hoistable only if the RegExp is the first argument of RegExpBuiltinExec.
-    CompileRuntime* runtime = GetJitContext()->runtime;
     if (name == runtime->names().RegExpBuiltinExec ||
         name == runtime->names().UnwrapAndCallRegExpBuiltinExec ||
         name == runtime->names().RegExpMatcher ||
         name == runtime->names().RegExpTester ||
         name == runtime->names().RegExpSearcher)
     {
         return IsExclusiveFirstArg(call, def);
     }
@@ -2194,26 +2193,26 @@ IsRegExpHoistable(MIRGenerator* mir, MDe
                         continue;
                 }
             } else if (useDef->isSetPropertyCache()) {
                 if (IsExclusiveNthOperand(useDef, 0, def)) {
                     MSetPropertyCache* setProp = useDef->toSetPropertyCache();
                     if (setProp->idval()->isConstant()) {
                         Value propIdVal = setProp->idval()->toConstant()->toJSValue();
                         if (propIdVal.isString()) {
-                            CompileRuntime* runtime = GetJitContext()->runtime;
+                            CompileRuntime* runtime = mir->runtime;
                             if (propIdVal.toString() == runtime->names().lastIndex)
                                 continue;
                         }
                     }
                 }
             }
             // MCall is safe only for some known safe functions.
             else if (useDef->isCall()) {
-                if (IsRegExpHoistableCall(useDef->toCall(), def))
+                if (IsRegExpHoistableCall(mir->runtime, useDef->toCall(), def))
                     continue;
             }
 
             // Everything else is unsafe.
             SetNotInWorklist(worklist);
             worklist.clear();
             *hoistable = false;
 
@@ -4204,17 +4203,17 @@ jit::AnalyzeNewScriptDefiniteProperties(
 
     TypeScript::SetThis(cx, script, TypeSet::ObjectType(group));
 
     MIRGraph graph(&temp);
     InlineScriptTree* inlineScriptTree = InlineScriptTree::New(&temp, nullptr, nullptr, script);
     if (!inlineScriptTree)
         return false;
 
-    CompileInfo info(script, fun,
+    CompileInfo info(CompileRuntime::get(cx->runtime()), script, fun,
                      /* osrPc = */ nullptr,
                      Analysis_DefiniteProperties,
                      script->needsArgsObj(),
                      inlineScriptTree);
 
     const OptimizationInfo* optimizationInfo = IonOptimizations.get(OptimizationLevel::Normal);
 
     CompilerConstraintList* constraints = NewCompilerConstraintList(temp);
@@ -4450,17 +4449,17 @@ jit::AnalyzeArgumentsUsage(JSContext* cx
 
     MIRGraph graph(&temp);
     InlineScriptTree* inlineScriptTree = InlineScriptTree::New(&temp, nullptr, nullptr, script);
     if (!inlineScriptTree) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    CompileInfo info(script, script->functionNonDelazifying(),
+    CompileInfo info(CompileRuntime::get(cx->runtime()), script, script->functionNonDelazifying(),
                      /* osrPc = */ nullptr,
                      Analysis_ArgumentsUsage,
                      /* needsArgsObj = */ true,
                      inlineScriptTree);
 
     const OptimizationInfo* optimizationInfo = IonOptimizations.get(OptimizationLevel::Normal);
 
     CompilerConstraintList* constraints = NewCompilerConstraintList(temp);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1525,17 +1525,17 @@ IonBuilder::visitBlock(const CFGBlock* c
         //   (1) Have the ImplicitlyUsed flag set on them.
         //   (2) Have more uses than before compiling this op (the value is
         //       used as operand of a new MIR instruction).
         //
         // This is used to catch problems where IonBuilder pops a value without
         // adding any SSA uses and doesn't call setImplicitlyUsedUnchecked on it.
         Vector<MDefinition*, 4, JitAllocPolicy> popped(alloc());
         Vector<size_t, 4, JitAllocPolicy> poppedUses(alloc());
-        unsigned nuses = GetUseCount(script_, script_->pcToOffset(pc));
+        unsigned nuses = GetUseCount(pc);
 
         for (unsigned i = 0; i < nuses; i++) {
             MDefinition* def = current->peek(-int32_t(i + 1));
             if (!popped.append(def) || !poppedUses.append(def->defUseCount()))
                 return abort(AbortReason::Alloc);
         }
 #endif
 
@@ -2668,17 +2668,17 @@ IonBuilder::improveTypesAtTypeOfCompare(
 
     if (inputTypes->unknown())
         return Ok();
 
     // Note: we cannot remove the AnyObject type in the false branch,
     // since there are multiple ways to get an object. That is the reason
     // for the 'trueBranch' test.
     TemporaryTypeSet filter;
-    const JSAtomState& names = GetJitContext()->runtime->names();
+    const JSAtomState& names = runtime->names();
     if (constant->toString() == TypeName(JSTYPE_UNDEFINED, names)) {
         filter.addType(TypeSet::UndefinedType(), alloc_->lifoAlloc());
         if (typeOf->inputMaybeCallableOrEmulatesUndefined() && trueBranch)
             filter.addType(TypeSet::AnyObjectType(), alloc_->lifoAlloc());
     } else if (constant->toString() == TypeName(JSTYPE_BOOLEAN, names)) {
         filter.addType(TypeSet::BooleanType(), alloc_->lifoAlloc());
     } else if (constant->toString() == TypeName(JSTYPE_NUMBER, names)) {
         filter.addType(TypeSet::Int32Type(), alloc_->lifoAlloc());
@@ -3762,17 +3762,17 @@ IonBuilder::inlineScriptedCall(CallInfo&
     }
 
     // Start inlining.
     LifoAlloc* lifoAlloc = alloc_->lifoAlloc();
     InlineScriptTree* inlineScriptTree =
         info().inlineScriptTree()->addCallee(alloc_, pc, calleeScript);
     if (!inlineScriptTree)
         return abort(AbortReason::Alloc);
-    CompileInfo* info = lifoAlloc->new_<CompileInfo>(calleeScript, target,
+    CompileInfo* info = lifoAlloc->new_<CompileInfo>(runtime, calleeScript, target,
                                                      (jsbytecode*)nullptr,
                                                      this->info().analysisMode(),
                                                      /* needsArgsObj = */ false,
                                                      inlineScriptTree);
     if (!info)
         return abort(AbortReason::Alloc);
 
     MIRGraphReturns returns(alloc());
@@ -4610,17 +4610,18 @@ IonBuilder::inlineCalls(CallInfo& callIn
             return abort(AbortReason::Alloc);
         inlineInfo.popFormals(inlineBlock);
         inlineInfo.setFun(funcDef);
 
         if (maybeCache) {
             // Assign the 'this' value a TypeSet specialized to the groups that
             // can generate this inlining target.
             MOZ_ASSERT(callInfo.thisArg() == maybeCache->value());
-            TemporaryTypeSet* thisTypes = maybeCache->propTable()->buildTypeSetForFunction(target);
+            TemporaryTypeSet* thisTypes =
+                maybeCache->propTable()->buildTypeSetForFunction(alloc(), target);
             if (!thisTypes)
                 return abort(AbortReason::Alloc);
 
             MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), inlineInfo.thisArg(), thisTypes);
             inlineBlock->add(filter);
             inlineInfo.setThis(filter);
         }
 
@@ -6051,17 +6052,17 @@ AbortReasonOr<Ok>
 IonBuilder::jsop_newarray(uint32_t length)
 {
     JSObject* templateObject = inspector->getTemplateObject(pc);
     MOZ_TRY(jsop_newarray(templateObject, length));
 
     // Improve resulting typeset.
     ObjectGroup* templateGroup = inspector->getTemplateObjectGroup(pc);
     if (templateGroup) {
-        TemporaryTypeSet* types = MakeSingletonTypeSet(constraints(), templateGroup);
+        TemporaryTypeSet* types = MakeSingletonTypeSet(alloc(), constraints(), templateGroup);
         current->peek(-1)->setResultTypeSet(types);
     }
 
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_newarray(JSObject* templateObject, uint32_t length)
@@ -6378,17 +6379,17 @@ IonBuilder::initializeArrayElement(MDefi
 
         if (addResumePointAndIncrementInitializedLength) {
             MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
             current->add(increment);
 
             MOZ_TRY(resumeAfter(increment));
         }
     } else {
-        if (NeedsPostBarrier(value))
+        if (needsPostBarrier(value))
             current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
         if ((obj->isNewArray() && obj->toNewArray()->convertDoubleElements()) ||
             (obj->isNullarySharedStub() &&
             obj->resultTypeSet()->convertDoubleElements(constraints()) == TemporaryTypeSet::AlwaysConvertToDoubles))
         {
             MInstruction* valueDouble = MToDouble::New(alloc(), value);
             current->add(valueDouble);
@@ -7341,18 +7342,19 @@ IonBuilder::getStaticName(bool* emitted,
     // Don't optimize global lexical bindings if they aren't initialized at
     // compile time.
     if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name))
         return Ok();
 
     *emitted = true;
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
-    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
-                                                       name, types, /* updateObserved = */ true);
+    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
+                                                       staticKey, name, types,
+                                                       /* updateObserved = */ true);
 
     if (barrier == BarrierKind::NoBarrier) {
         // Try to inline properties holding a known constant object.
         JSObject* singleton = types->maybeSingleton();
         if (singleton) {
             if (testSingletonProperty(staticObject, id) == singleton) {
                 pushConstant(ObjectValue(*singleton));
                 return Ok();
@@ -7395,19 +7397,19 @@ IonBuilder::loadStaticSlot(JSObject* sta
     if (barrier != BarrierKind::NoBarrier)
         rvalType = MIRType::Value;
 
     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())
+IonBuilder::needsPostBarrier(MDefinition* value)
+{
+    if (!compartment->zone()->nurseryExists())
         return false;
     return value->mightBeType(MIRType::Object);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::setStaticName(JSObject* staticObject, PropertyName* name)
 {
     jsid id = NameToId(name);
@@ -7444,17 +7446,17 @@ IonBuilder::setStaticName(JSObject* stat
         return jsop_setprop(name);
 
     current->pop();
 
     // Pop the bound object on the stack.
     MDefinition* obj = current->pop();
     MOZ_ASSERT(&obj->toConstant()->toObject() == staticObject);
 
-    if (NeedsPostBarrier(value))
+    if (needsPostBarrier(value))
         current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
     // 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;
@@ -7613,18 +7615,19 @@ IonBuilder::jsop_getimport(PropertyName*
     PropertyName* localName = JSID_TO_STRING(shape->propid())->asAtom().asPropertyName();
     bool emitted = false;
     MOZ_TRY(getStaticName(&emitted, targetEnv, localName));
 
     if (!emitted) {
         // This can happen if we don't have type information.
         TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(targetEnv);
         TemporaryTypeSet* types = bytecodeTypes(pc);
-        BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticKey,
-                                                           name, types, /* updateObserved = */ true);
+        BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
+                                                           staticKey, name, types,
+                                                           /* updateObserved = */ true);
 
         MOZ_TRY(loadStaticSlot(targetEnv, barrier, types, shape->slot()));
     }
 
     // In the rare case where this import hasn't been initialized already (we
     // have an import cycle where modules reference each other's imports), emit
     // a check.
     if (targetEnv->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL)) {
@@ -7973,17 +7976,17 @@ IonBuilder::pushReferenceLoadFromTypedOb
     MDefinition* scaledOffset;
     int32_t adjustment;
     uint32_t alignment = ReferenceTypeDescr::alignment(type);
     MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment, &elements, &scaledOffset, &adjustment));
 
     TemporaryTypeSet* observedTypes = bytecodeTypes(pc);
 
     MInstruction* load = nullptr;  // initialize to silence GCC warning
-    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
+    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
                                                        typedObj, name, observedTypes);
 
     switch (type) {
       case ReferenceTypeDescr::TYPE_ANY: {
         // Make sure the barrier reflects the possibility of reading undefined.
         bool bailOnUndefined = barrier == BarrierKind::NoBarrier &&
                                !observedTypes->hasType(TypeSet::UndefinedType());
         if (bailOnUndefined)
@@ -8482,17 +8485,17 @@ IonBuilder::getElemAddCache(MDefinition*
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
 
     BarrierKind barrier;
     if (obj->type() == MIRType::Object) {
         // Always add a barrier if the index is not an int32 value, so we can
         // attach stubs for particular properties.
         if (index->type() == MIRType::Int32) {
-            barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
+            barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(), obj,
                                                    nullptr, types);
         } else {
             barrier = BarrierKind::TypeSet;
         }
     } else {
         // PropertyReadNeedsTypeBarrier only accounts for object types, so for
         // now always insert a barrier if the input is not known to be an
         // object.
@@ -8586,21 +8589,21 @@ IonBuilder::jsop_getelem_dense(MDefiniti
 {
     TemporaryTypeSet* types = bytecodeTypes(pc);
 
     MOZ_ASSERT(index->type() == MIRType::Int32 || index->type() == MIRType::Double);
     if (JSOp(*pc) == JSOP_CALLELEM) {
         // Indexed call on an element of an array. Populate the observed types
         // with any objects that could be in the array, to avoid extraneous
         // type barriers.
-        AddObjectsForPropertyRead(obj, nullptr, types);
-    }
-
-    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
-                                                       nullptr, types);
+        AddObjectsForPropertyRead(alloc(), obj, nullptr, types);
+    }
+
+    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
+                                                       obj, nullptr, types);
     bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
 
     // Reads which are on holes in the object do not have to bail out if
     // undefined values have been observed at this access site and the access
     // cannot hit another indexed property on the object or its prototypes.
     bool readOutOfBounds = false;
     if (types->hasType(TypeSet::UndefinedType())) {
         bool hasExtraIndexedProperty;
@@ -9262,17 +9265,17 @@ IonBuilder::initOrSetElemTryCache(bool* 
     // Make sure the object being written to doesn't have copy on write elements.
     const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
     bool checkNative = !clasp || !clasp->isNative();
     object = addMaybeCopyElementsForWrite(object, checkNative);
 
     // Emit SetPropertyCache.
     bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
     MSetPropertyCache* ins =
-        MSetPropertyCache::New(alloc(), object, index, value, strict, NeedsPostBarrier(value),
+        MSetPropertyCache::New(alloc(), object, index, value, strict, needsPostBarrier(value),
                                barrier, guardHoles);
     current->add(ins);
 
     // Push value back onto stack. Init ops keep their object on stack.
     if (!IsPropertyInitOp(JSOp(*pc)))
         current->push(value);
 
     MOZ_TRY(resumeAfter(ins));
@@ -9310,17 +9313,17 @@ IonBuilder::initOrSetElemDense(Temporary
 
     *emitted = true;
 
     // Ensure id is an integer.
     MInstruction* idInt32 = MToInt32::New(alloc(), id);
     current->add(idInt32);
     id = idInt32;
 
-    if (NeedsPostBarrier(value))
+    if (needsPostBarrier(value))
         current->add(MPostWriteElementBarrier::New(alloc(), obj, value, id));
 
     // Copy the elements vector if necessary.
     obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
 
     // Get the elements vector.
     MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
     current->add(elements);
@@ -9650,17 +9653,17 @@ IonBuilder::jsop_rest()
         index = MConstant::New(alloc(), Int32Value(i - numFormals));
         current->add(index);
 
         MDefinition* arg = inlineCallInfo_->argv()[i];
         MStoreElement* store = MStoreElement::New(alloc(), elements, index, arg,
                                                   /* needsHoleCheck = */ false);
         current->add(store);
 
-        if (NeedsPostBarrier(arg))
+        if (needsPostBarrier(arg))
             current->add(MPostWriteBarrier::New(alloc(), array, arg));
     }
 
     // The array's length is incorrectly 0 now, from the template object
     // created by BaselineCompiler::emit_JSOP_REST() before the actual argument
     // count was known. Set the correct length now that we know that count.
     MSetArrayLength* length = MSetArrayLength::New(alloc(), elements, index);
     current->add(length);
@@ -10312,17 +10315,17 @@ IonBuilder::jsop_getprop(PropertyName* n
         if (emitted)
             return Ok();
     }
 
     obj = maybeUnboxForPropertyAccess(obj);
     if (obj->type() == MIRType::Object)
         obj = convertUnboxedObjects(obj);
 
-    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
+    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
                                                        obj, name, types);
 
     // Try to optimize to a specific constant.
     trackOptimizationAttempt(TrackedStrategy::GetProp_InferredConstant);
     if (barrier == BarrierKind::NoBarrier) {
         MOZ_TRY(getPropTryInferredConstant(&emitted, obj, name, types));
         if (emitted)
             return Ok();
@@ -11475,17 +11478,17 @@ IonBuilder::getPropTryInnerize(bool* emi
         trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter);
         MOZ_TRY(getPropTryCommonGetter(emitted, inner, name, types, /* innerized = */true));
         if (*emitted)
             return Ok();
     }
 
     // Passing the inner object to GetProperty IC is safe, see the
     // needsOuterizedThisObject check in IsCacheableGetPropCallNative.
-    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
+    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
                                                        inner, name, types);
     trackOptimizationAttempt(TrackedStrategy::GetProp_InlineCache);
     MOZ_TRY(getPropAddCache(inner, name, barrier, types));
 
     *emitted = true;
     return Ok();
 }
 
@@ -11825,17 +11828,17 @@ IonBuilder::setPropTryDefiniteSlot(bool*
         HeapTypeSetKey property = key->property(NameToId(name));
         if (property.nonWritable(constraints())) {
             trackOptimizationOutcome(TrackedOutcome::NonWritableProperty);
             return Ok();
         }
         writeBarrier |= property.needsBarrier(constraints());
     }
 
-    if (NeedsPostBarrier(value))
+    if (needsPostBarrier(value))
         current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
     MInstruction* store;
     if (slot < nfixed) {
         store = MStoreFixedSlot::New(alloc(), obj, slot, value);
         if (writeBarrier)
             store->toStoreFixedSlot()->setNeedsBarrier();
     } else {
@@ -11977,17 +11980,17 @@ IonBuilder::setPropTryInlineAccess(bool*
             // Monomorphic store to a native object.
             spew("Inlining monomorphic native SETPROP");
 
             obj = addShapeGuard(obj, receivers[0].shape, Bailout_ShapeGuard);
 
             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
             MOZ_ASSERT(shape);
 
-            if (NeedsPostBarrier(value))
+            if (needsPostBarrier(value))
                 current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
             bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
             MOZ_TRY(storeSlot(obj, shape, value, needsPreBarrier));
 
             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
             *emitted = true;
             return Ok();
@@ -12003,17 +12006,17 @@ IonBuilder::setPropTryInlineAccess(bool*
             MInstruction* expando = MLoadUnboxedExpando::New(alloc(), obj);
             current->add(expando);
 
             expando = addShapeGuard(expando, receivers[0].shape, Bailout_ShapeGuard);
 
             Shape* shape = receivers[0].shape->searchLinear(NameToId(name));
             MOZ_ASSERT(shape);
 
-            if (NeedsPostBarrier(value))
+            if (needsPostBarrier(value))
                 current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
             bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
             MOZ_TRY(storeSlot(expando, shape, value, needsPreBarrier));
 
             trackOptimizationOutcome(TrackedOutcome::Monomorphic);
             *emitted = true;
             return Ok();
@@ -12023,17 +12026,17 @@ IonBuilder::setPropTryInlineAccess(bool*
         spew("Inlining monomorphic unboxed SETPROP");
 
         ObjectGroup* group = receivers[0].group;
         if (!objTypes->hasType(TypeSet::ObjectType(group)))
             return Ok();
 
         obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
 
-        if (NeedsPostBarrier(value))
+        if (needsPostBarrier(value))
             current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
         const UnboxedLayout::Property* property = group->unboxedLayout().lookup(name);
         storeUnboxedProperty(obj, property->offset, property->type, value);
 
         current->push(value);
 
         trackOptimizationOutcome(TrackedOutcome::Monomorphic);
@@ -12044,28 +12047,28 @@ IonBuilder::setPropTryInlineAccess(bool*
     MOZ_ASSERT(receivers.length() > 1);
     spew("Inlining polymorphic SETPROP");
 
     if (Shape* propShape = PropertyShapesHaveSameSlot(receivers, NameToId(name))) {
         obj = addGuardReceiverPolymorphic(obj, receivers);
         if (!obj)
             return abort(AbortReason::Alloc);
 
-        if (NeedsPostBarrier(value))
+        if (needsPostBarrier(value))
             current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
         bool needsPreBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
         MOZ_TRY(storeSlot(obj, propShape, value, needsPreBarrier));
 
         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
         *emitted = true;
         return Ok();
     }
 
-    if (NeedsPostBarrier(value))
+    if (needsPostBarrier(value))
         current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
     MSetPropertyPolymorphic* ins = MSetPropertyPolymorphic::New(alloc(), obj, value, name);
     current->add(ins);
     current->push(value);
 
     for (size_t i = 0; i < receivers.length(); i++) {
         Shape* propShape = nullptr;
@@ -12093,17 +12096,17 @@ IonBuilder::setPropTryCache(bool* emitte
                             bool barrier, TemporaryTypeSet* objTypes)
 {
     MOZ_ASSERT(*emitted == false);
 
     bool strict = IsStrictSetPC(pc);
 
     MConstant* id = constant(StringValue(name));
     MSetPropertyCache* ins = MSetPropertyCache::New(alloc(), obj, id, value, strict,
-                                                    NeedsPostBarrier(value), barrier,
+                                                    needsPostBarrier(value), barrier,
                                                     /* guardHoles = */ false);
     current->add(ins);
     current->push(value);
 
     MOZ_TRY(resumeAfter(ins));
 
     trackOptimizationSuccess();
     *emitted = true;
@@ -12267,17 +12270,17 @@ IonBuilder::jsop_setarg(uint32_t arg)
     // MGetFrameArgument.
     MOZ_ASSERT_IF(script()->hasBaselineScript(),
                   script()->baselineScript()->modifiesArguments());
     MDefinition* val = current->peek(-1);
 
     // If an arguments object is in use, and it aliases formals, then all SETARGs
     // must go through the arguments object.
     if (info().argsObjAliasesFormals()) {
-        if (NeedsPostBarrier(val))
+        if (needsPostBarrier(val))
             current->add(MPostWriteBarrier::New(alloc(), current->argumentsObject(), val));
         current->add(MSetArgumentsObjectArg::New(alloc(), current->argumentsObject(),
                                                  GET_ARGNO(pc), val));
         return Ok();
     }
 
     // :TODO: if hasArguments() is true, and the script has a JSOP_SETARG, then
     // convert all arg accesses to go through the arguments object. (see Bug 957475)
@@ -12761,17 +12764,17 @@ IonBuilder::jsop_setaliasedvar(Environme
         return jsop_setprop(name);
     }
 
     MDefinition* rval = current->peek(-1);
     MDefinition* obj = walkEnvironmentChain(ec.hops());
 
     Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
 
-    if (NeedsPostBarrier(rval))
+    if (needsPostBarrier(rval))
         current->add(MPostWriteBarrier::New(alloc(), obj, rval));
 
     MInstruction* store;
     if (shape->numFixedSlots() <= ec.slot()) {
         MInstruction* slots = MSlots::New(alloc(), obj);
         current->add(slots);
 
         store = MStoreSlot::NewBarriered(alloc(), slots, ec.slot() - shape->numFixedSlots(), rval);
@@ -13520,17 +13523,17 @@ IonBuilder::storeReferenceTypedObjectVal
     int32_t adjustment;
     uint32_t alignment = ReferenceTypeDescr::alignment(type);
     MOZ_TRY(loadTypedObjectElements(typedObj, byteOffset, alignment,
                                     &elements, &scaledOffset, &adjustment));
 
     MInstruction* store = nullptr;  // initialize to silence GCC warning
     switch (type) {
       case ReferenceTypeDescr::TYPE_ANY:
-        if (NeedsPostBarrier(value))
+        if (needsPostBarrier(value))
             current->add(MPostWriteBarrier::New(alloc(), typedObj, value));
         store = MStoreElement::New(alloc(), elements, scaledOffset, value, false, adjustment);
         store->toStoreElement()->setNeedsBarrier();
         break;
       case ReferenceTypeDescr::TYPE_OBJECT:
         // Note: We cannot necessarily tell at this point whether a post
         // barrier is needed, because the type policy may insert ToObjectOrNull
         // instructions later, and those may require a post barrier. Therefore,
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -661,16 +661,17 @@ class IonBuilder
 
     // String natives.
     InliningResult inlineStringObject(CallInfo& callInfo);
     InliningResult inlineStrCharCodeAt(CallInfo& callInfo);
     InliningResult inlineConstantCharCodeAt(CallInfo& callInfo);
     InliningResult inlineStrFromCharCode(CallInfo& callInfo);
     InliningResult inlineStrFromCodePoint(CallInfo& callInfo);
     InliningResult inlineStrCharAt(CallInfo& callInfo);
+    InliningResult inlineStringConvertCase(CallInfo& callInfo, MStringConvertCase::Mode mode);
 
     // String intrinsics.
     InliningResult inlineStringReplaceString(CallInfo& callInfo);
     InliningResult inlineConstantStringSplitString(CallInfo& callInfo);
     InliningResult inlineStringSplitString(CallInfo& callInfo);
 
     // Reflect natives.
     InliningResult inlineReflectGetPrototypeOf(CallInfo& callInfo);
@@ -1120,16 +1121,18 @@ class IonBuilder
     // Discard the MGetPropertyCache if it is handled by WrapMGetPropertyCache.
     void keepFallbackFunctionGetter(MGetPropertyCache* cache) {
         if (cache == maybeFallbackFunctionGetter_)
             maybeFallbackFunctionGetter_ = nullptr;
     }
 
     MGetPropertyCache* maybeFallbackFunctionGetter_;
 
+    bool needsPostBarrier(MDefinition* value);
+
     // Used in tracking outcomes of optimization strategies for devtools.
     void startTrackingOptimizations();
 
     // The track* methods below are called often. Do not combine them with the
     // unchecked variants, despite the unchecked variants having no other
     // callers.
     void trackTypeInfo(JS::TrackedTypeSite site, MIRType mirType,
                        TemporaryTypeSet* typeSet)
@@ -1344,14 +1347,12 @@ class CallInfo
         thisArg_->setImplicitlyUsedUnchecked();
         if (newTargetArg_)
             newTargetArg_->setImplicitlyUsedUnchecked();
         for (uint32_t i = 0; i < argc(); i++)
             getArg(i)->setImplicitlyUsedUnchecked();
     }
 };
 
-bool NeedsPostBarrier(MDefinition* value);
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonBuilder_h */
--- a/js/src/jit/Label.h
+++ b/js/src/jit/Label.h
@@ -13,17 +13,20 @@ namespace js {
 namespace jit {
 
 struct LabelBase
 {
   protected:
     // offset_ >= 0 means that the label is either bound or has incoming
     // uses and needs to be bound.
     int32_t offset_ : 31;
-    bool bound_   : 1;
+
+    // We use uint32_t instead of bool to ensure MSVC packs these fields
+    // correctly.
+    uint32_t bound_ : 1;
 
     void operator =(const LabelBase& label) = delete;
 
   public:
     static const int32_t INVALID_OFFSET = -1;
 
     LabelBase() : offset_(INVALID_OFFSET), bound_(false)
     { }
@@ -91,16 +94,18 @@ class Label : public LabelBase
         JitContext* context = MaybeGetJitContext();
         bool hadError = js::oom::HadSimulatedOOM() ||
                         (context && context->runtime && context->runtime->hadOutOfMemory());
         MOZ_ASSERT_IF(!hadError, !used());
 #endif
     }
 };
 
+static_assert(sizeof(Label) == sizeof(uint32_t), "Label should have same size as uint32_t");
+
 // Label's destructor asserts that if it has been used it has also been bound.
 // In the case long-lived labels, however, failed compilation (e.g. OOM) will
 // trigger this failure innocuously. This Label silences the assertion.
 class NonAssertingLabel : public Label
 {
   public:
     ~NonAssertingLabel()
     {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1997,16 +1997,26 @@ LIRGenerator::visitFromCodePoint(MFromCo
 
     LFromCodePoint* lir = new(alloc()) LFromCodePoint(useRegister(codePoint), temp(), temp());
     assignSnapshot(lir, Bailout_BoundsCheck);
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
+LIRGenerator::visitStringConvertCase(MStringConvertCase* ins)
+{
+    MOZ_ASSERT(ins->string()->type() == MIRType::String);
+
+    auto* lir = new(alloc()) LStringConvertCase(useRegisterAtStart(ins->string()));
+    defineReturn(lir, ins);
+    assignSafepoint(lir, ins);
+}
+
+void
 LIRGenerator::visitStart(MStart* start)
 {
     LStart* lir = new(alloc()) LStart;
 
     // Create a snapshot that captures the initial state of the function.
     assignSnapshot(lir, Bailout_ArgumentCheck);
     if (start->block()->graph().entryBlock() == start->block())
         lirGraph_.setEntrySnapshot(lir->snapshot());
@@ -2297,24 +2307,24 @@ LIRGenerator::visitWrapInt64ToInt32(MWra
 
 void
 LIRGenerator::visitToString(MToString* ins)
 {
     MDefinition* opd = ins->input();
 
     switch (opd->type()) {
       case MIRType::Null: {
-        const JSAtomState& names = GetJitContext()->runtime->names();
+        const JSAtomState& names = gen->runtime->names();
         LPointer* lir = new(alloc()) LPointer(names.null);
         define(lir, ins);
         break;
       }
 
       case MIRType::Undefined: {
-        const JSAtomState& names = GetJitContext()->runtime->names();
+        const JSAtomState& names = gen->runtime->names();
         LPointer* lir = new(alloc()) LPointer(names.undefined);
         define(lir, ins);
         break;
       }
 
       case MIRType::Boolean: {
         LBooleanToString* lir = new(alloc()) LBooleanToString(useRegister(opd));
         define(lir, ins);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -151,16 +151,17 @@ class LIRGenerator : public LIRGenerator
     void visitSub(MSub* ins);
     void visitMul(MMul* ins);
     void visitDiv(MDiv* ins);
     void visitMod(MMod* ins);
     void visitConcat(MConcat* ins);
     void visitCharCodeAt(MCharCodeAt* ins);
     void visitFromCharCode(MFromCharCode* ins);
     void visitFromCodePoint(MFromCodePoint* ins);
+    void visitStringConvertCase(MStringConvertCase* ins);
     void visitSinCos(MSinCos *ins);
     void visitStringSplit(MStringSplit* ins);
     void visitStart(MStart* start);
     void visitOsrEntry(MOsrEntry* entry);
     void visitNop(MNop* nop);
     void visitLimitedTruncate(MLimitedTruncate* nop);
     void visitOsrValue(MOsrValue* value);
     void visitOsrEnvironmentChain(MOsrEnvironmentChain* object);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -223,16 +223,20 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::StringCharCodeAt:
         return inlineStrCharCodeAt(callInfo);
       case InlinableNative::StringFromCharCode:
         return inlineStrFromCharCode(callInfo);
       case InlinableNative::StringFromCodePoint:
         return inlineStrFromCodePoint(callInfo);
       case InlinableNative::StringCharAt:
         return inlineStrCharAt(callInfo);
+      case InlinableNative::StringToLowerCase:
+        return inlineStringConvertCase(callInfo, MStringConvertCase::LowerCase);
+      case InlinableNative::StringToUpperCase:
+        return inlineStringConvertCase(callInfo, MStringConvertCase::UpperCase);
 
       // String intrinsics.
       case InlinableNative::IntrinsicStringReplaceString:
         return inlineStringReplaceString(callInfo);
       case InlinableNative::IntrinsicStringSplitString:
         return inlineStringSplitString(callInfo);
       case InlinableNative::IntrinsicNewStringIterator:
         return inlineNewIterator(callInfo, MNewIterator::StringIterator);
@@ -718,17 +722,17 @@ IonBuilder::inlineArrayPopShift(CallInfo
 
     if (clasp == &ArrayObject::class_)
         obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
 
     TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
     bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(TypeSet::UndefinedType());
 
-    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
+    BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, alloc(), constraints(),
                                                        obj, nullptr, returnTypes);
     if (barrier != BarrierKind::NoBarrier)
         returnType = MIRType::Value;
 
     MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode,
                                               unboxedType, needsHoleCheck, maybeUndefined);
     current->add(ins);
     current->push(ins);
@@ -829,17 +833,17 @@ IonBuilder::inlineArrayPush(CallInfo& ca
         MInstruction* valueDouble = MToDouble::New(alloc(), value);
         current->add(valueDouble);
         value = valueDouble;
     }
 
     if (unboxedType == JSVAL_TYPE_MAGIC)
         obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
 
-    if (NeedsPostBarrier(value))
+    if (needsPostBarrier(value))
         current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
     MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
     current->add(ins);
     current->push(ins);
 
     MOZ_TRY(resumeAfter(ins));
     return InliningStatus_Inlined;
@@ -1639,17 +1643,17 @@ IonBuilder::inlineStringSplitString(Call
     if (sepArg->type() != MIRType::String)
         return InliningStatus_NotInlined;
 
     IonBuilder::InliningStatus resultConstStringSplit;
     MOZ_TRY_VAR(resultConstStringSplit, inlineConstantStringSplitString(callInfo));
     if (resultConstStringSplit != InliningStatus_NotInlined)
         return resultConstStringSplit;
 
-    JSContext* cx = GetJitContext()->cx;
+    JSContext* cx = TlsContext.get();
     ObjectGroup* group = ObjectGroupCompartment::getStringSplitStringGroup(cx);
     if (!group)
         return InliningStatus_NotInlined;
     if (group->maybePreliminaryObjects())
         return InliningStatus_NotInlined;
 
     TypeSet::ObjectKey* retKey = TypeSet::ObjectKey::get(group);
     if (retKey->unknownProperties())
@@ -1966,16 +1970,37 @@ IonBuilder::inlineStrCharAt(CallInfo& ca
 
     MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
     current->add(string);
     current->push(string);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
+IonBuilder::inlineStringConvertCase(CallInfo& callInfo, MStringConvertCase::Mode mode)
+{
+    if (callInfo.argc() != 0 || callInfo.constructing()) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
+    if (getInlineReturnType() != MIRType::String)
+        return InliningStatus_NotInlined;
+    if (callInfo.thisArg()->type() != MIRType::String)
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    auto* ins = MStringConvertCase::New(alloc(), callInfo.thisArg(), mode);
+    current->add(ins);
+    current->push(ins);
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningResult
 IonBuilder::inlineRegExpMatcher(CallInfo& callInfo)
 {
     // This is called from Self-hosted JS, after testing each argument,
     // most of following tests should be passed.
 
     if (callInfo.argc() != 3 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
@@ -1994,17 +2019,17 @@ IonBuilder::inlineRegExpMatcher(CallInfo
         return InliningStatus_NotInlined;
 
     if (strArg->mightBeType(MIRType::Object))
         return InliningStatus_NotInlined;
 
     if (lastIndexArg->type() != MIRType::Int32)
         return InliningStatus_NotInlined;
 
-    JSContext* cx = GetJitContext()->cx;
+    JSContext* cx = TlsContext.get();
     if (!cx->compartment()->jitCompartment()->ensureRegExpMatcherStubExists(cx)) {
         cx->clearPendingException(); // OOM or overrecursion.
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* matcher = MRegExpMatcher::New(alloc(), rxArg, strArg, lastIndexArg);
@@ -2040,17 +2065,17 @@ IonBuilder::inlineRegExpSearcher(CallInf
         return InliningStatus_NotInlined;
 
     if (strArg->mightBeType(MIRType::Object))
         return InliningStatus_NotInlined;
 
     if (lastIndexArg->type() != MIRType::Int32)
         return InliningStatus_NotInlined;
 
-    JSContext* cx = GetJitContext()->cx;
+    JSContext* cx = TlsContext.get();
     if (!cx->compartment()->jitCompartment()->ensureRegExpSearcherStubExists(cx)) {
         cx->clearPendingException(); // OOM or overrecursion.
         return abort(AbortReason::Error);
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* searcher = MRegExpSearcher::New(alloc(), rxArg, strArg, lastIndexArg);
@@ -2086,17 +2111,17 @@ IonBuilder::inlineRegExpTester(CallInfo&
         return InliningStatus_NotInlined;
 
     if (strArg->mightBeType(MIRType::Object))
         return InliningStatus_NotInlined;
 
     if (lastIndexArg->type() != MIRType::Int32)
         return InliningStatus_NotInlined;
 
-    JSContext* cx = GetJitContext()->cx;
+    JSContext* cx = TlsContext.get();
     if (!cx->compartment()->jitCompartment()->ensureRegExpTesterStubExists(cx)) {
         cx->clearPendingException(); // OOM or overrecursion.
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* tester = MRegExpTester::New(alloc(), rxArg, strArg, lastIndexArg);
@@ -2867,17 +2892,17 @@ IonBuilder::inlineUnsafeSetReservedSlot(
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MStoreFixedSlot* store =
         MStoreFixedSlot::NewBarriered(alloc(), obj, slot, callInfo.getArg(2));
     current->add(store);
     current->push(store);
 
-    if (NeedsPostBarrier(callInfo.getArg(2)))
+    if (needsPostBarrier(callInfo.getArg(2)))
         current->add(MPostWriteBarrier::New(alloc(), obj, callInfo.getArg(2)));
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineUnsafeGetReservedSlot(CallInfo& callInfo, MIRType knownValueType)
 {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -795,23 +795,23 @@ bool
 MDefinition::emptyResultTypeSet() const
 {
     return resultTypeSet() && resultTypeSet()->empty();
 }
 
 MConstant*
 MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints)
 {
-    return new(alloc) MConstant(v, constraints);
+    return new(alloc) MConstant(alloc, v, constraints);
 }
 
 MConstant*
 MConstant::New(TempAllocator::Fallible alloc, const Value& v, CompilerConstraintList* constraints)
 {
-    return new(alloc) MConstant(v, constraints);
+    return new(alloc) MConstant(alloc.alloc, v, constraints);
 }
 
 MConstant*
 MConstant::NewFloat32(TempAllocator& alloc, double d)
 {
     MOZ_ASSERT(IsNaN(d) || d == double(float(d)));
     return new(alloc) MConstant(float(d));
 }
@@ -834,45 +834,46 @@ MConstant::New(TempAllocator& alloc, con
 
 MConstant*
 MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v)
 {
     return new(alloc) MConstant(v);
 }
 
 static TemporaryTypeSet*
-MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::ObjectKey* key)
+MakeSingletonTypeSetFromKey(TempAllocator& tempAlloc, CompilerConstraintList* constraints,
+                            TypeSet::ObjectKey* key)
 {
     // Invalidate when this object's ObjectGroup gets unknown properties. This
     // happens for instance when we mutate an object's __proto__, in this case
     // we want to invalidate and mark this TypeSet as containing AnyObject
     // (because mutating __proto__ will change an object's ObjectGroup).
     MOZ_ASSERT(constraints);
     (void)key->hasStableClassAndProto(constraints);
 
-    LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
+    LifoAlloc* alloc = tempAlloc.lifoAlloc();
     return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::ObjectType(key));
 }
 
 TemporaryTypeSet*
-jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj)
-{
-    return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj));
+jit::MakeSingletonTypeSet(TempAllocator& alloc, CompilerConstraintList* constraints, JSObject* obj)
+{
+    return MakeSingletonTypeSetFromKey(alloc, constraints, TypeSet::ObjectKey::get(obj));
 }
 
 TemporaryTypeSet*
-jit::MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj)
-{
-    return MakeSingletonTypeSetFromKey(constraints, TypeSet::ObjectKey::get(obj));
+jit::MakeSingletonTypeSet(TempAllocator& alloc, CompilerConstraintList* constraints, ObjectGroup* obj)
+{
+    return MakeSingletonTypeSetFromKey(alloc, constraints, TypeSet::ObjectKey::get(obj));
 }
 
 static TemporaryTypeSet*
-MakeUnknownTypeSet()
-{
-    LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
+MakeUnknownTypeSet(TempAllocator& tempAlloc)
+{
+    LifoAlloc* alloc = tempAlloc.lifoAlloc();
     return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::UnknownType());
 }
 
 #ifdef DEBUG
 
 bool
 jit::IonCompilationCanUseNurseryPointers()
 {
@@ -886,17 +887,17 @@ jit::IonCompilationCanUseNurseryPointers
     // store buffer must have been notified that minor GCs must cancel pending
     // or in progress Ion compilations.
     JSContext* cx = TlsContext.get();
     return cx->zone()->group()->storeBuffer().cancelIonCompilations();
 }
 
 #endif // DEBUG
 
-MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints)
+MConstant::MConstant(TempAllocator& alloc, const js::Value& vp, CompilerConstraintList* constraints)
 {
     setResultType(MIRTypeFromValue(vp));
 
     MOZ_ASSERT(payload_.asBits == 0);
 
     switch (type()) {
       case MIRType::Undefined:
       case MIRType::Null:
@@ -917,32 +918,32 @@ MConstant::MConstant(const js::Value& vp
       case MIRType::Symbol:
         payload_.sym = vp.toSymbol();
         break;
       case MIRType::Object:
         payload_.obj = &vp.toObject();
         // Create a singleton type set for the object. This isn't necessary for
         // other types as the result type encodes all needed information.
         MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), IonCompilationCanUseNurseryPointers());
-        setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject()));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, &vp.toObject()));
         break;
       case MIRType::MagicOptimizedArguments:
       case MIRType::MagicOptimizedOut:
       case MIRType::MagicHole:
       case MIRType::MagicIsConstructing:
         break;
       case MIRType::MagicUninitializedLexical:
         // JS_UNINITIALIZED_LEXICAL does not escape to script and is not
         // observed in type sets. However, it may flow around freely during
         // Ion compilation. Give it an unknown typeset to poison any type sets
         // it merges with.
         //
         // TODO We could track uninitialized lexicals more precisely by tracking
         // them in type sets.
-        setResultTypeSet(MakeUnknownTypeSet());
+        setResultTypeSet(MakeUnknownTypeSet(alloc));
         break;
       default:
         MOZ_CRASH("Unexpected type");
     }
 
     setMovable();
 }
 
@@ -5114,28 +5115,29 @@ MArgumentState::Copy(TempAllocator& allo
     MArgumentState* res = new(alloc) MArgumentState();
     if (!res || !res->init(alloc, state->numElements()))
         return nullptr;
     for (size_t i = 0, e = res->numOperands(); i < e; i++)
         res->initOperand(i, state->getOperand(i));
     return res;
 }
 
-MNewArray::MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst,
-                     gc::InitialHeap initialHeap, jsbytecode* pc, bool vmCall)
+MNewArray::MNewArray(TempAllocator& alloc, CompilerConstraintList* constraints, uint32_t length,
+                     MConstant* templateConst, gc::InitialHeap initialHeap, jsbytecode* pc,
+                     bool vmCall)
   : MUnaryInstruction(templateConst),
     length_(length),
     initialHeap_(initialHeap),
     convertDoubleElements_(false),
     pc_(pc),
     vmCall_(vmCall)
 {
     setResultType(MIRType::Object);
     if (templateObject()) {
-        if (TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, templateObject())) {
+        if (TemporaryTypeSet* types = MakeSingletonTypeSet(alloc, constraints, templateObject())) {
             setResultTypeSet(types);
             if (types->convertDoubleElements(constraints) == TemporaryTypeSet::AlwaysConvertToDoubles)
                 convertDoubleElements_ = true;
         }
     }
 }
 
 MDefinition::AliasType
@@ -5531,19 +5533,19 @@ InlinePropertyTable::hasObjectGroup(Obje
     for (size_t i = 0; i < numEntries(); i++) {
         if (entries_[i]->group == group)
             return true;
     }
     return false;
 }
 
 TemporaryTypeSet*
-InlinePropertyTable::buildTypeSetForFunction(JSFunction* func) const
-{
-    LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
+InlinePropertyTable::buildTypeSetForFunction(TempAllocator& tempAlloc, JSFunction* func) const
+{
+    LifoAlloc* alloc = tempAlloc.lifoAlloc();
     TemporaryTypeSet* types = alloc->new_<TemporaryTypeSet>();
     if (!types)
         return nullptr;
     for (size_t i = 0; i < numEntries(); i++) {
         if (entries_[i]->func == func)
             types->addType(TypeSet::ObjectType(entries_[i]->group), alloc);
     }
     return types;
@@ -6165,16 +6167,17 @@ PropertyReadNeedsTypeBarrier(CompilerCon
     }
 
     property.freeze(constraints);
     return BarrierKind::NoBarrier;
 }
 
 BarrierKind
 jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
+                                  TempAllocator& alloc,
                                   CompilerConstraintList* constraints,
                                   TypeSet::ObjectKey* key, PropertyName* name,
                                   TemporaryTypeSet* observed, bool updateObserved)
 {
     if (!updateObserved)
         return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
 
     // If this access has never executed, try to add types to the observed set
@@ -6193,50 +6196,51 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
 
             HeapTypeSetKey property = obj->property(NameToId(name));
             if (property.maybeTypes()) {
                 TypeSet::TypeList types;
                 if (!property.maybeTypes()->enumerateTypes(&types))
                     break;
                 if (types.length() == 1) {
                     // Note: the return value here is ignored.
-                    observed->addType(types[0], GetJitContext()->temp->lifoAlloc());
+                    observed->addType(types[0], alloc.lifoAlloc());
                 }
                 break;
             }
 
             if (!obj->proto().isObject())
                 break;
             obj = TypeSet::ObjectKey::get(obj->proto().toObject());
         } while (obj);
     }
 
     return PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
 }
 
 BarrierKind
 jit::PropertyReadNeedsTypeBarrier(JSContext* propertycx,
+                                  TempAllocator& alloc,
                                   CompilerConstraintList* constraints,
                                   MDefinition* obj, PropertyName* name,
                                   TemporaryTypeSet* observed)
 {
     if (observed->unknown())
         return BarrierKind::NoBarrier;
 
     TypeSet* types = obj->resultTypeSet();
     if (!types || types->unknownObject())
         return BarrierKind::TypeSet;
 
     BarrierKind res = BarrierKind::NoBarrier;
 
     bool updateObserved = types->getObjectCount() == 1;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
         if (TypeSet::ObjectKey* key = types->getObject(i)) {
-            BarrierKind kind = PropertyReadNeedsTypeBarrier(propertycx, constraints, key, name,
-                                                            observed, updateObserved);
+            BarrierKind kind = PropertyReadNeedsTypeBarrier(propertycx, alloc, constraints, key,
+                                                            name, observed, updateObserved);
             if (kind == BarrierKind::TypeSet)
                 return BarrierKind::TypeSet;
 
             if (kind == BarrierKind::TypeTagOnly) {
                 MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly);
                 res = BarrierKind::TypeTagOnly;
             } else {
                 MOZ_ASSERT(kind == BarrierKind::NoBarrier);
@@ -6312,23 +6316,23 @@ jit::PropertyReadIsIdempotent(CompilerCo
                 return false;
         }
     }
 
     return true;
 }
 
 void
-jit::AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name,
+jit::AddObjectsForPropertyRead(TempAllocator& tempAlloc, MDefinition* obj, PropertyName* name,
                                TemporaryTypeSet* observed)
 {
     // Add objects to observed which *could* be observed by reading name from obj,
     // to hopefully avoid unnecessary type barriers and code invalidations.
 
-    LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
+    LifoAlloc* alloc = tempAlloc.lifoAlloc();
 
     TemporaryTypeSet* types = obj->resultTypeSet();
     if (!types || types->unknownObject()) {
         observed->addType(TypeSet::AnyObjectType(), alloc);
         return;
     }
 
     for (size_t i = 0; i < types->getObjectCount(); i++) {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1212,16 +1212,26 @@ class MInstruction
         return new(alloc) MThisOpcode(mozilla::Forward<Args>(args)...);     \
     }                                                                       \
     template <typename... Args>                                             \
     static MThisOpcode* New(TempAllocator::Fallible alloc, Args&&... args)  \
     {                                                                       \
         return new(alloc) MThisOpcode(mozilla::Forward<Args>(args)...);     \
     }
 
+#define TRIVIAL_NEW_WRAPPERS_WITH_ALLOC                                     \
+    template <typename... Args>                                             \
+    static MThisOpcode* New(TempAllocator& alloc, Args&&... args) {         \
+        return new(alloc) MThisOpcode(alloc, mozilla::Forward<Args>(args)...); \
+    }                                                                       \
+    template <typename... Args>                                             \
+    static MThisOpcode* New(TempAllocator::Fallible alloc, Args&&... args)  \
+    {                                                                       \
+        return new(alloc) MThisOpcode(alloc, mozilla::Forward<Args>(args)...); \
+    }
 
 // These macros are used as a syntactic sugar for writting getOperand
 // accessors. They are meant to be used in the body of MIR Instructions as
 // follows:
 //
 //   public:
 //     INSTRUCTION_HEADER(Foo)
 //     NAMED_OPERANDS((0, lhs), (1, rhs))
@@ -1561,17 +1571,17 @@ class MConstant : public MNullaryInstruc
 
 #ifdef DEBUG
     void assertInitializedPayload() const;
 #else
     void assertInitializedPayload() const {}
 #endif
 
   protected:
-    MConstant(const Value& v, CompilerConstraintList* constraints);
+    MConstant(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints);
     explicit MConstant(JSObject* obj);
     explicit MConstant(float f);
     explicit MConstant(int64_t i);
 
   public:
     INSTRUCTION_HEADER(Constant)
     static MConstant* New(TempAllocator& alloc, const Value& v,
                           CompilerConstraintList* constraints = nullptr);
@@ -3205,20 +3215,20 @@ class MThrow
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 // Fabricate a type set containing only the type of the specified object.
 TemporaryTypeSet*
-MakeSingletonTypeSet(CompilerConstraintList* constraints, JSObject* obj);
+MakeSingletonTypeSet(TempAllocator& alloc, CompilerConstraintList* constraints, JSObject* obj);
 
 TemporaryTypeSet*
-MakeSingletonTypeSet(CompilerConstraintList* constraints, ObjectGroup* obj);
+MakeSingletonTypeSet(TempAllocator& alloc, CompilerConstraintList* constraints, ObjectGroup* obj);
 
 MOZ_MUST_USE bool
 MergeTypes(TempAllocator& alloc, MIRType* ptype, TemporaryTypeSet** ptypeSet,
            MIRType newType, TemporaryTypeSet* newTypeSet);
 
 bool
 TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);
 
@@ -3243,28 +3253,30 @@ class MNewArray
 
     // Whether values written to this array should be converted to double first.
     bool convertDoubleElements_;
 
     jsbytecode* pc_;
 
     bool vmCall_;
 
-    MNewArray(CompilerConstraintList* constraints, uint32_t length, MConstant* templateConst,
-              gc::InitialHeap initialHeap, jsbytecode* pc, bool vmCall = false);
+    MNewArray(TempAllocator& alloc, CompilerConstraintList* constraints, uint32_t length,
+              MConstant* templateConst, gc::InitialHeap initialHeap, jsbytecode* pc,
+              bool vmCall = false);
 
   public:
     INSTRUCTION_HEADER(NewArray)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     static MNewArray* NewVM(TempAllocator& alloc, CompilerConstraintList* constraints,
                             uint32_t length, MConstant* templateConst,
                             gc::InitialHeap initialHeap, jsbytecode* pc)
     {
-        return new(alloc) MNewArray(constraints, length, templateConst, initialHeap, pc, true);
+        return new(alloc) MNewArray(alloc, constraints, length, templateConst, initialHeap, pc,
+                                    true);
     }
 
     uint32_t length() const {
         return length_;
     }
 
     JSObject* templateObject() const {
         return getOperand(0)->toConstant()->toObjectOrNull();
@@ -3304,29 +3316,29 @@ class MNewArray
     }
 };
 
 class MNewArrayCopyOnWrite : public MNullaryInstruction
 {
     CompilerGCPointer<ArrayObject*> templateObject_;
     gc::InitialHeap initialHeap_;
 
-    MNewArrayCopyOnWrite(CompilerConstraintList* constraints, ArrayObject* templateObject,
-                         gc::InitialHeap initialHeap)
+    MNewArrayCopyOnWrite(TempAllocator& alloc, CompilerConstraintList* constraints,
+                         ArrayObject* templateObject, gc::InitialHeap initialHeap)
       : templateObject_(templateObject),
         initialHeap_(initialHeap)
     {
         MOZ_ASSERT(!templateObject->isSingleton());
         setResultType(MIRType::Object);
-        setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(NewArrayCopyOnWrite)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     ArrayObject* templateObject() const {
         return templateObject_;
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
@@ -3342,31 +3354,32 @@ class MNewArrayCopyOnWrite : public MNul
 
 class MNewArrayDynamicLength
   : public MUnaryInstruction,
     public IntPolicy<0>::Data
 {
     CompilerObject templateObject_;
     gc::InitialHeap initialHeap_;
 
-    MNewArrayDynamicLength(CompilerConstraintList* constraints, JSObject* templateObject,
-                           gc::InitialHeap initialHeap, MDefinition* length)
+    MNewArrayDynamicLength(TempAllocator& alloc, CompilerConstraintList* constraints,
+                           JSObject* templateObject, gc::InitialHeap initialHeap,
+                           MDefinition* length)
       : MUnaryInstruction(length),
         templateObject_(templateObject),
         initialHeap_(initialHeap)
     {
         setGuard(); // Need to throw if length is negative.
         setResultType(MIRType::Object);
         if (!templateObject->isSingleton())
-            setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
+            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(NewArrayDynamicLength)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
     NAMED_OPERANDS((0, length))
 
     JSObject* templateObject() const {
         return templateObject_;
     }
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
@@ -3381,29 +3394,29 @@ class MNewArrayDynamicLength
 };
 
 class MNewTypedArray
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
     gc::InitialHeap initialHeap_;
 
-    MNewTypedArray(CompilerConstraintList* constraints, MConstant* templateConst,
-                   gc::InitialHeap initialHeap)
+    MNewTypedArray(TempAllocator& alloc, CompilerConstraintList* constraints,
+                   MConstant* templateConst, gc::InitialHeap initialHeap)
       : MUnaryInstruction(templateConst),
         initialHeap_(initialHeap)
     {
         MOZ_ASSERT(!templateObject()->isSingleton());
         setResultType(MIRType::Object);
-        setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject()));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
     }
 
   public:
     INSTRUCTION_HEADER(NewTypedArray)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     TypedArrayObject* templateObject() const {
         return &getOperand(0)->toConstant()->toObject().as<TypedArrayObject>();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
@@ -3420,37 +3433,32 @@ class MNewTypedArray
 
 class MNewTypedArrayDynamicLength
   : public MUnaryInstruction,
     public IntPolicy<0>::Data
 {
     CompilerObject templateObject_;
     gc::InitialHeap initialHeap_;
 
-    MNewTypedArrayDynamicLength(CompilerConstraintList* constraints, JSObject* templateObject,
-                           gc::InitialHeap initialHeap, MDefinition* length)
+    MNewTypedArrayDynamicLength(TempAllocator& alloc, CompilerConstraintList* constraints,
+                                JSObject* templateObject, gc::InitialHeap initialHeap,
+                                MDefinition* length)
       : MUnaryInstruction(length),
         templateObject_(templateObject),
         initialHeap_(initialHeap)
     {
         setGuard(); // Need to throw if length is negative.
         setResultType(MIRType::Object);
         if (!templateObject->isSingleton())
-            setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
+            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(NewTypedArrayDynamicLength)
-
-    static MNewTypedArrayDynamicLength* New(TempAllocator& alloc, CompilerConstraintList* constraints,
-                                            JSObject* templateObject, gc::InitialHeap initialHeap,
-                                            MDefinition* length)
-    {
-        return new(alloc) MNewTypedArrayDynamicLength(constraints, templateObject, initialHeap, length);
-    }
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     MDefinition* length() const {
         return getOperand(0);
     }
     JSObject* templateObject() const {
         return templateObject_;
     }
     gc::InitialHeap initialHeap() const {
@@ -3473,47 +3481,47 @@ class MNewObject
   public:
     enum Mode { ObjectLiteral, ObjectCreate };
 
   private:
     gc::InitialHeap initialHeap_;
     Mode mode_;
     bool vmCall_;
 
-    MNewObject(CompilerConstraintList* constraints, MConstant* templateConst,
+    MNewObject(TempAllocator& alloc, CompilerConstraintList* constraints, MConstant* templateConst,
                gc::InitialHeap initialHeap, Mode mode, bool vmCall = false)
       : MUnaryInstruction(templateConst),
         initialHeap_(initialHeap),
         mode_(mode),
         vmCall_(vmCall)
     {
         MOZ_ASSERT_IF(mode != ObjectLiteral, templateObject());
         setResultType(MIRType::Object);
 
         if (JSObject* obj = templateObject())
-            setResultTypeSet(MakeSingletonTypeSet(constraints, obj));
+            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, obj));
 
         // The constant is kept separated in a MConstant, this way we can safely
         // mark it during GC if we recover the object allocation.  Otherwise, by
         // making it emittedAtUses, we do not produce register allocations for
         // it and inline its content inside the code produced by the
         // CodeGenerator.
         if (templateConst->toConstant()->type() == MIRType::Object)
             templateConst->setEmittedAtUses();
     }
 
   public:
     INSTRUCTION_HEADER(NewObject)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     static MNewObject* NewVM(TempAllocator& alloc, CompilerConstraintList* constraints,
                              MConstant* templateConst, gc::InitialHeap initialHeap,
                              Mode mode)
     {
-        return new(alloc) MNewObject(constraints, templateConst, initialHeap, mode, true);
+        return new(alloc) MNewObject(alloc, constraints, templateConst, initialHeap, mode, true);
     }
 
     Mode mode() const {
         return mode_;
     }
 
     JSObject* templateObject() const {
         return getOperand(0)->toConstant()->toObjectOrNull();
@@ -3544,28 +3552,29 @@ class MNewIterator
     enum Type {
         ArrayIterator,
         StringIterator,
     };
 
 private:
     Type type_;
 
-    MNewIterator(CompilerConstraintList* constraints, MConstant* templateConst, Type type)
+    MNewIterator(TempAllocator& alloc, CompilerConstraintList* constraints,
+                 MConstant* templateConst, Type type)
       : MUnaryInstruction(templateConst),
         type_(type)
     {
         setResultType(MIRType::Object);
-        setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject()));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
         templateConst->setEmittedAtUses();
     }
 
   public:
     INSTRUCTION_HEADER(NewIterator)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     Type type() const {
         return type_;
     }
 
     JSObject* templateObject() {
         return getOperand(0)->toConstant()->toObjectOrNull();
     }
@@ -3581,29 +3590,29 @@ private:
 };
 
 
 class MNewTypedObject : public MNullaryInstruction
 {
     CompilerGCPointer<InlineTypedObject*> templateObject_;
     gc::InitialHeap initialHeap_;
 
-    MNewTypedObject(CompilerConstraintList* constraints,
+    MNewTypedObject(TempAllocator& alloc, CompilerConstraintList* constraints,
                     InlineTypedObject* templateObject,
                     gc::InitialHeap initialHeap)
       : templateObject_(templateObject),
         initialHeap_(initialHeap)
     {
         setResultType(MIRType::Object);
-        setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(NewTypedObject)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     InlineTypedObject* templateObject() const {
         return templateObject_;
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
@@ -3649,36 +3658,37 @@ class MSimdBox
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   protected:
     CompilerGCPointer<InlineTypedObject*> templateObject_;
     SimdType simdType_;
     gc::InitialHeap initialHeap_;
 
-    MSimdBox(CompilerConstraintList* constraints,
+    MSimdBox(TempAllocator& alloc,
+             CompilerConstraintList* constraints,
              MDefinition* op,
              InlineTypedObject* templateObject,
              SimdType simdType,
              gc::InitialHeap initialHeap)
       : MUnaryInstruction(op),
         templateObject_(templateObject),
         simdType_(simdType),
         initialHeap_(initialHeap)
     {
         MOZ_ASSERT(IsSimdType(op->type()));
         setMovable();
         setResultType(MIRType::Object);
         if (constraints)
-            setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
+            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(SimdBox)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     InlineTypedObject* templateObject() const {
         return templateObject_;
     }
 
     SimdType simdType() const {
         return simdType_;
     }
@@ -4994,28 +5004,28 @@ class MAssertRange
 // Caller-side allocation of |this| for |new|:
 // Given a templateobject, construct |this| for JSOP_NEW
 class MCreateThisWithTemplate
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
     gc::InitialHeap initialHeap_;
 
-    MCreateThisWithTemplate(CompilerConstraintList* constraints, MConstant* templateConst,
-                            gc::InitialHeap initialHeap)
+    MCreateThisWithTemplate(TempAllocator& alloc, CompilerConstraintList* constraints,
+                            MConstant* templateConst, gc::InitialHeap initialHeap)
       : MUnaryInstruction(templateConst),
         initialHeap_(initialHeap)
     {
         setResultType(MIRType::Object);
-        setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject()));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject()));
     }
 
   public:
     INSTRUCTION_HEADER(CreateThisWithTemplate)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     // Template for |this|, provided by TI.
     JSObject* templateObject() const {
         return &getOperand(0)->toConstant()->toObject();
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
@@ -7474,16 +7484,52 @@ class MFromCodePoint
     }
     bool congruentTo(const MDefinition* ins) const override {
         return congruentIfOperandsEqual(ins);
     }
 
     ALLOW_CLONE(MFromCodePoint)
 };
 
+class MStringConvertCase
+  : public MUnaryInstruction,
+    public StringPolicy<0>::Data
+{
+  public:
+    enum Mode { LowerCase, UpperCase };
+
+  private:
+    Mode mode_;
+
+    MStringConvertCase(MDefinition* string, Mode mode)
+      : MUnaryInstruction(string), mode_(mode)
+    {
+        setResultType(MIRType::String);
+        setMovable();
+    }
+
+  public:
+    INSTRUCTION_HEADER(StringConvertCase)
+    TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, string))
+
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins) && ins->toStringConvertCase()->mode() == mode();
+    }
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+    bool possiblyCalls() const override {
+        return true;
+    }
+    Mode mode() const {
+        return mode_;
+    }
+};
+
 class MSinCos
   : public MUnaryInstruction,
     public FloatingPointPolicy<0>::Data
 {
     const MathCache* cache_;
 
     MSinCos(MDefinition *input, const MathCache *cache) : MUnaryInstruction(input), cache_(cache)
     {
@@ -7514,29 +7560,29 @@ class MSinCos
 };
 
 class MStringSplit
   : public MBinaryInstruction,
     public MixPolicy<StringPolicy<0>, StringPolicy<1> >::Data
 {
     CompilerObjectGroup group_;
 
-    MStringSplit(CompilerConstraintList* constraints, MDefinition* string, MDefinition* sep,
-                 ObjectGroup* group)
+    MStringSplit(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* string,
+                 MDefinition* sep, ObjectGroup* group)
       : MBinaryInstruction(string, sep),
         group_(group)
     {
         setResultType(MIRType::Object);
-        TemporaryTypeSet* types = MakeSingletonTypeSet(constraints, group);
+        TemporaryTypeSet* types = MakeSingletonTypeSet(alloc, constraints, group);
         setResultTypeSet(types);
     }
 
   public:
     INSTRUCTION_HEADER(StringSplit)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
     NAMED_OPERANDS((0, string), (1, separator))
 
     ObjectGroup* group() const {
         return group_;
     }
     bool possiblyCalls() const override {
         return true;
     }
@@ -8219,28 +8265,29 @@ class MDefFun
 };
 
 class MRegExp : public MNullaryInstruction
 {
     CompilerGCPointer<RegExpObject*> source_;
     bool mustClone_;
     bool hasShared_;
 
-    MRegExp(CompilerConstraintList* constraints, RegExpObject* source, bool hasShared)
+    MRegExp(TempAllocator& alloc, CompilerConstraintList* constraints, RegExpObject* source,
+            bool hasShared)
       : source_(source),
         mustClone_(true),
         hasShared_(hasShared)
     {
         setResultType(MIRType::Object);
-        setResultTypeSet(MakeSingletonTypeSet(constraints, source));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, source));
     }
 
   public:
     INSTRUCTION_HEADER(RegExp)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
 
     void setDoNotClone() {
         mustClone_ = false;
     }
     bool mustClone() const {
         return mustClone_;
     }
     bool hasShared() const {
@@ -8566,27 +8613,28 @@ struct LambdaFunctionInfo
 };
 
 class MLambda
   : public MBinaryInstruction,
     public SingleObjectPolicy::Data
 {
     const LambdaFunctionInfo info_;
 
-    MLambda(CompilerConstraintList* constraints, MDefinition* envChain, MConstant* cst)
+    MLambda(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* envChain,
+            MConstant* cst)
       : MBinaryInstruction(envChain, cst), info_(&cst->toObject().as<JSFunction>())
     {
         setResultType(MIRType::Object);
         if (!info().fun->isSingleton() && !ObjectGroup::useSingletonForClone(info().fun))
-            setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun));
+            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, info().fun));
     }
 
   public:
     INSTRUCTION_HEADER(Lambda)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
     NAMED_OPERANDS((0, environmentChain))
 
     MConstant* functionOperand() const {
         return getOperand(1)->toConstant();
     }
     const LambdaFunctionInfo& info() const {
         return info_;
     }
@@ -8600,30 +8648,30 @@ class MLambda
 };
 
 class MLambdaArrow
   : public MTernaryInstruction,
     public Mix3Policy<ObjectPolicy<0>, BoxPolicy<1>, ObjectPolicy<2>>::Data
 {
     const LambdaFunctionInfo info_;
 
-    MLambdaArrow(CompilerConstraintList* constraints, MDefinition* envChain,
+    MLambdaArrow(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* envChain,
                  MDefinition* newTarget, MConstant* cst)
       : MTernaryInstruction(envChain, newTarget, cst),
         info_(&cst->toObject().as<JSFunction>())
     {
         setResultType(MIRType::Object);
         MOZ_ASSERT(!ObjectGroup::useSingletonForClone(info().fun));
         if (!info().fun->isSingleton())
-            setResultTypeSet(MakeSingletonTypeSet(constraints, info().fun));
+            setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, info().fun));
     }
 
   public:
     INSTRUCTION_HEADER(LambdaArrow)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
     NAMED_OPERANDS((0, environmentChain), (1, newTargetDef))
 
     MConstant* functionOperand() const {
         return getOperand(2)->toConstant();
     }
     const LambdaFunctionInfo& info() const {
         return info_;
     }
@@ -10884,17 +10932,17 @@ class InlinePropertyTable : public TempO
     JSFunction* getFunction(size_t i) const {
         MOZ_ASSERT(i < numEntries());
         return entries_[i]->func;
     }
 
     bool hasFunction(JSFunction* func) const;
     bool hasObjectGroup(ObjectGroup* group) const;
 
-    TemporaryTypeSet* buildTypeSetForFunction(JSFunction* func) const;
+    TemporaryTypeSet* buildTypeSetForFunction(TempAllocator& tempAlloc, JSFunction* func) const;
 
     // Remove targets that vetoed inlining from the InlinePropertyTable.
     void trimTo(const InliningTargets& targets, const BoolVector& choiceSet);
 
     // Ensure that the InlinePropertyTable's domain is a subset of |targets|.
     void trimToTargets(const InliningTargets& targets);
 
     bool appendRoots(MRootList& roots) const;
@@ -12874,28 +12922,28 @@ class MRestCommon
     }
 };
 
 class MRest
   : public MUnaryInstruction,
     public MRestCommon,
     public IntPolicy<0>::Data
 {
-    MRest(CompilerConstraintList* constraints, MDefinition* numActuals, unsigned numFormals,
-          ArrayObject* templateObject)
+    MRest(TempAllocator& alloc, CompilerConstraintList* constraints, MDefinition* numActuals,
+          unsigned numFormals, ArrayObject* templateObject)
       : MUnaryInstruction(numActuals),
         MRestCommon(numFormals, templateObject)
     {
         setResultType(MIRType::Object);
-        setResultTypeSet(MakeSingletonTypeSet(constraints, templateObject));
+        setResultTypeSet(MakeSingletonTypeSet(alloc, constraints, templateObject));
     }
 
   public:
     INSTRUCTION_HEADER(Rest)
-    TRIVIAL_NEW_WRAPPERS
+    TRIVIAL_NEW_WRAPPERS_WITH_ALLOC
     NAMED_OPERANDS((0, numActuals))
 
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
@@ -14879,30 +14927,32 @@ bool ElementAccessIsTypedArray(CompilerC
                                Scalar::Type* arrayType);
 bool ElementAccessIsPacked(CompilerConstraintList* constraints, MDefinition* obj);
 bool ElementAccessMightBeCopyOnWrite(CompilerConstraintList* constraints, MDefinition* obj);
 bool ElementAccessMightBeFrozen(CompilerConstraintList* constraints, MDefinition* obj);
 AbortReasonOr<bool>
 ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj);
 MIRType DenseNativeElementType(CompilerConstraintList* constraints, MDefinition* obj);
 BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx,
+                                         TempAllocator& alloc,
                                          CompilerConstraintList* constraints,
                                          TypeSet::ObjectKey* key, PropertyName* name,
                                          TemporaryTypeSet* observed, bool updateObserved);
 BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx,
+                                         TempAllocator& alloc,
                                          CompilerConstraintList* constraints,
                                          MDefinition* obj, PropertyName* name,
                                          TemporaryTypeSet* observed);
 AbortReasonOr<BarrierKind>
 PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder,
                                         MDefinition* obj, PropertyName* name,
                                         TemporaryTypeSet* observed);
 bool PropertyReadIsIdempotent(CompilerConstraintList* constraints,
                               MDefinition* obj, PropertyName* name);
-void AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name,
+void AddObjectsForPropertyRead(TempAllocator& tempAlloc, MDefinition* obj, PropertyName* name,
                                TemporaryTypeSet* observed);
 bool CanWriteProperty(TempAllocator& alloc, CompilerConstraintList* constraints,
                       HeapTypeSetKey property, MDefinition* value,
                       MIRType implicitType = MIRType::None);
 bool PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints,
                                    MBasicBlock* current, MDefinition** pobj,
                                    PropertyName* name, MDefinition** pvalue,
                                    bool canModify, MIRType implicitType = MIRType::None);
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -48,24 +48,27 @@ class MIRGenerator
     }
     MIRGraph& graph() {
         return *graph_;
     }
     MOZ_MUST_USE bool ensureBallast() {
         return alloc().ensureBallast();
     }
     const JitRuntime* jitRuntime() const {
-        return GetJitContext()->runtime->jitRuntime();
+        return runtime->jitRuntime();
     }
     const CompileInfo& info() const {
         return *info_;
     }
     const OptimizationInfo& optimizationInfo() const {
         return *optimizationInfo_;
     }
+    bool hasProfilingScripts() const {
+        return runtime && runtime->profilingScripts();
+    }
 
     template <typename T>
     T* allocate(size_t count = 1) {
         size_t bytes;
         if (MOZ_UNLIKELY(!CalculateAllocSize<T>(count, &bytes)))
             return nullptr;
         return static_cast<T*>(alloc().allocate(bytes));
     }
@@ -86,17 +89,17 @@ class MIRGenerator
         offThreadStatus_ = result;
     }
     AbortReasonOr<Ok> getOffThreadStatus() const {
         return offThreadStatus_;
     }
 
     MOZ_MUST_USE bool instrumentedProfiling() {
         if (!instrumentedProfilingIsCached_) {
-            instrumentedProfiling_ = GetJitContext()->runtime->geckoProfiler().enabled();
+            instrumentedProfiling_ = runtime->geckoProfiler().enabled();
             instrumentedProfilingIsCached_ = true;
         }
         return instrumentedProfiling_;
     }
 
     bool isProfilerInstrumentationEnabled() {
         return !compilingWasm() && instrumentedProfiling();
     }
@@ -174,16 +177,17 @@ class MIRGenerator
     // When aborting with AbortReason::PreliminaryObjects, all groups with
     // preliminary objects which haven't been analyzed yet.
     const ObjectGroupVector& abortedPreliminaryGroups() const {
         return abortedPreliminaryGroups_;
     }
 
   public:
     CompileCompartment* compartment;
+    CompileRuntime* runtime;
 
   protected:
     const CompileInfo* info_;
     const OptimizationInfo* optimizationInfo_;
     TempAllocator* alloc_;
     MIRGraph* graph_;
     AbortReasonOr<Ok> offThreadStatus_;
     ObjectGroupVector abortedPreliminaryGroups_;
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -16,16 +16,17 @@
 using namespace js;
 using namespace js::jit;
 using mozilla::Swap;
 
 MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOptions& options,
                            TempAllocator* alloc, MIRGraph* graph, const CompileInfo* info,
                            const OptimizationInfo* optimizationInfo)
   : compartment(compartment),
+    runtime(compartment ? compartment->runtime() : nullptr),
     info_(info),
     optimizationInfo_(optimizationInfo),
     alloc_(alloc),
     graph_(graph),
     offThreadStatus_(Ok()),
     abortedPreliminaryGroups_(*alloc_),
     pauseBuild_(nullptr),
     cancelBuild_(false),
@@ -657,31 +658,16 @@ MBasicBlock::initEntrySlots(TempAllocato
 
     // Create a resume point using our initial stack state.
     entryResumePoint_ = MResumePoint::New(alloc, this, pc(), MResumePoint::ResumeAt);
     if (!entryResumePoint_)
         return false;
     return true;
 }
 
-MDefinition*
-MBasicBlock::getSlot(uint32_t index)
-{
-    MOZ_ASSERT(index < stackPosition_);
-    return slots_[index];
-}
-
-void
-MBasicBlock::initSlot(uint32_t slot, MDefinition* ins)
-{
-    slots_[slot] = ins;
-    if (entryResumePoint())
-        entryResumePoint()->initOperand(slot, ins);
-}
-
 void
 MBasicBlock::shimmySlots(int discardDepth)
 {
     // Move all slots above the given depth down by one,
     // overwriting the MDefinition at discardDepth.
 
     MOZ_ASSERT(discardDepth < 0);
     MOZ_ASSERT(stackPosition_ + discardDepth >= info_.firstStackSlot());
@@ -735,106 +721,23 @@ MBasicBlock::linkOsrValues(MStart* start
             cloneRp->setResumePoint(clone);
         }
     }
 
     return true;
 }
 
 void
-MBasicBlock::setSlot(uint32_t slot, MDefinition* ins)
-{
-    slots_[slot] = ins;
-}
-
-void
-MBasicBlock::setVariable(uint32_t index)
-{
-    MOZ_ASSERT(stackPosition_ > info_.firstStackSlot());
-    setSlot(index, slots_[stackPosition_ - 1]);
-}
-
-void
-MBasicBlock::setArg(uint32_t arg)
-{
-    setVariable(info_.argSlot(arg));
-}
-
-void
-MBasicBlock::setLocal(uint32_t local)
-{
-    setVariable(info_.localSlot(local));
-}
-
-void
-MBasicBlock::setSlot(uint32_t slot)
-{
-    setVariable(slot);
-}
-
-void
-MBasicBlock::rewriteSlot(uint32_t slot, MDefinition* ins)
-{
-    setSlot(slot, ins);
-}
-
-void
 MBasicBlock::rewriteAtDepth(int32_t depth, MDefinition* ins)
 {
     MOZ_ASSERT(depth < 0);
     MOZ_ASSERT(stackPosition_ + depth >= info_.firstStackSlot());
     rewriteSlot(stackPosition_ + depth, ins);
 }
 
-void
-MBasicBlock::push(MDefinition* ins)
-{
-    MOZ_ASSERT(stackPosition_ < nslots());
-    slots_[stackPosition_++] = ins;
-}
-
-void
-MBasicBlock::pushVariable(uint32_t slot)
-{
-    push(slots_[slot]);
-}
-
-void
-MBasicBlock::pushArg(uint32_t arg)
-{
-    pushVariable(info_.argSlot(arg));
-}
-
-void
-MBasicBlock::pushLocal(uint32_t local)
-{
-    pushVariable(info_.localSlot(local));
-}
-
-void
-MBasicBlock::pushSlot(uint32_t slot)
-{
-    pushVariable(slot);
-}
-
-MDefinition*
-MBasicBlock::pop()
-{
-    MOZ_ASSERT(stackPosition_ > info_.firstStackSlot());
-    return slots_[--stackPosition_];
-}
-
-void
-MBasicBlock::popn(uint32_t n)
-{
-    MOZ_ASSERT(stackPosition_ - n >= info_.firstStackSlot());
-    MOZ_ASSERT(stackPosition_ >= stackPosition_ - n);
-    stackPosition_ -= n;
-}
-
 MDefinition*
 MBasicBlock::environmentChain()
 {
     return getSlot(info().environmentChainSlot());
 }
 
 MDefinition*
 MBasicBlock::argumentsObject()
@@ -885,24 +788,16 @@ MBasicBlock::swapAt(int32_t depth)
     uint32_t lhsDepth = stackPosition_ + depth - 1;
     uint32_t rhsDepth = stackPosition_ + depth;
 
     MDefinition* temp = slots_[lhsDepth];
     slots_[lhsDepth] = slots_[rhsDepth];
     slots_[rhsDepth] = temp;
 }
 
-MDefinition*
-MBasicBlock::peek(int32_t depth)
-{
-    MOZ_ASSERT(depth < 0);
-    MOZ_ASSERT(stackPosition_ + depth >= info_.firstStackSlot());
-    return getSlot(stackPosition_ + depth);
-}
-
 void
 MBasicBlock::discardLastIns()
 {
     discard(lastIns());
 }
 
 MConstant*
 MBasicBlock::optimizedOutConstant(TempAllocator& alloc)
@@ -1133,34 +1028,16 @@ MBasicBlock::insertAtEnd(MInstruction* i
 {
     if (hasLastIns())
         insertBefore(lastIns(), ins);
     else
         add(ins);
 }
 
 void
-MBasicBlock::add(MInstruction* ins)
-{
-    MOZ_ASSERT(!hasLastIns());
-    ins->setBlock(this);
-    graph().allocDefinitionId(ins);
-    instructions_.pushBack(ins);
-    ins->setTrackedSite(trackedSite_);
-}
-
-void
-MBasicBlock::end(MControlInstruction* ins)
-{
-    MOZ_ASSERT(!hasLastIns()); // Existing control instructions should be removed first.
-    MOZ_ASSERT(ins);
-    add(ins);
-}
-
-void
 MBasicBlock::addPhi(MPhi* phi)
 {
     phis_.pushBack(phi);
     phi->setBlock(this);
     graph().allocDefinitionId(phi);
 }
 
 void
@@ -1456,30 +1333,16 @@ MBasicBlock::setLoopHeader(MBasicBlock* 
         }
     }
 
     MOZ_ASSERT(newBackedge->loopHeaderOfBackedge() == this);
     MOZ_ASSERT(backedge() == newBackedge);
 }
 
 size_t
-MBasicBlock::numSuccessors() const
-{
-    MOZ_ASSERT(lastIns());
-    return lastIns()->numSuccessors();
-}
-
-MBasicBlock*
-MBasicBlock::getSuccessor(size_t index) const
-{
-    MOZ_ASSERT(lastIns());
-    return lastIns()->getSuccessor(index);
-}
-
-size_t
 MBasicBlock::getSuccessorIndex(MBasicBlock* block) const
 {
     MOZ_ASSERT(lastIns());
     for (size_t i = 0; i < numSuccessors(); i++) {
         if (getSuccessor(i) == block)
             return i;
     }
     MOZ_CRASH("Invalid successor");
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -56,21 +56,26 @@ class MBasicBlock : public TempObject, p
 
     // This block cannot be reached by any means.
     bool unreachable_;
 
     // Keeps track if the phis has been type specialized already.
     bool specialized_;
 
     // Pushes a copy of a local variable or argument.
-    void pushVariable(uint32_t slot);
+    void pushVariable(uint32_t slot) {
+        push(slots_[slot]);
+    }
 
     // Sets a variable slot to the top of the stack, correctly creating copies
     // as needed.
-    void setVariable(uint32_t slot);
+    void setVariable(uint32_t slot) {
+        MOZ_ASSERT(stackPosition_ > info_.firstStackSlot());
+        setSlot(slot, slots_[stackPosition_ - 1]);
+    }
 
     enum ReferencesType {
         RefType_None = 0,
 
         // Assert that the instruction is unused.
         RefType_AssertNoUses = 1 << 0,
 
         // Discard the operands of the resume point / instructions if the
@@ -146,69 +151,107 @@ class MBasicBlock : public TempObject, p
     void pick(int32_t depth);
 
     // Move the top of the stack definition under the depth-th stack value.
     void unpick(int32_t depth);
 
     // Exchange 2 stack slots at the defined depth
     void swapAt(int32_t depth);
 
+    // Note: most of the methods below are hot. Do not un-inline them without
+    // measuring the impact.
+
     // Gets the instruction associated with various slot types.
-    MDefinition* peek(int32_t depth);
+    MDefinition* peek(int32_t depth) {
+        MOZ_ASSERT(depth < 0);
+        MOZ_ASSERT(stackPosition_ + depth >= info_.firstStackSlot());
+        return getSlot(stackPosition_ + depth);
+    }
 
     MDefinition* environmentChain();
     MDefinition* argumentsObject();
 
     // Increase the number of slots available
     MOZ_MUST_USE bool increaseSlots(size_t num);
     MOZ_MUST_USE bool ensureHasSlots(size_t num);
 
     // Initializes a slot value; must not be called for normal stack
     // operations, as it will not create new SSA names for copies.
-    void initSlot(uint32_t index, MDefinition* ins);
+    void initSlot(uint32_t slot, MDefinition* ins) {
+        slots_[slot] = ins;
+        if (entryResumePoint())
+            entryResumePoint()->initOperand(slot, ins);
+    }
 
     // Discard the slot at the given depth, lowering all slots above.
     void shimmySlots(int discardDepth);
 
     // In an OSR block, set all MOsrValues to use the MResumePoint attached to
     // the MStart.
     MOZ_MUST_USE bool linkOsrValues(MStart* start);
 
     // Sets the instruction associated with various slot types. The
     // instruction must lie at the top of the stack.
-    void setLocal(uint32_t local);
-    void setArg(uint32_t arg);
-    void setSlot(uint32_t slot);
-    void setSlot(uint32_t slot, MDefinition* ins);
+    void setLocal(uint32_t local) {
+        setVariable(info_.localSlot(local));
+    }
+    void setArg(uint32_t arg) {
+        setVariable(info_.argSlot(arg));
+    }
+    void setSlot(uint32_t slot, MDefinition* ins) {
+        slots_[slot] = ins;
+    }
 
     // Rewrites a slot directly, bypassing the stack transition. This should
     // not be used under most circumstances.
-    void rewriteSlot(uint32_t slot, MDefinition* ins);
+    void rewriteSlot(uint32_t slot, MDefinition* ins) {
+        setSlot(slot, ins);
+    }
 
     // Rewrites a slot based on its depth (same as argument to peek()).
     void rewriteAtDepth(int32_t depth, MDefinition* ins);
 
     // Tracks an instruction as being pushed onto the operand stack.
-    void push(MDefinition* ins);
-    void pushArg(uint32_t arg);
-    void pushLocal(uint32_t local);
-    void pushSlot(uint32_t slot);
+    void push(MDefinition* ins) {
+        MOZ_ASSERT(stackPosition_ < nslots());
+        slots_[stackPosition_++] = ins;
+    }
+    void pushArg(uint32_t arg) {
+        pushVariable(info_.argSlot(arg));
+    }
+    void pushLocal(uint32_t local) {
+        pushVariable(info_.localSlot(local));
+    }
+    void pushSlot(uint32_t slot) {
+        pushVariable(slot);
+    }
     void setEnvironmentChain(MDefinition* ins);
     void setArgumentsObject(MDefinition* ins);
 
     // Returns the top of the stack, then decrements the virtual stack pointer.
-    MDefinition* pop();
-    void popn(uint32_t n);
+    MDefinition* pop() {
+        MOZ_ASSERT(stackPosition_ > info_.firstStackSlot());
+        return slots_[--stackPosition_];
+    }
+    void popn(uint32_t n) {
+        MOZ_ASSERT(stackPosition_ - n >= info_.firstStackSlot());
+        MOZ_ASSERT(stackPosition_ >= stackPosition_ - n);
+        stackPosition_ -= n;
+    }
 
     // Adds an instruction to this block's instruction list.
-    void add(MInstruction* ins);
+    inline void add(MInstruction* ins);
 
     // Marks the last instruction of the block; no further instructions
     // can be added.
-    void end(MControlInstruction* ins);
+    void end(MControlInstruction* ins) {
+        MOZ_ASSERT(!hasLastIns()); // Existing control instructions should be removed first.
+        MOZ_ASSERT(ins);
+        add(ins);
+    }
 
     // Adds a phi instruction, but does not set successorWithPhis.
     void addPhi(MPhi* phi);
 
     // Adds a resume point to this block.
     void addResumePoint(MResumePoint* resume) {
 #ifdef DEBUG
         resumePoints_.pushFront(resume);
@@ -535,17 +578,20 @@ class MBasicBlock : public TempObject, p
     bool addImmediatelyDominatedBlock(MBasicBlock* child);
 
     // Remove |child| from this block's immediately-dominated set.
     void removeImmediatelyDominatedBlock(MBasicBlock* child);
 
     // This function retrieves the internal instruction associated with a
     // slot, and should not be used for normal stack operations. It is an
     // internal helper that is also used to enhance spew.
-    MDefinition* getSlot(uint32_t index);
+    MDefinition* getSlot(uint32_t index) {
+        MOZ_ASSERT(index < stackPosition_);
+        return slots_[index];
+    }
 
     MResumePoint* entryResumePoint() const {
         return entryResumePoint_;
     }
     void setEntryResumePoint(MResumePoint* rp) {
         entryResumePoint_ = rp;
     }
     void clearEntryResumePoint() {
@@ -594,18 +640,24 @@ class MBasicBlock : public TempObject, p
     }
     void setSuccessorWithPhis(MBasicBlock* successor, uint32_t id) {
         successorWithPhis_ = successor;
         positionInPhiSuccessor_ = id;
     }
     void clearSuccessorWithPhis() {
         successorWithPhis_ = nullptr;
     }
-    size_t numSuccessors() const;
-    MBasicBlock* getSuccessor(size_t index) const;
+    size_t numSuccessors() const {
+        MOZ_ASSERT(lastIns());
+        return lastIns()->numSuccessors();
+    }
+    MBasicBlock* getSuccessor(size_t index) const {
+        MOZ_ASSERT(lastIns());
+        return lastIns()->getSuccessor(index);
+    }
     size_t getSuccessorIndex(MBasicBlock*) const;
     size_t getPredecessorIndex(MBasicBlock*) const;
 
     void setLoopDepth(uint32_t loopDepth) {
         loopDepth_ = loopDepth;
     }
     uint32_t loopDepth() const {
         return loopDepth_;
@@ -1059,12 +1111,22 @@ class MNodeIterator
     }
 
     MNode* operator ->() {
         return getNode();
     }
 
 };
 
+void
+MBasicBlock::add(MInstruction* ins)
+{
+    MOZ_ASSERT(!hasLastIns());
+    ins->setBlock(this);
+    graph().allocDefinitionId(ins);
+    instructions_.pushBack(ins);
+    ins->setTrackedSite(trackedSite_);
+}
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_MIRGraph_h */
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -105,16 +105,17 @@ namespace jit {
     _(Sub)                                                                  \
     _(Mul)                                                                  \
     _(Div)                                                                  \
     _(Mod)                                                                  \
     _(Concat)                                                               \
     _(CharCodeAt)                                                           \
     _(FromCharCode)                                                         \
     _(FromCodePoint)                                                        \
+    _(StringConvertCase)                                                    \
     _(SinCos)                                                               \
     _(StringSplit)                                                          \
     _(Substr)                                                               \
     _(Return)                                                               \
     _(Throw)                                                                \
     _(Box)                                                                  \
     _(Unbox)                                                                \
     _(GuardObject)                                                          \
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1481,28 +1481,29 @@ MacroAssembler::typeOfObject(Register ob
               isObject);
 
     jump(isCallable);
 }
 
 void
 MacroAssembler::loadJSContext(Register dest)
 {
-    CompileCompartment* compartment = GetJitContext()->compartment;
+    JitContext* jcx = GetJitContext();
+    CompileCompartment* compartment = jcx->compartment;
     if (compartment->zone()->isAtomsZone()) {
         // If we are in the atoms zone then we are generating a runtime wide
         // trampoline which can run in any zone. Load the context which is
         // currently running using cooperative scheduling in the runtime.
         // (This will need to be fixed when we have preemptive scheduling,
         // bug 1323066).
-        loadPtr(AbsoluteAddress(GetJitContext()->runtime->addressOfActiveJSContext()), dest);
+        loadPtr(AbsoluteAddress(jcx->runtime->addressOfActiveJSContext()), dest);
     } else {
         // If we are in a specific zone then the current context will be stored
         // in the containing zone group.
-        loadPtr(AbsoluteAddress(GetJitContext()->compartment->zone()->addressOfJSContext()), dest);
+        loadPtr(AbsoluteAddress(compartment->zone()->addressOfJSContext()), dest);
     }
 }
 
 void
 MacroAssembler::guardGroupHasUnanalyzedNewScript(Register group, Register scratch, Label* fail)
 {
     Label noNewScript;
     load32(Address(group, ObjectGroup::offsetOfFlags()), scratch);
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1343,17 +1343,17 @@ CodeGeneratorShared::resetOsiPointRegs(L
 
 // Before doing any call to Cpp, you should ensure that volatile
 // registers are evicted by the register allocator.
 void
 CodeGeneratorShared::callVM(const VMFunction& fun, LInstruction* ins, const Register* dynStack)
 {
     // If we're calling a function with an out parameter type of double, make
     // sure we have an FPU.
-    MOZ_ASSERT_IF(fun.outParam == Type_Double, GetJitContext()->runtime->jitSupportsFloatingPoint());
+    MOZ_ASSERT_IF(fun.outParam == Type_Double, gen->runtime->jitSupportsFloatingPoint());
 
 #ifdef DEBUG
     if (ins->mirRaw()) {
         MOZ_ASSERT(ins->mirRaw()->isInstruction());
         MInstruction* mir = ins->mirRaw()->toInstruction();
         MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint());
     }
 #endif
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -4106,16 +4106,36 @@ class LFromCodePoint : public LInstructi
         return this->getTemp(0);
     }
 
     const LDefinition* temp2() {
         return this->getTemp(1);
     }
 };
 
+// Calls the ToLowerCase or ToUpperCase case conversion function.
+class LStringConvertCase : public LCallInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(StringConvertCase)
+
+    explicit LStringConvertCase(const LAllocation& string)
+    {
+        setOperand(0, string);
+    }
+
+    const MStringConvertCase* mir() const {
+        return mir_->toStringConvertCase();
+    }
+
+    const LAllocation* string() {
+        return this->getOperand(0);
+    }
+};
+
 // Calculates sincos(x) and returns two values (sin/cos).
 class LSinCos : public LCallInstructionHelper<2, 1, 2>
 {
   public:
     LIR_HEADER(SinCos)
 
     LSinCos(const LAllocation &input, const LDefinition &temp, const LDefinition &temp2)
     {
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -192,16 +192,17 @@
     _(ModI)                         \
     _(ModPowTwoI)                   \
     _(ModD)                         \
     _(BinaryV)                      \
     _(Concat)                       \
     _(CharCodeAt)                   \
     _(FromCharCode)                 \
     _(FromCodePoint)                \
+    _(StringConvertCase)            \
     _(SinCos)                       \
     _(StringSplit)                  \
     _(Int32ToDouble)                \
     _(Float32ToDouble)              \
     _(DoubleToFloat32)              \
     _(Int32ToFloat32)               \
     _(ValueToDouble)                \
     _(ValueToInt32)                 \
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -110,49 +110,16 @@ js::GetVariableBytecodeLength(jsbytecode
         unsigned ncases = unsigned(high - low + 1);
         return 1 + 3 * JUMP_OFFSET_LEN + ncases * JUMP_OFFSET_LEN;
       }
       default:
         MOZ_CRASH("Unexpected op");
     }
 }
 
-unsigned
-js::StackUses(JSScript* script, jsbytecode* pc)
-{
-    JSOp op = (JSOp) *pc;
-    const JSCodeSpec& cs = CodeSpec[op];
-    if (cs.nuses >= 0)
-        return cs.nuses;
-
-    MOZ_ASSERT(CodeSpec[op].nuses == -1);
-    switch (op) {
-      case JSOP_POPN:
-        return GET_UINT16(pc);
-      case JSOP_NEW:
-      case JSOP_SUPERCALL:
-        return 2 + GET_ARGC(pc) + 1;
-      default:
-        /* stack: fun, this, [argc arguments] */
-        MOZ_ASSERT(op == JSOP_CALL || op == JSOP_CALL_IGNORES_RV || op == JSOP_EVAL ||
-                   op == JSOP_CALLITER ||
-                   op == JSOP_STRICTEVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY);
-        return 2 + GET_ARGC(pc);
-    }
-}
-
-unsigned
-js::StackDefs(JSScript* script, jsbytecode* pc)
-{
-    JSOp op = (JSOp) *pc;
-    const JSCodeSpec& cs = CodeSpec[op];
-    MOZ_ASSERT(cs.ndefs >= 0);
-    return cs.ndefs;
-}
-
 const char * PCCounts::numExecName = "interp";
 
 static MOZ_MUST_USE bool
 DumpIonScriptCounts(Sprinter* sp, HandleScript script, jit::IonScriptCounts* ionCounts)
 {
     if (!sp->jsprintf("IonScript [%zu blocks]:\n", ionCounts->numBlocks()))
         return false;
 
@@ -601,18 +568,19 @@ class BytecodeParser
 };
 
 }  // anonymous namespace
 
 uint32_t
 BytecodeParser::simulateOp(JSOp op, uint32_t offset, OffsetAndDefIndex* offsetStack,
                            uint32_t stackDepth)
 {
-    uint32_t nuses = GetUseCount(script_, offset);
-    uint32_t ndefs = GetDefCount(script_, offset);
+    jsbytecode* pc = script_->offsetToPC(offset);
+    uint32_t nuses = GetUseCount(pc);
+    uint32_t ndefs = GetDefCount(pc);
 
     MOZ_ASSERT(stackDepth >= nuses);
     stackDepth -= nuses;
     MOZ_ASSERT(stackDepth + ndefs <= maximumStackDepth());
 
 #ifdef DEBUG
     if (isStackDump) {
         // Opcodes that modifies the object but keeps it on the stack while
@@ -678,45 +646,42 @@ BytecodeParser::simulateOp(JSOp op, uint
       case JSOP_DUP2:
         MOZ_ASSERT(ndefs == 4);
         offsetStack[stackDepth + 2] = offsetStack[stackDepth];
         offsetStack[stackDepth + 3] = offsetStack[stackDepth + 1];
         break;
 
       case JSOP_DUPAT: {
         MOZ_ASSERT(ndefs == 1);
-        jsbytecode* pc = script_->offsetToPC(offset);
         unsigned n = GET_UINT24(pc);
         MOZ_ASSERT(n < stackDepth);
         offsetStack[stackDepth] = offsetStack[stackDepth - 1 - n];
         break;
       }
 
       case JSOP_SWAP: {
         MOZ_ASSERT(ndefs == 2);
         OffsetAndDefIndex tmp = offsetStack[stackDepth + 1];
         offsetStack[stackDepth + 1] = offsetStack[stackDepth];
         offsetStack[stackDepth] = tmp;
         break;
       }
 
       case JSOP_PICK: {
-        jsbytecode* pc = script_->offsetToPC(offset);
         unsigned n = GET_UINT8(pc);
         MOZ_ASSERT(ndefs == n + 1);
         uint32_t top = stackDepth + n;
         OffsetAndDefIndex tmp = offsetStack[stackDepth];
         for (uint32_t i = stackDepth; i < top; i++)
             offsetStack[i] = offsetStack[i + 1];
         offsetStack[top] = tmp;
         break;
       }
 
       case JSOP_UNPICK: {
-        jsbytecode* pc = script_->offsetToPC(offset);
         unsigned n = GET_UINT8(pc);
         MOZ_ASSERT(ndefs == n + 1);
         uint32_t top = stackDepth + n;
         OffsetAndDefIndex tmp = offsetStack[top];
         for (uint32_t i = top; i > stackDepth; i--)
             offsetStack[i] = offsetStack[i - 1];
         offsetStack[stackDepth] = tmp;
         break;
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -521,21 +521,47 @@ class SrcNoteLineScanner
 
     bool isLineHeader() const {
         return lineHeader;
     }
 
     uint32_t getLine() const { return lineno; }
 };
 
-extern unsigned
-StackUses(JSScript* script, jsbytecode* pc);
+MOZ_ALWAYS_INLINE unsigned
+StackUses(jsbytecode* pc)
+{
+    JSOp op = JSOp(*pc);
+    int nuses = CodeSpec[op].nuses;
+    if (nuses >= 0)
+        return nuses;
 
-extern unsigned
-StackDefs(JSScript* script, jsbytecode* pc);
+    MOZ_ASSERT(nuses == -1);
+    switch (op) {
+      case JSOP_POPN:
+        return GET_UINT16(pc);
+      case JSOP_NEW:
+      case JSOP_SUPERCALL:
+        return 2 + GET_ARGC(pc) + 1;