Merge autoland to mozilla-central. a=merge
authorBogdan Tara <btara@mozilla.com>
Thu, 15 Aug 2019 00:57:08 +0300
changeset 487956 7d9a2196d3132f3716652666a945cb1831dfda36
parent 487851 62094e9b468a0903e56b7af177d7590d616a8d5b (current diff)
parent 487955 79127c2c712388688e6cbe5748b0d14d6efb766d (diff)
child 488057 144fbfb409b72b5849ace2a1e3c199c259f7c1d3
child 488083 d65454c57706101473fc1a6e78c20d63c44cff9b
push id36433
push userbtara@mozilla.com
push dateWed, 14 Aug 2019 21:57:52 +0000
treeherdermozilla-central@7d9a2196d313 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone70.0a1
first release with
nightly linux32
7d9a2196d313 / 70.0a1 / 20190814215752 / files
nightly linux64
7d9a2196d313 / 70.0a1 / 20190814215752 / files
nightly mac
7d9a2196d313 / 70.0a1 / 20190814215752 / files
nightly win32
7d9a2196d313 / 70.0a1 / 20190814215752 / files
nightly win64
7d9a2196d313 / 70.0a1 / 20190814215752 / 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 autoland to mozilla-central. a=merge
browser/components/protections/content/protections.ftl
layout/reftests/mathml/dir-11.html
layout/reftests/mathml/dir-6-ref.html
layout/reftests/mathml/dir-6.html
testing/firefox-ui/harness/firefox_ui_harness/arguments/update.py
testing/firefox-ui/harness/firefox_ui_harness/cli_update.py
testing/firefox-ui/harness/firefox_ui_harness/runners/update.py
testing/firefox-ui/harness/firefox_ui_harness/testcases.py
testing/firefox-ui/tests/puppeteer/test_software_update.py
testing/firefox-ui/tests/puppeteer/test_update_wizard.py
testing/firefox-ui/tests/update/direct/manifest.ini
testing/firefox-ui/tests/update/direct/test_direct_update.py
testing/firefox-ui/tests/update/fallback/manifest.ini
testing/firefox-ui/tests/update/fallback/test_fallback_update.py
testing/firefox-ui/tests/update/manifest.ini
testing/marionette/puppeteer/firefox/docs/api/software_update.rst
testing/marionette/puppeteer/firefox/docs/ui/update_wizard/dialog.rst
testing/marionette/puppeteer/firefox/firefox_puppeteer/api/software_update.py
testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/__init__.py
testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/dialog.py
testing/marionette/puppeteer/firefox/firefox_puppeteer/ui/update_wizard/wizard.py
testing/mozbase/mozproxy/mozproxy/backends/mitm.py
testing/mozbase/mozproxy/mozproxy/backends/mitmproxy-rel-bin-4.0.4-linux64.manifest
testing/mozbase/mozproxy/mozproxy/backends/mitmproxy-rel-bin-4.0.4-osx.manifest
testing/mozbase/mozproxy/mozproxy/backends/mitmproxy-rel-bin-4.0.4-win.manifest
testing/mozbase/mozproxy/mozproxy/backends/mitmproxy-rel-bin-linux64.manifest
testing/mozbase/mozproxy/mozproxy/backends/mitmproxy-rel-bin-osx.manifest
testing/mozbase/mozproxy/mozproxy/backends/mitmproxy-rel-bin-win.manifest
testing/mozbase/mozproxy/mozproxy/backends/mitmproxy_requirements.txt
testing/raptor/raptor/playback/alternate-server-replay-2.0.2.py
testing/raptor/raptor/playback/alternate-server-replay-4.0.4.py
testing/raptor/raptor/playback/scripts/catapult/LICENSE
testing/raptor/raptor/playback/scripts/catapult/deterministic.js
testing/raptor/raptor/playback/scripts/inject-deterministic.py
--- a/.eslintignore
+++ b/.eslintignore
@@ -290,27 +290,29 @@ testing/marionette/harness
 testing/mochitest/tests/Harness_sanity/**
 testing/mochitest/MochiKit/**
 testing/mochitest/tests/MochiKit-1.4.2/**
 testing/mochitest/tests/SimpleTest/**
 testing/modules/ajv-4.1.1.js
 testing/modules/sinon-7.2.7.js
 # octothorpe used for pref file comment causes parsing error
 testing/mozbase/mozprofile/tests/files/prefs_with_comments.js
+
+# Mozproxy third party
+testing/mozbase/mozproxy/mozproxy/backends/mitm/scripts/catapult/**
+
 testing/talos/talos/scripts/jszip.min.js
 testing/talos/talos/startup_test/sessionrestore/profile/sessionstore.js
 testing/talos/talos/startup_test/sessionrestore/profile-manywindows/sessionstore.js
 testing/talos/talos/tests/devtools/addon/content/pages/**
 testing/talos/talos/tests/dromaeo/**
 testing/talos/talos/tests/v8_7/**
 testing/talos/talos/tests/kraken/**
 # Runing Talos may extract data here, see bug 1435677.
 testing/talos/talos/tests/tp5n/**
-# Raptor third party
-testing/raptor/raptor/playback/scripts/catapult/**
 
 testing/web-platform/**
 testing/xpcshell/moz-http2/**
 testing/xpcshell/node-http2/**
 testing/xpcshell/dns-packet/**
 testing/xpcshell/node-ip/**
 
 
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -6,18 +6,18 @@
 
 #include "HTMLListAccessible.h"
 
 #include "DocAccessible.h"
 #include "nsAccUtils.h"
 #include "Role.h"
 #include "States.h"
 
-#include "nsBlockFrame.h"
-#include "nsBulletFrame.h"
+#include "nsContainerFrame.h"
+#include "nsLayoutUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -33,18 +33,18 @@ uint64_t HTMLListAccessible::NativeState
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLLIAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 HTMLLIAccessible::HTMLLIAccessible(nsIContent* aContent, DocAccessible* aDoc)
     : HyperTextAccessibleWrap(aContent, aDoc), mBullet(nullptr) {
   mType = eHTMLLiType;
 
-  nsBlockFrame* blockFrame = do_QueryFrame(GetFrame());
-  if (blockFrame && blockFrame->HasMarker()) {
+  nsIContent* marker = nsLayoutUtils::GetMarkerPseudo(aContent);
+  if (marker && marker->GetPrimaryFrame()) {
     mBullet = new HTMLListBulletAccessible(mContent, mDoc);
     Document()->BindToDocument(mBullet, nullptr);
     AppendChild(mBullet);
   }
 }
 
 void HTMLLIAccessible::Shutdown() {
   mBullet = nullptr;
@@ -114,47 +114,47 @@ HTMLListBulletAccessible::HTMLListBullet
   mGenericTypes |= eText;
   mStateFlags |= eSharedNode;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible: Accessible
 
 nsIFrame* HTMLListBulletAccessible::GetFrame() const {
-  nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
-  return blockFrame ? blockFrame->GetMarker() : nullptr;
+  nsIContent* marker = nsLayoutUtils::GetMarkerPseudo(mContent);
+  return marker ? marker->GetPrimaryFrame() : nullptr;
 }
 
 ENameValueFlag HTMLListBulletAccessible::Name(nsString& aName) const {
   aName.Truncate();
 
   // Native anonymous content, ARIA can't be used. Get list bullet text.
-  nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
-  if (blockFrame) {
-    blockFrame->GetSpokenMarkerText(aName);
+  if (nsContainerFrame* frame = do_QueryFrame(mContent->GetPrimaryFrame())) {
+    frame->GetSpokenMarkerText(aName);
   }
 
   return eNameOK;
 }
 
 role HTMLListBulletAccessible::NativeRole() const { return roles::STATICTEXT; }
 
 uint64_t HTMLListBulletAccessible::NativeState() const {
   return LeafAccessible::NativeState() | states::READONLY;
 }
 
 void HTMLListBulletAccessible::AppendTextTo(nsAString& aText,
                                             uint32_t aStartOffset,
                                             uint32_t aLength) {
   nsAutoString bulletText;
-  nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
-  if (blockFrame) blockFrame->GetSpokenMarkerText(bulletText);
-
+  Name(bulletText);
   aText.Append(Substring(bulletText, aStartOffset, aLength));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLListBulletAccessible: public
 
 bool HTMLListBulletAccessible::IsInside() const {
-  nsBlockFrame* blockFrame = do_QueryFrame(mContent->GetPrimaryFrame());
-  return blockFrame ? blockFrame->HasInsideMarker() : false;
+  if (nsIFrame* frame = mContent->GetPrimaryFrame()) {
+    return frame->StyleList()->mListStylePosition ==
+        NS_STYLE_LIST_STYLE_POSITION_INSIDE;
+  }
+  return false;
 }
--- a/browser/base/content/test/about/browser_aboutCertError.js
+++ b/browser/base/content/test/about/browser_aboutCertError.js
@@ -401,46 +401,81 @@ add_task(async function checkCautionClas
     });
 
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
 
 add_task(async function checkViewCertificate() {
   info("Loading a cert error and checking that the certificate can be shown.");
-  for (let useFrame of [false, true]) {
+  SpecialPowers.pushPrefEnv({
+    set: [["security.aboutcertificate.enabled", true]],
+  });
+  for (let useFrame of [true, false]) {
+    if (useFrame) {
+      // Bug #1573502
+      continue;
+    }
     let tab = await openErrorPage(UNKNOWN_ISSUER, useFrame);
     let browser = tab.linkedBrowser;
 
-    let dialogOpened = BrowserTestUtils.domWindowOpened();
-
+    let loaded = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
     await ContentTask.spawn(browser, { frame: useFrame }, async function({
       frame,
     }) {
       let doc = frame
         ? content.document.querySelector("iframe").contentDocument
         : content.document;
       let viewCertificate = doc.getElementById("viewCertificate");
       viewCertificate.click();
     });
+    await loaded;
 
-    let win = await dialogOpened;
-    await BrowserTestUtils.waitForEvent(win, "load");
-    is(
-      win.document.documentURI,
-      "chrome://pippki/content/certViewer.xul",
-      "Opened the cert viewer dialog"
+    let spec = gBrowser.selectedTab.linkedBrowser.documentURI.spec;
+    Assert.ok(
+      spec.startsWith("about:certificate"),
+      "about:certificate is the new opened tab"
     );
-    is(
-      win.document.getElementById("commonname").value,
-      "self-signed.example.com",
-      "Shows the correct certificate in the dialog"
+
+    await ContentTask.spawn(
+      gBrowser.selectedTab.linkedBrowser,
+      null,
+      async function() {
+        let doc = content.document;
+
+        let certificateSection = await ContentTaskUtils.waitForCondition(() => {
+          return doc.querySelector("certificate-section");
+        }, "Certificate section found");
+
+        let infoGroup = certificateSection.shadowRoot.querySelector(
+          "info-group"
+        );
+        Assert.ok(infoGroup, "infoGroup found");
+
+        let items = infoGroup.shadowRoot.querySelectorAll("info-item");
+        let commonnameID = items[items.length - 1].shadowRoot
+          .querySelector("label")
+          .getAttribute("data-l10n-id");
+        is(
+          commonnameID,
+          "certificate-viewer-common-name",
+          "The correct item was selected"
+        );
+
+        let commonnameValue = items[items.length - 1].shadowRoot.querySelector(
+          ".info"
+        ).textContent;
+        is(
+          commonnameValue,
+          "self-signed.example.com",
+          "Shows the correct certificate in the page"
+        );
+      }
     );
-    win.close();
-
+    BrowserTestUtils.removeTab(gBrowser.selectedTab); // closes about:certificate
     BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
 
 add_task(async function checkBadStsCertHeadline() {
   info(
     "Loading a bad sts cert error page and checking that the correct headline is shown"
   );
--- a/browser/base/content/test/performance/browser_startup_syncIPC.js
+++ b/browser/base/content/test/performance/browser_startup_syncIPC.js
@@ -103,16 +103,19 @@ const startupPhases = {
       name: "PAPZInputBridge::Msg_ProcessUnhandledEvent",
       condition: WIN,
       ignoreIfUnused: true, // Only on Win10 64
       maxCount: 1,
     },
     {
       name: "PGPU::Msg_GetDeviceStatus",
       condition: WIN && WEBRENDER, // bug 1553740 might want to drop the WEBRENDER clause here
+      // If Init() completes before we call EnsureGPUReady we won't send GetDeviceStatus
+      // so we can safely ignore if unused.
+      ignoreIfUnused: true,
       maxCount: 1,
     },
   ],
 
   // We are at this phase once we are ready to handle user events.
   // Any IO at this phase or before gets in the way of the user
   // interacting with the first browser window.
   "before handling user events": [
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -377,17 +377,20 @@ let LEGACY_ACTORS = {
         "webrtc:Deny",
         "webrtc:StopSharing",
       ],
     },
   },
 };
 
 (function earlyBlankFirstPaint() {
-  if (!Services.prefs.getBoolPref("browser.startup.blankWindow", false)) {
+  if (
+    AppConstants.platform == "macosx" ||
+    !Services.prefs.getBoolPref("browser.startup.blankWindow", false)
+  ) {
     return;
   }
 
   // Until bug 1450626 and bug 1488384 are fixed, skip the blank window when
   // using a non-default theme.
   if (
     Services.prefs.getCharPref(
       "extensions.activeThemeID",
--- a/browser/components/protections/content/protections.html
+++ b/browser/components/protections/content/protections.html
@@ -5,18 +5,17 @@
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8">
     <meta http-equiv="Content-Security-Policy" content="default-src chrome: blob:">
     <link rel="localization" href="browser/branding/brandings.ftl"/>
     <link rel="localization" href="branding/brand.ftl"/>
     <link rel="localization" href="browser/branding/sync-brand.ftl">
-    <!-- rename to browser/protections.ftl when exposing to l10n -->
-    <link rel="localization" href="preview/protections.ftl">
+    <link rel="localization" href="browser/protections.ftl">
     <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
     <link rel="stylesheet" href="chrome://browser/content/protections.css">
     <link rel="icon" href="chrome://browser/skin/controlcenter/dashboard.svg">
     <script type="module" src="chrome://browser/content/protections.js"></script>
     <script type="module" src="chrome://browser/content/lockwise-card.js"></script>
     <script type="module" src="chrome://browser/content/monitor-card.js"></script>
     <title data-l10n-id="protection-report-page-title"></title>
   </head>
rename from browser/components/protections/content/protections.ftl
rename to browser/locales/en-US/browser/protections.ftl
--- a/browser/components/protections/content/protections.ftl
+++ b/browser/locales/en-US/browser/protections.ftl
@@ -1,16 +1,12 @@
 # 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/.
 
-### This file is not in a locales directory to prevent it from
-### being translated as the feature is still in heavy development
-### and strings are likely to change often.
-
 # Variables:
 #   $count (Number) - Number of tracking events blocked.
 graph-week-summary =
   { $count ->
      [one] { -brand-short-name } blocked  { $count } tracker over the past week
     *[other] { -brand-short-name } blocked { $count } trackers over the past week
   }
 
@@ -39,63 +35,63 @@ etp-card-content = Trackers follow you a
 # This string is used to label the X axis of a graph. Other days of the week are generated via Intl.DateTimeFormat,
 # capitalization for this string should match the output for your locale.
 graph-today = Today
 
 # This string is used to describe the graph for screenreader users.
 graph-legend-description = A graph containing the total number of each type of tracker blocked this week.
 
 social-tab-title = Social Media Trackers
-social-tab-contant = Social media like, post, and comment buttons on other websites can track you — even if you don’t use them. Logging in to sites using your Facebook or Twitter account is another way they can track what you do on those sites. We remove these trackers so Facebook and Twitter see less of what you do online. <a data-l10n-name="learn-more-link">Learn more</a>
+social-tab-contant = Social networks place trackers on other websites to follow what you do, see, and watch online. This allows social media companies to learn more about you beyond what you share on your social media profiles. <a data-l10n-name="learn-more-link">Learn more</a>
 
 cookie-tab-title = Cross-Site Tracking Cookies
-cookie-tab-content = Cross-site tracking cookies follow you from site to site to collect data about your browsing habits. Advertisers and analytics companies gather this data to create a profile of your interests across many sites. Blocking them reduces the number of personalized ads that follow you around. <a data-l10n-name="learn-more-link">Learn more</a>
+cookie-tab-content = These cookies follow you from site to site to gather data about what you do online. They are set by third parties such as advertisers and analytics companies. Blocking cross-site tracking cookies reduces the number of ads that follow you around. <a data-l10n-name="learn-more-link">Learn more</a>
 
 tracker-tab-title = Tracking Content
-tracker-tab-content = Websites may load outside ads, videos, and other content that contain hidden trackers. Blocking tracking content can make websites load faster, but some buttons, forms, and login fields might not work. <a data-l10n-name="learn-more-link">Learn more</a>
+tracker-tab-content = Websites may load external ads, videos, and other content that contain tracking code. Blocking tracking content can help sites load faster, but some buttons, forms, and login fields might not work. <a data-l10n-name="learn-more-link">Learn more</a>
 
 fingerprinter-tab-title = Fingerprinters
-fingerprinter-tab-content = Fingerprinting is a form of online tracking that’s different from your real fingerprints. Companies use it to create a unique profile of you using data about your browser, device, and other settings. We block ad trackers from fingerprinting your device. <a data-l10n-name="learn-more-link">Learn more</a>
+fingerprinter-tab-content = Fingerprinters collect settings from your browser and computer to create a profile of you. Using this digital fingerprint, they can track you across different websites. <a data-l10n-name="learn-more-link">Learn more</a>
 
 cryptominer-tab-title = Cryptominers
-cryptominer-tab-content = Some websites host hidden malware that secretly uses your system’s computing power to mine cryptocurrency, or digital money. It drains your battery, slows down your computer, and increases your energy bill. We block known cryptominers from using your computing resources to make money. <a data-l10n-name="learn-more-link">Learn more</a>
+cryptominer-tab-content = Cryptominers use your system’s computing power to mine digital money. Cryptomining scripts drain your battery, slow down your computer, and can increase your energy bill. <a data-l10n-name="learn-more-link">Learn more</a>
 
 lockwise-title = Never forget a password again
 lockwise-title-logged-in = { -lockwise-brand-name }
 lockwise-header-content = { -lockwise-brand-name } securely stores your passwords in your browser.
 lockwise-header-content-logged-in = Securely store and sync your passwords to all your devices.
 open-about-logins-button = Open in { -brand-short-name }
 lockwise-no-logins-content = Get the <a data-l10n-name="lockwise-inline-link">{ -lockwise-brand-name }</a> app to take your passwords everywhere.
 
 # This string is displayed after a large numeral that indicates the total number
 # of email addresses being monitored. Don’t add $count to
 # your localization, because it would result in the number showing twice.
 lockwise-passwords-stored =
   { $count ->
-     [one] Password stored securely. <a data-l10n-name="lockwise-how-it-works">How it works</a>
-    *[other] Passwords stored securely. <a data-l10n-name="lockwise-how-it-works">How it works</a>
+     [one] Password stored securely <a data-l10n-name="lockwise-how-it-works">How it works</a>
+    *[other] Passwords stored securely <a data-l10n-name="lockwise-how-it-works">How it works</a>
   }
 
 turn-on-sync = Turn on { -sync-brand-short-name }…
   .title = Go to sync preferences
 
 # Variables:
 #   $count (Number) - Number of devices connected with sync.
 lockwise-sync-status =
   { $count ->
-     [one] Syncing to { $count } other device.
-    *[other] Syncing to { $count } other devices.
+     [one] Syncing to { $count } other device
+    *[other] Syncing to { $count } other devices
   }
 lockwise-sync-not-syncing = Not syncing to other devices.
 
 monitor-title = Look out for data breaches
 monitor-link = How it works
 monitor-header-content = Check { -monitor-brand-name } to see if you’ve been part of a data breach and get alerts about new breaches.
 monitor-header-content-logged-in = { -monitor-brand-name } warns you if your info has appeared in a known data breach
-monitor-sign-up = Sign up for Breach Alerts
+monitor-sign-up = Sign Up for Breach Alerts
 auto-scan = Automatically scanned today
 
 # This string is displayed after a large numeral that indicates the total number
 # of email addresses being monitored. Don’t add $count to
 # your localization, because it would result in the number showing twice.
 info-monitored-addresses =
   { $count ->
      [one] Email address being monitored.
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -3,17 +3,16 @@
 # 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/.
 
 # Note: This file should only contain locale entries. All
 # override and resource entries should go to browser/base/jar.mn to avoid
 # having to create the same entry for each locale.
 
 [localization] @AB_CD@.jar:
-  preview/protections.ftl                          (../components/protections/content/protections.ftl)
   preview/certviewer.ftl                           (../../toolkit/components/certviewer/content/certviewer.ftl)
   browser                                          (%browser/**/*.ftl)
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/browser/
 # bookmarks.html is produced by LOCALIZED_GENERATED_FILES.
     locale/browser/bookmarks.html                  (bookmarks.html)
 
--- a/build/moz.configure/bindgen.configure
+++ b/build/moz.configure/bindgen.configure
@@ -234,17 +234,17 @@ set_config('MOZ_LIBCLANG_PATH', bindgen_
 set_config('MOZ_CLANG_PATH', bindgen_config_paths.clang_path)
 
 
 @depends(target, target_is_unix, cxx_compiler, bindgen_cflags_android,
          bindgen_config_paths.clang_flags)
 def basic_bindgen_cflags(target, is_unix, compiler_info, android_cflags,
                          clang_flags):
     args = [
-        '-x', 'c++', '-fno-sized-deallocation',
+        '-x', 'c++', '-fno-sized-deallocation', '-fno-aligned-new',
         '-DTRACING=1', '-DIMPL_LIBXUL', '-DMOZILLA_INTERNAL_API',
         '-DRUST_BINDGEN'
     ]
 
     if is_unix:
         args += ['-DOS_POSIX=1']
 
     if target.os == 'Android':
--- a/build/moz.configure/flags.configure
+++ b/build/moz.configure/flags.configure
@@ -2,16 +2,19 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # We support C++14, but we don't want to enable the sized deallocation
 # facilities in C++14 yet.
 check_and_add_gcc_flag('-fno-sized-deallocation', compiler=cxx_compiler)
+# Likewise for C++17 and aligned allocation.  It's not immediately obvious
+# from the clang and GCC documentation, but they both support this.
+check_and_add_gcc_flag('-fno-aligned-new', compiler=cxx_compiler)
 
 # Please keep these last in this file.
 add_old_configure_assignment('_COMPILATION_CFLAGS', compilation_flags.cflags)
 add_old_configure_assignment(
     '_COMPILATION_CXXFLAGS', compilation_flags.cxxflags)
 add_old_configure_assignment(
     '_COMPILATION_HOST_CFLAGS', compilation_flags.host_cflags)
 add_old_configure_assignment(
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -403,18 +403,16 @@ everything::
 	$(MAKE) clean
 	$(MAKE) all
 
 # Dependencies which, if modified, should cause everything to rebuild
 GLOBAL_DEPS += Makefile $(addprefix $(DEPTH)/config/,$(INCLUDED_AUTOCONF_MK)) $(MOZILLA_DIR)/config/config.mk
 
 ##############################################
 ifdef COMPILE_ENVIRONMENT
-OBJ_TARGETS = $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS)
-
 compile:: host target
 
 host:: $(HOST_OBJS) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS) $(HOST_RUST_PROGRAMS) $(HOST_RUST_LIBRARY_FILE) $(HOST_SHARED_LIBRARY)
 
 target:: $(filter-out $(MOZBUILD_NON_DEFAULT_TARGETS),$(LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS) $(RUST_LIBRARY_FILE) $(RUST_PROGRAMS))
 target-shared:: $(filter-out $(MOZBUILD_NON_DEFAULT_TARGETS),$(SHARED_LIBRARY))
 
 ifndef LIBRARY
--- a/devtools/client/netmonitor/src/actions/web-sockets.js
+++ b/devtools/client/netmonitor/src/actions/web-sockets.js
@@ -13,21 +13,22 @@ const {
   WS_SET_REQUEST_FILTER_TEXT,
   WS_TOGGLE_COLUMN,
   WS_RESET_COLUMNS,
 } = require("../constants");
 
 /**
  * Add frame into state.
  */
-function addFrame(httpChannelId, data) {
+function addFrame(httpChannelId, data, batch) {
   return {
     type: WS_ADD_FRAME,
     httpChannelId,
     data,
+    meta: { batch },
   };
 }
 
 /**
  * Select frame.
  */
 function selectFrame(frame) {
   return {
--- a/devtools/client/netmonitor/src/assets/styles/websockets.css
+++ b/devtools/client/netmonitor/src/assets/styles/websockets.css
@@ -1,8 +1,22 @@
+/* Scroll to bottom */
+
+#messages-panel .uncontrolled {
+  flex-direction: column;
+}
+
+#messages-panel .ws-frames-list-scroll-anchor {
+  /* anchor nodes are required to have non-zero area */
+  min-height: 1px;
+  margin: 0;
+  padding: 0;
+  border: none;
+ }
+
 /* Empty notice */
 
 #messages-panel .ws-frame-list-empty-notice {
   width: 100%;
 }
 
 /* Frame type icon in the WebSockets Time column */
 
--- a/devtools/client/netmonitor/src/components/websockets/FrameListColumnData.js
+++ b/devtools/client/netmonitor/src/components/websockets/FrameListColumnData.js
@@ -2,59 +2,44 @@
  * 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";
 
 const { Component } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { getFramePayload } = require("../../utils/request-utils");
 const { L10N } = require("../../utils/l10n");
 
 /**
  * Renders the "Data" column of a WebSocket frame.
  */
 class FrameListColumnData extends Component {
   static get propTypes() {
     return {
       item: PropTypes.object.isRequired,
       connector: PropTypes.object.isRequired,
     };
   }
 
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      payload: "",
-    };
-  }
+  render() {
+    const { type, payload } = this.props.item;
+    const typeLabel = L10N.getStr(`netmonitor.ws.type.${type}`);
 
-  componentDidMount() {
-    const { item, connector } = this.props;
-    getFramePayload(item.payload, connector.getLongString).then(payload => {
-      this.setState({
-        payload,
-      });
-    });
-  }
-
-  render() {
-    const { type } = this.props.item;
-    const typeLabel = L10N.getStr(`netmonitor.ws.type.${type}`);
+    // If payload is a LongStringActor object, we show the first 1000 characters
+    const displayedPayload = payload.initial ? payload.initial : payload;
 
     return dom.td(
       {
         className: "ws-frames-list-column ws-frames-list-payload",
-        title: typeLabel + " " + this.state.payload,
+        title: typeLabel + " " + displayedPayload,
       },
       dom.img({
         alt: typeLabel,
         className: `ws-frames-list-type-icon ws-frames-list-type-icon-${type}`,
         src: `chrome://devtools/content/netmonitor/src/assets/icons/arrow-up.svg`,
       }),
-      " " + this.state.payload
+      " " + displayedPayload
     );
   }
 }
 
 module.exports = FrameListColumnData;
--- a/devtools/client/netmonitor/src/components/websockets/FrameListContent.js
+++ b/devtools/client/netmonitor/src/components/websockets/FrameListContent.js
@@ -11,26 +11,23 @@ const {
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const Services = require("Services");
 const {
   connect,
 } = require("devtools/client/shared/redux/visibility-handler-connect");
 const { PluralForm } = require("devtools/shared/plural-form");
 const { getDisplayedFrames } = require("../../selectors/index");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { table, tbody, tr, td, div, input, label } = dom;
+const { table, tbody, tr, td, div, input, label, hr } = dom;
 const { L10N } = require("../../utils/l10n");
 const FRAMES_EMPTY_TEXT = L10N.getStr("messagesEmptyText");
 const TOGGLE_MESSAGES_TRUNCATION = L10N.getStr("toggleMessagesTruncation");
 const TOGGLE_MESSAGES_TRUNCATION_TITLE = L10N.getStr(
   "toggleMessagesTruncation.title"
 );
-const TRUNCATED_MESSAGES_WARNING = L10N.getStr(
-  "netmonitor.ws.truncated-messages.warning"
-);
 const Actions = require("../../actions/index");
 
 const { getSelectedFrame } = require("../../selectors/index");
 
 loader.lazyGetter(this, "FrameListHeader", function() {
   return createFactory(require("./FrameListHeader"));
 });
 loader.lazyGetter(this, "FrameListItem", function() {
@@ -41,16 +38,17 @@ const LEFT_MOUSE_BUTTON = 0;
 
 /**
  * Renders the actual contents of the WebSocket frame list.
  */
 class FrameListContent extends Component {
   static get propTypes() {
     return {
       connector: PropTypes.object.isRequired,
+      startPanelContainer: PropTypes.object,
       frames: PropTypes.array,
       selectedFrame: PropTypes.object,
       selectFrame: PropTypes.func.isRequired,
       columns: PropTypes.object.isRequired,
     };
   }
 
   constructor(props) {
@@ -58,16 +56,52 @@ class FrameListContent extends Component
 
     this.framesLimit = Services.prefs.getIntPref(
       "devtools.netmonitor.ws.displayed-frames.limit"
     );
     this.currentTruncatedNum = 0;
     this.state = {
       checked: false,
     };
+    this.pinnedToBottom = false;
+    this.initIntersectionObserver = false;
+  }
+
+  componentDidUpdate() {
+    const { startPanelContainer } = this.props;
+    const scrollAnchor = this.refs.scrollAnchor;
+
+    // When frames are cleared, the previous scrollAnchor would be destroyed, so we need to reset this boolean.
+    if (!scrollAnchor) {
+      this.initIntersectionObserver = false;
+    }
+
+    if (startPanelContainer && scrollAnchor) {
+      // Initialize intersection observer.
+      if (!this.initIntersectionObserver) {
+        const observer = new IntersectionObserver(
+          () => {
+            // When scrollAnchor first comes into view, this.pinnedToBottom is set to true.
+            // When the anchor goes out of view, this callback function triggers again and toggles this.pinnedToBottom.
+            // Subsequent scroll into/out of view will toggle this.pinnedToBottom.
+            this.pinnedToBottom = !this.pinnedToBottom;
+          },
+          {
+            root: startPanelContainer,
+            threshold: 0.1,
+          }
+        );
+        observer.observe(scrollAnchor);
+        this.initIntersectionObserver = true;
+      }
+
+      if (this.pinnedToBottom) {
+        scrollAnchor.scrollIntoView();
+      }
+    }
   }
 
   onMouseDown(evt, item) {
     if (evt.button === LEFT_MOUSE_BUTTON) {
       this.props.selectFrame(item);
     }
   }
 
@@ -124,17 +158,16 @@ class FrameListContent extends Component
                   className: "truncated-messages-header",
                 },
                 div(
                   {
                     className: "truncated-messages-container",
                   },
                   div({
                     className: "truncated-messages-warning-icon",
-                    title: TRUNCATED_MESSAGES_WARNING,
                   }),
                   div(
                     {
                       className: "truncated-message",
                       title: MESSAGES_TRUNCATED,
                     },
                     MESSAGES_TRUNCATED
                   )
@@ -171,17 +204,21 @@ class FrameListContent extends Component
               index,
               isSelected: item === selectedFrame,
               onMouseDown: evt => this.onMouseDown(evt, item),
               connector,
               visibleColumns,
             })
           )
         )
-      )
+      ),
+      hr({
+        ref: "scrollAnchor",
+        className: "ws-frames-list-scroll-anchor",
+      })
     );
   }
 }
 
 module.exports = connect(
   state => ({
     selectedFrame: getSelectedFrame(state),
     frames: getDisplayedFrames(state),
--- a/devtools/client/netmonitor/src/components/websockets/WebSocketsPanel.js
+++ b/devtools/client/netmonitor/src/components/websockets/WebSocketsPanel.js
@@ -51,16 +51,20 @@ class WebSocketsPanel extends Component 
     };
   }
 
   constructor(props) {
     super(props);
 
     this.searchboxRef = createRef();
     this.clearFilterText = this.clearFilterText.bind(this);
+    this.handleContainerElement = this.handleContainerElement.bind(this);
+    this.state = {
+      startPanelContainer: null,
+    };
   }
 
   componentDidUpdate(prevProps) {
     const { selectedFrameVisible, openFrameDetailsTab, channelId } = this.props;
 
     // If a new WebSocket connection is selected, clear the filter text
     if (channelId !== prevProps.channelId) {
       this.clearFilterText();
@@ -80,42 +84,59 @@ class WebSocketsPanel extends Component 
     if (clientHeight) {
       Services.prefs.setIntPref(
         "devtools.netmonitor.ws.payload-preview-height",
         clientHeight
       );
     }
   }
 
+  /* Store the parent DOM element of the SplitBox startPanel's element.
+     We need this element for as an option for the IntersectionObserver */
+  handleContainerElement(element) {
+    if (!this.state.startPanelContainer) {
+      this.setState({
+        startPanelContainer: element,
+      });
+    }
+  }
+
   // Reset the filter text
   clearFilterText() {
     if (this.searchboxRef) {
       this.searchboxRef.current.onClearButtonClick();
     }
   }
 
   render() {
     const { frameDetailsOpen, connector, selectedFrame } = this.props;
 
+    const searchboxRef = this.searchboxRef;
+    const startPanelContainer = this.state.startPanelContainer;
+
     const initialHeight = Services.prefs.getIntPref(
       "devtools.netmonitor.ws.payload-preview-height"
     );
 
     return div(
       { className: "monitor-panel" },
       Toolbar({
-        searchboxRef: this.searchboxRef,
+        searchboxRef,
       }),
       SplitBox({
         className: "devtools-responsive-container",
         initialHeight: initialHeight,
         minSize: "50px",
         maxSize: "80%",
         splitterSize: frameDetailsOpen ? 1 : 0,
-        startPanel: FrameListContent({ connector }),
+        onSelectContainerElement: this.handleContainerElement,
+        startPanel: FrameListContent({
+          connector,
+          startPanelContainer,
+        }),
         endPanel:
           frameDetailsOpen &&
           FramePayload({
             ref: "endPanel",
             connector,
             selectedFrame,
           }),
         endPanelCollapsed: !frameDetailsOpen,
--- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js
+++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js
@@ -479,17 +479,17 @@ class FirefoxDataProvider {
   /**
    * Add a new WebSocket frame to application state.
    *
    * @param {number} httpChannelId the channel ID
    * @param {object} data websocket frame information
    */
   async addFrame(httpChannelId, data) {
     if (this.actionsEnabled && this.actions.addFrame) {
-      await this.actions.addFrame(httpChannelId, data);
+      await this.actions.addFrame(httpChannelId, data, true);
     }
     // TODO: Emit an event for test here
   }
 
   /**
    * Notify actions when messages from onNetworkEventUpdate are done, networkEventUpdate
    * messages contain initial network info for each updateType and then we can invoke
    * requestData to fetch its corresponded data lazily.
--- a/devtools/client/shared/components/splitter/SplitBox.js
+++ b/devtools/client/shared/components/splitter/SplitBox.js
@@ -43,16 +43,18 @@ class SplitBox extends Component {
       // Size of the splitter handle bar.
       splitterSize: PropTypes.number,
       // True if the splitter bar is vertical (default is vertical).
       vert: PropTypes.bool,
       // Style object.
       style: PropTypes.object,
       // Call when controlled panel was resized.
       onControlledPanelResized: PropTypes.func,
+      // Retrieve DOM reference to the start panel element
+      onSelectContainerElement: PropTypes.any,
     };
   }
 
   static get defaultProps() {
     return {
       splitterSize: 5,
       vert: true,
       endPanelControl: false,
@@ -193,17 +195,23 @@ class SplitBox extends Component {
     }
   }
 
   // Rendering
 
   /* eslint-disable complexity */
   render() {
     const { endPanelControl, splitterSize, vert } = this.state;
-    const { startPanel, endPanel, minSize, maxSize } = this.props;
+    const {
+      startPanel,
+      endPanel,
+      minSize,
+      maxSize,
+      onSelectContainerElement,
+    } = this.props;
 
     const style = Object.assign(
       {
         // Set the size of the controlled panel (height or width depending on the
         // current state). This can be used to help with styling of dependent
         // panels.
         "--split-box-controlled-panel-size": `${
           vert ? this.state.width : this.state.height
@@ -263,16 +271,19 @@ class SplitBox extends Component {
       startPanel
         ? dom.div(
             {
               className: endPanelControl ? "uncontrolled" : "controlled",
               style: leftPanelStyle,
               role: "presentation",
               ref: div => {
                 this.startPanelContainer = div;
+                if (onSelectContainerElement) {
+                  onSelectContainerElement(div);
+                }
               },
             },
             startPanel
           )
         : null,
       splitterSize > 0
         ? Draggable({
             className: "splitter",
--- a/devtools/client/themes/boxmodel.css
+++ b/devtools/client/themes/boxmodel.css
@@ -124,16 +124,17 @@
 /* Editable region sizes are contained in absolutely positioned <p> */
 
 .boxmodel-main > p,
 .boxmodel-size {
   position: absolute;
   pointer-events: none;
   margin: 0;
   text-align: center;
+  direction: ltr;
 }
 
 .boxmodel-main > p > span,
 .boxmodel-main > p > input,
 .boxmodel-content,
 .boxmodel-size > span {
   vertical-align: middle;
   pointer-events: auto;
@@ -383,16 +384,18 @@
 .boxmodel-size > span {
   cursor: default;
 }
 
 /* Box Model Info: contains the position and size of the element */
 
 .boxmodel-element-size {
   flex: 1;
+  direction: ltr;
+  text-align: match-parent;
 }
 
 .boxmodel-position-group {
   display: flex;
   align-items: center;
 }
 
 /* Tag displayed next to DOM Node previews (used to display reference elements) */
--- a/devtools/client/themes/breadcrumbs.css
+++ b/devtools/client/themes/breadcrumbs.css
@@ -109,32 +109,16 @@
   border-width: 0;
 }
 
 .breadcrumbs-widget-item:first-child::before {
   /* The first crumb does not need any separator before itself */
   content: unset;
 }
 
-.breadcrumbs-widget-item:dir(rtl)::before {
-  transform: scaleX(-1);
-}
-
-/* RTL support: move the images that were on the left to the right,
- * and move images that were on the right to the left.
- */
-.breadcrumbs-widget-item:dir(rtl) {
-  padding: 0 20px 0 8px;
-}
-
-.breadcrumbs-widget-item:dir(rtl),
-.breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item:dir(rtl) {
-  background-position: center right;
-}
-
 .breadcrumbs-widget-item:not(:first-child)::before {
   content: url(chrome://devtools/skin/images/breadcrumbs-divider.svg);
   background: none;
   position: relative;
   left: -3px;
   margin: 0 4px 0 -1px;
   top: -1px;
 }
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -142,16 +142,17 @@ window {
   display: flex;
   align-items: center;
 }
 
 #inspector-breadcrumbs .html-arrowscrollbox-inner {
   flex: 1;
   display: flex;
   overflow: hidden;
+  direction: ltr;
 }
 
 #inspector-breadcrumbs .breadcrumbs-widget-item {
   white-space: nowrap;
   flex-shrink: 0;
 }
 
 #inspector-rules-container,
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/manifest.js
@@ -0,0 +1,38 @@
+/* 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";
+
+const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
+const { manifestSpec } = require("devtools/shared/specs/manifest");
+
+loader.lazyImporter(
+  this,
+  "ManifestObtainer",
+  "resource://gre/modules/ManifestObtainer.jsm"
+);
+
+/**
+ * An actor for a Web Manifest
+ */
+const ManifestActor = ActorClassWithSpec(manifestSpec, {
+  initialize: function(conn, targetActor) {
+    Actor.prototype.initialize.call(this, conn);
+    this.targetActor = targetActor;
+  },
+
+  fetchCanonicalManifest: async function() {
+    try {
+      const manifest = await ManifestObtainer.contentObtainManifest(
+        this.targetActor.window,
+        { checkConformance: true }
+      );
+      return { manifest };
+    } catch (error) {
+      return { manifest: null, errorMessage: error.message };
+    }
+  },
+});
+
+exports.ManifestActor = ManifestActor;
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -34,16 +34,17 @@ DevToolsModules(
     'environment.js',
     'errordocs.js',
     'frame.js',
     'framerate.js',
     'heap-snapshot-file.js',
     'highlighters.css',
     'highlighters.js',
     'layout.js',
+    'manifest.js',
     'memory.js',
     'object.js',
     'pause-scoped.js',
     'perf.js',
     'performance-recording.js',
     'performance.js',
     'preference.js',
     'process.js',
--- a/devtools/server/actors/utils/actor-registry.js
+++ b/devtools/server/actors/utils/actor-registry.js
@@ -250,16 +250,21 @@ const ActorRegistry = {
     this.registerModule(
       "devtools/server/actors/network-monitor/websocket-actor",
       {
         prefix: "webSocket",
         constructor: "WebSocketActor",
         type: { target: true },
       }
     );
+    this.registerModule("devtools/server/actors/manifest", {
+      prefix: "manifest",
+      constructor: "ManifestActor",
+      type: { target: true },
+    });
   },
 
   /**
    * Registers handlers for new target-scoped request types defined dynamically.
    *
    * Note that the name or actorPrefix of the request type is not allowed to clash with
    * existing protocol packet properties, like 'title', 'url' or 'actor', since that would
    * break the protocol.
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/browser/application-manifest-404-manifest.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Simple manifest</title>
+  <link rel="manifest" href="non-existing-manifest.json">
+</head>
+<body>
+  <p>This page links to a manifest URL that is a 404.</p>
+</body>
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/browser/application-manifest-basic.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Simple manifest</title>
+  <link rel="manifest" href='data:application/manifest+json,{"name": "FooApp"}'>
+</head>
+<body>
+  <pre><code>{ "name": "Foo App" }</code></pre>
+</body>
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/browser/application-manifest-no-manifest.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>No manifest</title>
+</head>
+<body>
+  <p>This page does not link to a manifest</p>
+</body>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/browser/application-manifest-warnings.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Empty manifest</title>
+  <link rel="manifest" href='data:application/manifest+json,{"name": 0}'>
+</head>
+<body>
+  <pre><code>{ }</code></pre>
+</body>
--- a/devtools/server/tests/browser/browser.ini
+++ b/devtools/server/tests/browser/browser.ini
@@ -1,15 +1,19 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   animation.html
   animation-data.html
+  application-manifest-404-manifest.html
+  application-manifest-basic.html
+  application-manifest-no-manifest.html
+  application-manifest-warnings.html
   doc_accessibility_audit.html
   doc_accessibility_infobar.html
   doc_accessibility_text_label_audit_frame.html
   doc_accessibility_text_label_audit.html
   doc_accessibility.html
   doc_allocations.html
   doc_force_cc.html
   doc_force_gc.html
@@ -79,16 +83,17 @@ fail-if = fission
 fail-if = fission
 [browser_animation_playPauseSeveral.js]
 [browser_animation_reconstructState.js]
 [browser_animation_refreshTransitions.js]
 [browser_animation_setCurrentTime.js]
 [browser_animation_setPlaybackRate.js]
 [browser_animation_simple.js]
 [browser_animation_updatedState.js]
+[browser_application_manifest.js]
 [browser_canvasframe_helper_01.js]
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_canvasframe_helper_02.js]
 [browser_canvasframe_helper_03.js]
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_canvasframe_helper_04.js]
 skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
 [browser_canvasframe_helper_05.js]
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/browser/browser_application_manifest.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function() {
+  info("Testing fetching a valid manifest");
+  const response = await fetchManifest("application-manifest-basic.html");
+
+  ok(
+    response.manifest && response.manifest.name == "FooApp",
+    "Returns an object populated with the manifest data"
+  );
+});
+
+add_task(async function() {
+  info("Testing fetching an existing manifest with invalid values");
+  const response = await fetchManifest("application-manifest-warnings.html");
+
+  ok(
+    response.manifest && response.manifest.moz_validation,
+    "Returns an object populated with the manifest data"
+  );
+
+  const warnings = response.manifest.moz_validation;
+  ok(
+    warnings.length === 1 &&
+      warnings[0].warn &&
+      warnings[0].warn.includes("name member to be a string"),
+    "The returned object contains the expected warning info"
+  );
+});
+
+add_task(async function() {
+  info("Testing fetching a manifest in a page that does not have one");
+  const response = await fetchManifest("application-manifest-no-manifest.html");
+
+  is(response.manifest, null, "Returns an object with a `null` manifest");
+  ok(!response.errorMessage, "Does not return an error message");
+});
+
+add_task(async function() {
+  info("Testing an error happening fetching a manifest");
+  // the page that we are testing contains an invalid URL for the manifest
+  const response = await fetchManifest(
+    "application-manifest-404-manifest.html"
+  );
+
+  is(response.manifest, null, "Returns an object with a `null` manifest");
+  ok(
+    response.errorMessage &&
+      response.errorMessage.toLowerCase().includes("404 - not found"),
+    "Returns the expected error message"
+  );
+});
+
+async function fetchManifest(filename) {
+  const url = MAIN_DOMAIN + filename;
+  const target = await addTabTarget(url);
+
+  info("Initializing manifest front for tab");
+  const manifestFront = await target.getFront("manifest");
+
+  info("Fetching manifest");
+  const response = await manifestFront.fetchCanonicalManifest();
+
+  return response;
+}
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -5880,39 +5880,29 @@ exports.CSS_PROPERTIES = {
   },
   "display": {
     "isInherited": false,
     "subproperties": [
       "display"
     ],
     "supports": [],
     "values": [
-      "-moz-box",
-      "-moz-deck",
-      "-moz-grid",
-      "-moz-grid-group",
-      "-moz-grid-line",
-      "-moz-groupbox",
-      "-moz-inline-box",
-      "-moz-inline-grid",
-      "-moz-inline-stack",
-      "-moz-popup",
-      "-moz-stack",
       "-webkit-box",
-      "-webkit-flex",
       "-webkit-inline-box",
-      "-webkit-inline-flex",
       "block",
+      "block ruby",
       "contents",
       "flex",
       "flow-root",
       "grid",
       "inherit",
       "initial",
       "inline",
+      "inline flow-root list-item",
+      "inline list-item",
       "inline-block",
       "inline-flex",
       "inline-grid",
       "inline-table",
       "list-item",
       "none",
       "revert",
       "ruby",
new file mode 100644
--- /dev/null
+++ b/devtools/shared/fronts/manifest.js
@@ -0,0 +1,23 @@
+/* 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";
+
+const {
+  FrontClassWithSpec,
+  registerFront,
+} = require("devtools/shared/protocol");
+const { manifestSpec } = require("devtools/shared/specs/manifest");
+
+class ManifestFront extends FrontClassWithSpec(manifestSpec) {
+  constructor(client) {
+    super(client);
+
+    // attribute name from which to retrieve the actorID out of the target actor's form
+    this.formAttributeName = "manifestActor";
+  }
+}
+
+exports.ManifestFront = ManifestFront;
+registerFront(ManifestFront);
--- a/devtools/shared/fronts/moz.build
+++ b/devtools/shared/fronts/moz.build
@@ -20,16 +20,17 @@ DevToolsModules(
     'changes.js',
     'css-properties.js',
     'device.js',
     'emulation.js',
     'framerate.js',
     'highlighters.js',
     'inspector.js',
     'layout.js',
+    'manifest.js',
     'memory.js',
     'node.js',
     'perf.js',
     'performance-recording.js',
     'performance.js',
     'preference.js',
     'promises.js',
     'reflow.js',
--- a/devtools/shared/specs/index.js
+++ b/devtools/shared/specs/index.js
@@ -106,16 +106,21 @@ const Types = (exports.__TypesForTests =
     front: "devtools/shared/fronts/inspector",
   },
   {
     types: ["flexbox", "grid", "layout"],
     spec: "devtools/shared/specs/layout",
     front: "devtools/shared/fronts/layout",
   },
   {
+    types: ["manifest"],
+    spec: "devtools/shared/specs/manifest",
+    front: "devtools/shared/fronts/manifest",
+  },
+  {
     types: ["memory"],
     spec: "devtools/shared/specs/memory",
     front: "devtools/shared/fronts/memory",
   },
   {
     types: ["netEvent"],
     spec: "devtools/shared/specs/network-event",
     front: null,
new file mode 100644
--- /dev/null
+++ b/devtools/shared/specs/manifest.js
@@ -0,0 +1,18 @@
+/* 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";
+
+const { generateActorSpec, RetVal } = require("devtools/shared/protocol");
+
+const manifestSpec = generateActorSpec({
+  typeName: "manifest",
+  methods: {
+    fetchCanonicalManifest: {
+      request: {},
+      response: RetVal("json"),
+    },
+  },
+});
+
+exports.manifestSpec = manifestSpec;
--- a/devtools/shared/specs/moz.build
+++ b/devtools/shared/specs/moz.build
@@ -23,16 +23,17 @@ DevToolsModules(
     'environment.js',
     'frame.js',
     'framerate.js',
     'heap-snapshot-file.js',
     'highlighters.js',
     'index.js',
     'inspector.js',
     'layout.js',
+    'manifest.js',
     'memory.js',
     'network-event.js',
     'network-monitor.js',
     'node.js',
     'object.js',
     'perf.js',
     'performance-recording.js',
     'performance.js',
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -132,16 +132,17 @@
 
 #include "nsIServiceManager.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "imgLoader.h"
 
 #include "nsAboutProtocolUtils.h"
 #include "nsCanvasFrame.h"
 #include "nsContentCID.h"
+#include "nsContentSecurityUtils.h"
 #include "nsError.h"
 #include "nsPresContext.h"
 #include "nsThreadUtils.h"
 #include "nsNodeInfoManager.h"
 #include "nsIEditingSession.h"
 #include "nsIFileChannel.h"
 #include "nsIMultiPartChannel.h"
 #include "nsIRefreshURI.h"
@@ -7142,98 +7143,24 @@ void Document::DispatchContentLoadedEven
         swm->MaybeCheckNavigationUpdate(clientInfo.ref());
       }
     }
   }
 
   UnblockOnload(true);
 }
 
-#if defined(DEBUG) && !defined(ANDROID)
-// We want to get to a point where all about: pages ship with a CSP. This
-// assertion ensures that we can not deploy new about: pages without a CSP.
-// Initially we will whitelist legacy about: pages which not yet have a CSP
-// attached, but ultimately that whitelist should disappear.
-// Please note that any about: page should not use inline JS or inline CSS,
-// and instead should load JS and CSS from an external file (*.js, *.css)
-// which allows us to apply a strong CSP omitting 'unsafe-inline'. Ideally,
-// the CSP allows precisely the resources that need to be loaded; but it
-// should at least be as strong as:
-// <meta http-equiv="Content-Security-Policy" content="default-src chrome:"/>
-static void AssertAboutPageHasCSP(Document* aDocument) {
-  // Check if we are loading an about: URI at all
-  nsCOMPtr<nsIURI> documentURI = aDocument->GetDocumentURI();
-  if (!documentURI->SchemeIs("about") ||
-      Preferences::GetBool("csp.skip_about_page_has_csp_assert")) {
-    return;
-  }
-
-  // Potentially init the legacy whitelist of about URIs without a CSP.
-  static StaticAutoPtr<nsTArray<nsCString>> sLegacyAboutPagesWithNoCSP;
-  if (!sLegacyAboutPagesWithNoCSP ||
-      Preferences::GetBool("csp.overrule_about_uris_without_csp_whitelist")) {
-    sLegacyAboutPagesWithNoCSP = new nsTArray<nsCString>();
-    nsAutoCString legacyAboutPages;
-    Preferences::GetCString("csp.about_uris_without_csp", legacyAboutPages);
-    for (const nsACString& hostString : legacyAboutPages.Split(',')) {
-      // please note that for the actual whitelist we only store the path of
-      // about: URI. Let's reassemble the full about URI here so we don't
-      // have to remove query arguments later.
-      nsCString aboutURI;
-      aboutURI.AppendLiteral("about:");
-      aboutURI.Append(hostString);
-      sLegacyAboutPagesWithNoCSP->AppendElement(aboutURI);
-    }
-    ClearOnShutdown(&sLegacyAboutPagesWithNoCSP);
-  }
-
-  // Check if the about URI is whitelisted
-  nsAutoCString aboutSpec;
-  documentURI->GetSpec(aboutSpec);
-  ToLowerCase(aboutSpec);
-  for (auto& legacyPageEntry : *sLegacyAboutPagesWithNoCSP) {
-    // please note that we perform a substring match here on purpose,
-    // so we don't have to deal and parse out all the query arguments
-    // the various about pages rely on.
-    if (aboutSpec.Find(legacyPageEntry) == 0) {
-      return;
-    }
-  }
-
-  nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
-  bool foundDefaultSrc = false;
-  if (csp) {
-    uint32_t policyCount = 0;
-    csp->GetPolicyCount(&policyCount);
-    nsAutoString parsedPolicyStr;
-    for (uint32_t i = 0; i < policyCount; ++i) {
-      csp->GetPolicyString(i, parsedPolicyStr);
-      if (parsedPolicyStr.Find("default-src") >= 0) {
-        foundDefaultSrc = true;
-        break;
-      }
-    }
-  }
-  if (Preferences::GetBool("csp.overrule_about_uris_without_csp_whitelist")) {
-    NS_ASSERTION(foundDefaultSrc, "about: page must have a CSP");
-    return;
-  }
-  MOZ_ASSERT(foundDefaultSrc,
-             "about: page must contain a CSP including default-src");
-}
-#endif
-
 void Document::EndLoad() {
   bool turnOnEditing =
       mParser && (HasFlag(NODE_IS_EDITABLE) || mContentEditableCount > 0);
 
 #if defined(DEBUG) && !defined(ANDROID)
   // only assert if nothing stopped the load on purpose
   if (!mParserAborted) {
-    AssertAboutPageHasCSP(this);
+    nsContentSecurityUtils::AssertAboutPageHasCSP(this);
   }
 #endif
 
   // EndLoad may have been called without a matching call to BeginLoad, in the
   // case of a failed parse (for example, due to timeout). In such a case, we
   // still want to execute part of this code to do appropriate cleanup, but we
   // gate part of it because it is intended to match 1-for-1 with calls to
   // BeginLoad. We have an explicit flag bit for this purpose, since it's
--- a/dom/base/nsMappedAttributes.cpp
+++ b/dom/base/nsMappedAttributes.cpp
@@ -74,17 +74,17 @@ nsMappedAttributes::~nsMappedAttributes(
 nsMappedAttributes* nsMappedAttributes::Clone(bool aWillAddAttr) {
   uint32_t extra = aWillAddAttr ? 1 : 0;
 
   // This will call the overridden operator new
   return new (mAttrCount + extra) nsMappedAttributes(*this);
 }
 
 void* nsMappedAttributes::operator new(size_t aSize,
-                                       uint32_t aAttrCount) CPP_THROW_NEW {
+                                       uint32_t aAttrCount) noexcept(true) {
   size_t size = aSize + aAttrCount * sizeof(InternalAttr);
 
   // aSize will include the mAttrs buffer so subtract that.
   // We don't want to under-allocate, however, so do not subtract
   // if we have zero attributes. The zero attribute case only happens
   // for <body>'s mapped attributes
   if (aAttrCount != 0) {
     size -= sizeof(void * [1]);
--- a/dom/base/nsMappedAttributes.h
+++ b/dom/base/nsMappedAttributes.h
@@ -22,17 +22,17 @@ class nsAtom;
 class nsHTMLStyleSheet;
 
 class nsMappedAttributes final {
  public:
   nsMappedAttributes(nsHTMLStyleSheet* aSheet,
                      nsMapRuleToAttributesFunc aMapRuleFunc);
 
   // Do not return null.
-  void* operator new(size_t size, uint32_t aAttrCount = 1) CPP_THROW_NEW;
+  void* operator new(size_t size, uint32_t aAttrCount = 1) noexcept(true);
   nsMappedAttributes* Clone(bool aWillAddAttr);
 
   NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(nsMappedAttributes, LastRelease())
 
   void SetAndSwapAttr(nsAtom* aAttrName, nsAttrValue& aValue,
                       bool* aValueWasSet);
   const nsAttrValue* GetAttr(const nsAtom* aAttrName) const;
   const nsAttrValue* GetAttr(const nsAString& aAttrName) const;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2041,17 +2041,17 @@ DEFINE_XRAY_EXPANDO_CLASS(, DefaultXrayE
 NativePropertyHooks sEmptyNativePropertyHooks = {nullptr,
                                                  nullptr,
                                                  nullptr,
                                                  {nullptr, nullptr},
                                                  prototypes::id::_ID_Count,
                                                  constructors::id::_ID_Count,
                                                  nullptr};
 
-const js::ClassOps sBoringInterfaceObjectClassClassOps = {
+const JSClassOps sBoringInterfaceObjectClassClassOps = {
     nullptr,             /* addProperty */
     nullptr,             /* delProperty */
     nullptr,             /* enumerate */
     nullptr,             /* newEnumerate */
     nullptr,             /* resolve */
     nullptr,             /* mayResolve */
     nullptr,             /* finalize */
     ThrowingConstructor, /* call */
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2338,17 +2338,17 @@ inline JSObject* GetCachedSlotStorageObj
     return obj;
   }
 
   return GetCachedSlotStorageObjectSlow(cx, obj, isXray);
 }
 
 extern NativePropertyHooks sEmptyNativePropertyHooks;
 
-extern const js::ClassOps sBoringInterfaceObjectClassClassOps;
+extern const JSClassOps sBoringInterfaceObjectClassClassOps;
 
 extern const js::ObjectOps sInterfaceObjectClassObjectOps;
 
 inline bool UseDOMXray(JSObject* obj) {
   const js::Class* clasp = js::GetObjectClass(obj);
   return IsDOMClass(clasp) || JS_IsNativeFunction(obj, Constructor) ||
          IsDOMIfaceAndProtoClass(clasp);
 }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -504,17 +504,17 @@ class CGDOMJSClass(CGThing):
             newEnumerateHook = "mozilla::dom::EnumerateGlobal"
         else:
             resolveHook = "nullptr"
             mayResolveHook = "nullptr"
             newEnumerateHook = "nullptr"
 
         return fill(
             """
-            static const js::ClassOps sClassOps = {
+            static const JSClassOps sClassOps = {
               ${addProperty}, /* addProperty */
               nullptr,               /* delProperty */
               nullptr,               /* enumerate */
               ${newEnumerate}, /* newEnumerate */
               ${resolve}, /* resolve */
               ${mayResolve}, /* mayResolve */
               ${finalize}, /* finalize */
               ${call}, /* call */
@@ -776,17 +776,17 @@ class CGInterfaceObjectJSClass(CGThing):
             ret = ""
             classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
         elif ctorname == "nullptr":
             ret = ""
             classOpsPtr = "JS_NULL_CLASS_OPS"
         else:
             ret = fill(
                 """
-                static const js::ClassOps sInterfaceObjectClassOps = {
+                static const JSClassOps sInterfaceObjectClassOps = {
                     nullptr,               /* addProperty */
                     nullptr,               /* delProperty */
                     nullptr,               /* enumerate */
                     nullptr,               /* newEnumerate */
                     nullptr,               /* resolve */
                     nullptr,               /* mayResolve */
                     nullptr,               /* finalize */
                     ${ctorname}, /* call */
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -54,17 +54,17 @@ static size_t SimpleGlobal_moved(JSObjec
   SimpleGlobalObject* globalObject =
       static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
   if (globalObject) {
     globalObject->UpdateWrapper(obj, old);
   }
   return 0;
 }
 
-static const js::ClassOps SimpleGlobalClassOps = {
+static const JSClassOps SimpleGlobalClassOps = {
     nullptr,
     nullptr,
     nullptr,
     JS_NewEnumerateStandardClasses,
     JS_ResolveStandardClass,
     JS_MayResolveStandardClass,
     SimpleGlobal_finalize,
     nullptr,
--- a/dom/html/HTMLSelectElement.h
+++ b/dom/html/HTMLSelectElement.h
@@ -48,17 +48,17 @@ class MOZ_STACK_CLASS SafeOptionListMuta
    * @param aIndex  The index of the content object in the parent.
    */
   SafeOptionListMutation(nsIContent* aSelect, nsIContent* aParent,
                          nsIContent* aKid, uint32_t aIndex, bool aNotify);
   ~SafeOptionListMutation();
   void MutationFailed() { mNeedsRebuild = true; }
 
  private:
-  static void* operator new(size_t) CPP_THROW_NEW { return 0; }
+  static void* operator new(size_t) noexcept(true) { return 0; }
   static void operator delete(void*, size_t) {}
   /** The select element which option list is being mutated. */
   RefPtr<HTMLSelectElement> mSelect;
   /** true if the current mutation is the first one in the stack. */
   bool mTopLevelMutation;
   /** true if it is known that the option list must be recreated. */
   bool mNeedsRebuild;
   /** Whether we should be notifying when we make various method calls on
--- a/dom/ipc/PWindowGlobal.ipdl
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -38,17 +38,17 @@ async refcounted protocol PWindowGlobal
 child:
   async __delete__();
 
   async ChangeFrameRemoteness(BrowsingContext aFrameContext,
                               nsString aRemoteType,
                               uint64_t aSwitchId)
       returns (nsresult rv, nullable PBrowserBridge bridge);
 
-  async DrawSnapshot(IntRect? aRect, float aScale, nscolor aBackgroundColor) returns (PaintFragment retval);
+  async DrawSnapshot(IntRect? aRect, float aScale, nscolor aBackgroundColor, uint32_t aFlags) returns (PaintFragment retval);
 
   /**
    * Returns the serialized security info associated with this window.
    */
   async GetSecurityInfo() returns(nsCString? serializedSecInfo);
 
 both:
   async RawMessage(JSWindowActorMessageMeta aMetadata, ClonedMessageData aData);
--- a/dom/ipc/WindowGlobalChild.cpp
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -305,25 +305,26 @@ IPCResult WindowGlobalChild::RecvChangeF
 
   // To make the type system happy, we've gotta do some gymnastics.
   aResolver(Tuple<const nsresult&, PBrowserBridgeChild*>(rv, bbc));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WindowGlobalChild::RecvDrawSnapshot(
     const Maybe<IntRect>& aRect, const float& aScale,
-    const nscolor& aBackgroundColor, DrawSnapshotResolver&& aResolve) {
+    const nscolor& aBackgroundColor, const uint32_t& aFlags,
+    DrawSnapshotResolver&& aResolve) {
   nsCOMPtr<nsIDocShell> docShell = BrowsingContext()->GetDocShell();
   if (!docShell) {
     aResolve(gfx::PaintFragment{});
     return IPC_OK();
   }
 
-  aResolve(
-      gfx::PaintFragment::Record(docShell, aRect, aScale, aBackgroundColor));
+  aResolve(gfx::PaintFragment::Record(docShell, aRect, aScale, aBackgroundColor,
+                                      (gfx::CrossProcessPaintFlags)aFlags));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WindowGlobalChild::RecvGetSecurityInfo(
     GetSecurityInfoResolver&& aResolve) {
   Maybe<nsCString> result;
 
   if (nsCOMPtr<Document> doc = mWindowGlobal->GetDoc()) {
--- a/dom/ipc/WindowGlobalChild.h
+++ b/dom/ipc/WindowGlobalChild.h
@@ -116,16 +116,17 @@ class WindowGlobalChild final : public W
 
   mozilla::ipc::IPCResult RecvChangeFrameRemoteness(
       dom::BrowsingContext* aBc, const nsString& aRemoteType,
       uint64_t aPendingSwitchId, ChangeFrameRemotenessResolver&& aResolver);
 
   mozilla::ipc::IPCResult RecvDrawSnapshot(const Maybe<IntRect>& aRect,
                                            const float& aScale,
                                            const nscolor& aBackgroundColor,
+                                           const uint32_t& aFlags,
                                            DrawSnapshotResolver&& aResolve);
 
   mozilla::ipc::IPCResult RecvGetSecurityInfo(
       GetSecurityInfoResolver&& aResolve);
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
  private:
--- a/dom/ipc/WindowGlobalParent.cpp
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -358,28 +358,30 @@ already_AddRefed<mozilla::dom::Promise> 
   if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0),
                                                aBackgroundColor, &color,
                                                nullptr, nullptr))) {
     aRv = NS_ERROR_FAILURE;
     return nullptr;
   }
 
   if (!gfx::CrossProcessPaint::Start(this, aRect, (float)aScale, color,
+                                     gfx::CrossProcessPaintFlags::None,
                                      promise)) {
     aRv = NS_ERROR_FAILURE;
     return nullptr;
   }
   return promise.forget();
 }
 
 void WindowGlobalParent::DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
                                               const Maybe<IntRect>& aRect,
                                               float aScale,
-                                              nscolor aBackgroundColor) {
-  auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor);
+                                              nscolor aBackgroundColor,
+                                              uint32_t aFlags) {
+  auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor, aFlags);
 
   RefPtr<gfx::CrossProcessPaint> paint(aPaint);
   RefPtr<WindowGlobalParent> wgp(this);
   promise->Then(
       GetMainThreadSerialEventTarget(), __func__,
       [paint, wgp](PaintFragment&& aFragment) {
         paint->ReceiveFragment(wgp, std::move(aFragment));
       },
--- a/dom/ipc/WindowGlobalParent.h
+++ b/dom/ipc/WindowGlobalParent.h
@@ -151,17 +151,17 @@ class WindowGlobalParent final : public 
                                          const ClonedMessageData& aData);
   mozilla::ipc::IPCResult RecvDidEmbedBrowsingContext(
       dom::BrowsingContext* aContext);
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   void DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
                             const Maybe<IntRect>& aRect, float aScale,
-                            nscolor aBackgroundColor);
+                            nscolor aBackgroundColor, uint32_t aFlags);
 
  private:
   ~WindowGlobalParent();
 
   // NOTE: This document principal doesn't reflect possible |document.domain|
   // mutations which may have been made in the actual document.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIURI> mDocumentURI;
--- a/dom/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -80,16 +80,18 @@ WeakCipherSuiteWarning=This site uses th
 
 DeprecatedTLSVersion=This site uses a deprecated version of TLS that will be disabled in March 2020. Please upgrade to TLS 1.2 or 1.3.
 
 #XCTO: nosniff
 # LOCALIZATION NOTE: Do not translate "X-Content-Type-Options: nosniff".
 MimeTypeMismatch2=The resource from “%1$S” was blocked due to MIME type (“%2$S”) mismatch (X-Content-Type-Options: nosniff).
 # LOCALIZATION NOTE: Do not translate "X-Content-Type-Options" and also do not translate "nosniff".
 XCTOHeaderValueMissing=X-Content-Type-Options header warning: value was “%1$S”; did you mean to send “nosniff”?
+# LOCALIZATION NOTE: Do not translate "X-Content-Type-Options" and also do not translate "nosniff".
+XTCOWithMIMEValueMissing=The resource from “%1$S” was not rendered due to an unknown, incorrect or missing MIME type (X-Content-Type-Options: nosniff).
 
 BlockScriptWithWrongMimeType2=Script from “%1$S” was blocked because of a disallowed MIME type (“%2$S”).
 WarnScriptWithWrongMimeType=The script from “%1$S” was loaded even though its MIME type (“%2$S”) is not a valid JavaScript MIME type.
 # LOCALIZATION NOTE: Do not translate "importScripts()"
 BlockImportScriptsWithWrongMimeType=Loading script from “%1$S” with importScripts() was blocked because of a disallowed MIME type (“%2$S”).
 BlockWorkerWithWrongMimeType=Loading Worker from “%1$S” was blocked because of a disallowed MIME type (“%2$S”).
 BlockModuleWithWrongMimeType=Loading module from “%1$S” was blocked because of a disallowed MIME type (“%2$S”).
 
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -105,20 +105,16 @@ void nsMathMLElement::UnbindFromTree(boo
 
 bool nsMathMLElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
                                      const nsAString& aValue,
                                      nsIPrincipal* aMaybeScriptedPrincipal,
                                      nsAttrValue& aResult) {
   MOZ_ASSERT(IsMathMLElement());
 
   if (aNamespaceID == kNameSpaceID_None) {
-    if (mNodeInfo->Equals(nsGkAtoms::math) && aAttribute == nsGkAtoms::mode) {
-      WarnDeprecated(nsGkAtoms::mode->GetUTF16String(),
-                     nsGkAtoms::display->GetUTF16String(), OwnerDoc());
-    }
     if (aAttribute == nsGkAtoms::color) {
       WarnDeprecated(nsGkAtoms::color->GetUTF16String(),
                      nsGkAtoms::mathcolor_->GetUTF16String(), OwnerDoc());
     }
     if (aAttribute == nsGkAtoms::color || aAttribute == nsGkAtoms::mathcolor_ ||
         aAttribute == nsGkAtoms::background ||
         aAttribute == nsGkAtoms::mathbackground_) {
       return aResult.ParseColor(aValue);
--- a/dom/media/Benchmark.cpp
+++ b/dom/media/Benchmark.cpp
@@ -274,38 +274,38 @@ void BenchmarkPlayback::FinalizeShutdown
 
 void BenchmarkPlayback::GlobalShutdown() {
   MOZ_ASSERT(OnThread());
 
   MOZ_ASSERT(!mFinished, "We've already shutdown");
 
   mFinished = true;
 
+  if (mTrackDemuxer) {
+    mTrackDemuxer->Reset();
+    mTrackDemuxer->BreakCycles();
+    mTrackDemuxer = nullptr;
+  }
+  mDemuxer = nullptr;
+
   if (mDecoder) {
     RefPtr<Benchmark> ref(mGlobalState);
     mDecoder->Flush()->Then(
         Thread(), __func__,
         [ref, this]() {
           mDecoder->Shutdown()->Then(
               Thread(), __func__, [ref, this]() { FinalizeShutdown(); },
               []() { MOZ_CRASH("not reached"); });
           mDecoder = nullptr;
           mInfo = nullptr;
         },
         []() { MOZ_CRASH("not reached"); });
   } else {
     FinalizeShutdown();
   }
-
-  if (mTrackDemuxer) {
-    mTrackDemuxer->Reset();
-    mTrackDemuxer->BreakCycles();
-    mTrackDemuxer = nullptr;
-  }
-  mDemuxer = nullptr;
 }
 
 void BenchmarkPlayback::Output(MediaDataDecoder::DecodedData&& aResults) {
   MOZ_ASSERT(OnThread());
   MOZ_ASSERT(!mFinished);
 
   RefPtr<Benchmark> ref(mGlobalState);
   mFrameCount += aResults.Length();
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -76,17 +76,17 @@ class MOZ_STACK_CLASS ReentrantMonitorCo
   }
 
  private:
   // Restrict to constructor and destructor defined above.
   ReentrantMonitorConditionallyEnter();
   ReentrantMonitorConditionallyEnter(const ReentrantMonitorConditionallyEnter&);
   ReentrantMonitorConditionallyEnter& operator=(
       const ReentrantMonitorConditionallyEnter&);
-  static void* operator new(size_t) CPP_THROW_NEW;
+  static void* operator new(size_t) noexcept(true);
   static void operator delete(void*);
 
   ReentrantMonitor* mReentrantMonitor;
 };
 
 // Shuts down a thread asynchronously.
 class ShutdownThreadEvent : public Runnable {
  public:
--- a/dom/media/gtest/TestAudioCallbackDriver.cpp
+++ b/dom/media/gtest/TestAudioCallbackDriver.cpp
@@ -48,20 +48,20 @@ TEST(TestAudioCallbackDriver, Revive)
   EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
   EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
 
   driver->Revive();
   std::this_thread::sleep_for(std::chrono::milliseconds(200));
   EXPECT_TRUE(driver->ThreadRunning()) << "Verify thread is running";
   EXPECT_TRUE(driver->IsStarted()) << "Verify thread is started";
 
-  // This will block untill all events has been executed.
+  // This will block untill all events have been executed.
   driver->AsAudioCallbackDriver()->Shutdown();
   EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
   EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
 
   // This is required because the MSG and the driver hold references between
-  // each other. The driver has a reference to SharedThreadPool which would
-  // block for ever if it was not cleared. The same logic exists in
+  // each other. The driver has a reference to SharedThreadPool which will
+  // block for ever if it is not cleared. The same logic exists in
   // MediaStreamGraphShutDownRunnable
   graph->mDriver = nullptr;
 }
 #undef ENABLE_SET_CUBEB_BACKEND
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/TestBenchmarkStorage.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/BenchmarkStorageParent.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest-printers.h"
+#include "gtest/gtest.h"
+
+using ::testing::Return;
+using namespace mozilla;
+
+TEST(BenchmarkStorage, MovingAverage)
+{
+  int32_t av = 0;
+  int32_t win = 0;
+  int32_t val = 100;
+  BenchmarkStorageParent::MovingAverage(av, win, val);
+  EXPECT_EQ(av, val) << "1st average";
+  EXPECT_EQ(win, 1) << "1st window";
+
+  av = 50;
+  win = 1;
+  val = 100;
+  BenchmarkStorageParent::MovingAverage(av, win, val);
+  EXPECT_EQ(av, 75) << "2nd average";
+  EXPECT_EQ(win, 2) << "2nd window";
+
+  av = 100;
+  win = 9;
+  val = 90;
+  BenchmarkStorageParent::MovingAverage(av, win, val);
+  EXPECT_EQ(av, 99) << "9th average";
+  EXPECT_EQ(win, 10) << "9th window";
+
+  av = 90;
+  win = 19;
+  val = 90;
+  BenchmarkStorageParent::MovingAverage(av, win, val);
+  EXPECT_EQ(av, 90) << "19th average";
+  EXPECT_EQ(win, 20) << "19th window";
+
+  av = 90;
+  win = 20;
+  val = 100;
+  BenchmarkStorageParent::MovingAverage(av, win, val);
+  EXPECT_EQ(av, 91) << "20th average";
+  EXPECT_EQ(win, 20) << "20th window";
+}
+
+TEST(BenchmarkStorage, ParseStoredValue)
+{
+  int32_t win = 0;
+  int32_t score = BenchmarkStorageParent::ParseStoredValue(1100, win);
+  EXPECT_EQ(win, 1) << "Window";
+  EXPECT_EQ(score, 100) << "Score/Percentage";
+
+  win = 0;
+  score = BenchmarkStorageParent::ParseStoredValue(10099, win);
+  EXPECT_EQ(win, 10) << "Window";
+  EXPECT_EQ(score, 99) << "Score/Percentage";
+
+  win = 0;
+  score = BenchmarkStorageParent::ParseStoredValue(15038, win);
+  EXPECT_EQ(win, 15) << "Window";
+  EXPECT_EQ(score, 38) << "Score/Percentage";
+
+  win = 0;
+  score = BenchmarkStorageParent::ParseStoredValue(20099, win);
+  EXPECT_EQ(win, 20) << "Window";
+  EXPECT_EQ(score, 99) << "Score/Percentage";
+}
+
+TEST(BenchmarkStorage, PrepareStoredValue)
+{
+  int32_t stored_value = BenchmarkStorageParent::PrepareStoredValue(80, 1);
+  EXPECT_EQ(stored_value, 1080) << "Window";
+
+  stored_value = BenchmarkStorageParent::PrepareStoredValue(100, 6);
+  EXPECT_EQ(stored_value, 6100) << "Window";
+
+  stored_value = BenchmarkStorageParent::PrepareStoredValue(1, 10);
+  EXPECT_EQ(stored_value, 10001) << "Window";
+
+  stored_value = BenchmarkStorageParent::PrepareStoredValue(88, 13);
+  EXPECT_EQ(stored_value, 13088) << "Window";
+
+  stored_value = BenchmarkStorageParent::PrepareStoredValue(100, 20);
+  EXPECT_EQ(stored_value, 20100) << "Window";
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/TestDecoderBenchmark.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DecoderBenchmark.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest-printers.h"
+#include "gtest/gtest.h"
+
+using ::testing::Return;
+using namespace mozilla;
+
+TEST(DecoderBenchmark, CreateKey)
+{
+  DecoderBenchmarkInfo info{NS_LITERAL_CSTRING("video/av1"), 1, 1, 1, 8};
+  EXPECT_EQ(KeyUtil::CreateKey(info),
+            NS_LITERAL_CSTRING("ResolutionLevel0-FrameRateLevel0-8bit"))
+      << "Min level";
+
+  DecoderBenchmarkInfo info1{NS_LITERAL_CSTRING("video/av1"), 5000, 5000, 100,
+                             8};
+  EXPECT_EQ(KeyUtil::CreateKey(info1),
+            NS_LITERAL_CSTRING("ResolutionLevel7-FrameRateLevel4-8bit"))
+      << "Max level";
+
+  DecoderBenchmarkInfo info2{NS_LITERAL_CSTRING("video/av1"), 854, 480, 30, 8};
+  EXPECT_EQ(KeyUtil::CreateKey(info2),
+            NS_LITERAL_CSTRING("ResolutionLevel3-FrameRateLevel2-8bit"))
+      << "On the top of 4th resolution level";
+
+  DecoderBenchmarkInfo info3{NS_LITERAL_CSTRING("video/av1"), 1270, 710, 24, 8};
+  EXPECT_EQ(KeyUtil::CreateKey(info3),
+            NS_LITERAL_CSTRING("ResolutionLevel4-FrameRateLevel1-8bit"))
+      << "Closer to 5th resolution level - bellow";
+
+  DecoderBenchmarkInfo info4{NS_LITERAL_CSTRING("video/av1"), 1290, 730, 24, 8};
+  EXPECT_EQ(KeyUtil::CreateKey(info4),
+            NS_LITERAL_CSTRING("ResolutionLevel4-FrameRateLevel1-8bit"))
+      << "Closer to 5th resolution level - above";
+
+  DecoderBenchmarkInfo info5{NS_LITERAL_CSTRING("video/av1"), 854, 480, 20, 8};
+  EXPECT_EQ(KeyUtil::CreateKey(info5),
+            NS_LITERAL_CSTRING("ResolutionLevel3-FrameRateLevel1-8bit"))
+      << "Closer to 2nd frame rate level - bellow";
+
+  DecoderBenchmarkInfo info6{NS_LITERAL_CSTRING("video/av1"), 854, 480, 26, 8};
+  EXPECT_EQ(KeyUtil::CreateKey(info6),
+            NS_LITERAL_CSTRING("ResolutionLevel3-FrameRateLevel1-8bit"))
+      << "Closer to 2nd frame rate level - above";
+
+  DecoderBenchmarkInfo info7{NS_LITERAL_CSTRING("video/av1"), 1280, 720, 24,
+                             10};
+  EXPECT_EQ(KeyUtil::CreateKey(info7),
+            NS_LITERAL_CSTRING("ResolutionLevel4-FrameRateLevel1-non8bit"))
+      << "Bit depth 10 bits";
+
+  DecoderBenchmarkInfo info8{NS_LITERAL_CSTRING("video/av1"), 1280, 720, 24,
+                             12};
+  EXPECT_EQ(KeyUtil::CreateKey(info8),
+            NS_LITERAL_CSTRING("ResolutionLevel4-FrameRateLevel1-non8bit"))
+      << "Bit depth 12 bits";
+
+  DecoderBenchmarkInfo info9{NS_LITERAL_CSTRING("video/av1"), 1280, 720, 24,
+                             16};
+  EXPECT_EQ(KeyUtil::CreateKey(info9),
+            NS_LITERAL_CSTRING("ResolutionLevel4-FrameRateLevel1-non8bit"))
+      << "Bit depth 16 bits";
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/TestKeyValueStorage.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/KeyValueStorage.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest-printers.h"
+#include "gtest/gtest.h"
+
+#include "GMPTestMonitor.h"
+
+using ::testing::Return;
+using namespace mozilla;
+
+TEST(TestKeyValueStorage, BasicPutGet)
+{
+  auto kvs = MakeRefPtr<KeyValueStorage>();
+
+  nsCString name("database_name");
+  nsCString key("key1");
+  int32_t value = 100;
+
+  GMPTestMonitor mon;
+
+  kvs->Put(name, key, value)
+      ->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [&](bool) { return kvs->Get(name, key); },
+          [](nsresult rv) {
+            EXPECT_TRUE(false) << "Put promise has been rejected";
+            return KeyValueStorage::GetPromise::CreateAndReject(rv, __func__);
+          })
+      ->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [&](int32_t aValue) {
+            EXPECT_EQ(aValue, value) << "Values are the same";
+            mon.SetFinished();
+          },
+          [&](nsresult rv) {
+            EXPECT_TRUE(false) << "Get Promise has been rejected";
+            mon.SetFinished();
+          });
+
+  mon.AwaitFinished();
+}
+
+TEST(TestKeyValueStorage, GetNonExistedKey)
+{
+  auto kvs = MakeRefPtr<KeyValueStorage>();
+
+  nsCString name("database_name");
+  nsCString key("NonExistedKey");
+
+  GMPTestMonitor mon;
+
+  kvs->Get(name, key)->Then(
+      GetCurrentThreadSerialEventTarget(), __func__,
+      [&mon](int32_t aValue) {
+        EXPECT_EQ(aValue, -1) << "When key does not exist return -1";
+        mon.SetFinished();
+      },
+      [&mon](nsresult rv) {
+        EXPECT_TRUE(false) << "Get Promise has been rejected";
+        mon.SetFinished();
+      });
+
+  mon.AwaitFinished();
+}
+
+TEST(TestKeyValueStorage, Clear)
+{
+  auto kvs = MakeRefPtr<KeyValueStorage>();
+
+  nsCString name("database_name");
+  nsCString key("key1");
+  int32_t value = 100;
+
+  GMPTestMonitor mon;
+
+  kvs->Put(name, key, value)
+      ->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [&](bool) { return kvs->Clear(name); },
+          [](nsresult rv) {
+            EXPECT_TRUE(false) << "Put promise has been rejected";
+            return GenericPromise::CreateAndReject(rv, __func__);
+          })
+      ->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [&](bool) { return kvs->Get(name, key); },
+          [](nsresult rv) {
+            EXPECT_TRUE(false) << "Clear promise has been rejected";
+            return KeyValueStorage::GetPromise::CreateAndReject(rv, __func__);
+          })
+      ->Then(
+          GetCurrentThreadSerialEventTarget(), __func__,
+          [&](int32_t aValue) {
+            EXPECT_EQ(aValue, -1) << "After clear the key does not exist";
+            mon.SetFinished();
+          },
+          [&](nsresult rv) {
+            EXPECT_TRUE(false) << "Get Promise has been rejected";
+            mon.SetFinished();
+          });
+
+  mon.AwaitFinished();
+}
--- a/dom/media/gtest/TestOpusParser.cpp
+++ b/dom/media/gtest/TestOpusParser.cpp
@@ -2,16 +2,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
 #include "OpusParser.h"
 #include <algorithm>
 
+using namespace mozilla;
+
 TEST(OpusParser, Mapping2)
 {
   uint8_t validChannels[] = {1,   3,   4,   6,   9,   11,  16,  18,  25,  27,
                              36,  38,  49,  51,  64,  66,  81,  83,  100, 102,
                              121, 123, 144, 146, 169, 171, 196, 198, 225, 227};
   for (uint8_t channels = 0; channels < 255; channels++) {
     bool found = OpusParser::IsValidMapping2ChannelsCount(channels);
     bool foundTable =
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -18,24 +18,27 @@ UNIFIED_SOURCES += [
     'MockMediaResource.cpp',
     'TestAudioBuffers.cpp',
     'TestAudioCallbackDriver.cpp',
     'TestAudioCompactor.cpp',
     'TestAudioMixer.cpp',
     'TestAudioPacketizer.cpp',
     'TestAudioSegment.cpp',
     'TestAudioTrackEncoder.cpp',
+    'TestBenchmarkStorage.cpp',
     'TestBitWriter.cpp',
     'TestBlankVideoDataCreator.cpp',
     'TestBufferReader.cpp',
     'TestDataMutex.cpp',
+    'TestDecoderBenchmark.cpp',
     'TestDriftCompensation.cpp',
     'TestGMPUtils.cpp',
     'TestGroupId.cpp',
     'TestIntervalSet.cpp',
+    'TestKeyValueStorage.cpp',
     'TestMediaDataDecoder.cpp',
     'TestMediaDataEncoder.cpp',
     'TestMediaEventSource.cpp',
     'TestMediaMIMETypes.cpp',
     'TestMediaSpan.cpp',
     'TestMP3Demuxer.cpp',
     'TestMP4Demuxer.cpp',
     'TestMuxer.cpp',
--- a/dom/media/mediacapabilities/BenchmarkStorageParent.cpp
+++ b/dom/media/mediacapabilities/BenchmarkStorageParent.cpp
@@ -10,17 +10,18 @@
 namespace mozilla {
 
 /* Moving average window size. */
 const int32_t AVG_WINDOW = 20;
 /* Calculate the moving average for the new value aValue for the current window
  * aWindow and the already existing average aAverage. When the method returns
  * aAverage will contain the new average and aWindow will contain the new
  * window.*/
-void MovingAverage(int32_t& aAverage, int32_t& aWindow, const int32_t aValue) {
+void BenchmarkStorageParent::MovingAverage(int32_t& aAverage, int32_t& aWindow,
+                                           const int32_t aValue) {
   if (aWindow < AVG_WINDOW) {
     aAverage = (aAverage * aWindow + aValue) / (aWindow + 1);
     aWindow++;
     return;
   }
   MOZ_ASSERT(aWindow == AVG_WINDOW);
   aAverage = (aAverage - aAverage / aWindow) + (aValue / aWindow);
 }
@@ -34,26 +35,28 @@ void MovingAverage(int32_t& aAverage, in
  * corresponds to a window(W) 7 is 7088. The average of 100 that corresponds to
  * a window of 20 is 20100. The following methods are helpers to extract or
  * construct the stored value according to the above. */
 
 /* Stored value will be in the form WWAAA(19098). We need to extract the window
  * (19) and the average score (98). The aValue will
  * be parsed, the aWindow will contain the window (of the moving average) and
  * the return value will contain the average itself. */
-int32_t ParseStoredValue(int32_t aValue, int32_t& aWindow) {
+int32_t BenchmarkStorageParent::ParseStoredValue(int32_t aValue,
+                                                 int32_t& aWindow) {
   MOZ_ASSERT(aValue > 999);
   MOZ_ASSERT(aValue < 100000);
 
   int32_t score = aValue % 1000;
   aWindow = (aValue / 1000) % 100;
   return score;
 }
 
-int32_t PrepareStoredValue(int32_t aScore, int32_t aWindow) {
+int32_t BenchmarkStorageParent::PrepareStoredValue(int32_t aScore,
+                                                   int32_t aWindow) {
   MOZ_ASSERT(aScore >= 0);
   MOZ_ASSERT(aScore <= 100);
   MOZ_ASSERT(aWindow > 0);
   MOZ_ASSERT(aWindow < 21);
 
   return aWindow * 1000 + aScore;
 }
 
--- a/dom/media/mediacapabilities/BenchmarkStorageParent.h
+++ b/dom/media/mediacapabilities/BenchmarkStorageParent.h
@@ -23,15 +23,21 @@ class BenchmarkStorageParent : public PB
   IPCResult RecvPut(const nsCString& aDbName, const nsCString& aKey,
                     const int32_t& aValue);
 
   IPCResult RecvGet(const nsCString& aDbName, const nsCString& aKey,
                     GetResolver&& aResolve);
 
   IPCResult RecvCheckVersion(const nsCString& aDbName, int32_t aVersion);
 
+  /* Helper methods exposed here to be tested via gtest. */
+  static void MovingAverage(int32_t& aAverage, int32_t& aWindow,
+                            const int32_t aValue);
+  static int32_t ParseStoredValue(int32_t aValue, int32_t& aWindow);
+  static int32_t PrepareStoredValue(int32_t aScore, int32_t aWindow);
+
  private:
   RefPtr<KeyValueStorage> mStorage;
 };
 
 }  // namespace mozilla
 
 #endif  // include_dom_media_mediacapabilities_BenchmarkStorageParent_h
--- a/dom/media/mediacapabilities/DecoderBenchmark.cpp
+++ b/dom/media/mediacapabilities/DecoderBenchmark.cpp
@@ -100,18 +100,19 @@ const uint32_t PixelLevels[] = {
 };
 const size_t PixelLevelsSize = NELEMS(PixelLevels);
 
 const uint32_t FrameRateLevels[] = {
     15, 24, 30, 50, 60,
 };
 const size_t FrameRateLevelsSize = NELEMS(FrameRateLevels);
 
-nsCString FindLevel(const uint32_t aLevels[], const size_t length,
-                    uint32_t aValue) {
+/* static */
+nsCString KeyUtil::FindLevel(const uint32_t aLevels[], const size_t length,
+                             uint32_t aValue) {
   MOZ_ASSERT(aValue);
   if (aValue <= aLevels[0]) {
     return NS_LITERAL_CSTRING("Level0");
   }
   nsAutoCString level("Level");
   size_t lastIndex = length - 1;
   if (aValue >= aLevels[lastIndex]) {
     level.AppendInt(static_cast<uint32_t>(lastIndex));
@@ -127,30 +128,32 @@ nsCString FindLevel(const uint32_t aLeve
     }
     level.AppendInt(static_cast<uint32_t>(i + 1));
     return std::move(level);
   }
   MOZ_CRASH("Array is not sorted");
   return NS_LITERAL_CSTRING("");
 }
 
-nsCString BitDepthToStr(uint8_t aBitDepth) {
+/* static */
+nsCString KeyUtil::BitDepthToStr(uint8_t aBitDepth) {
   switch (aBitDepth) {
     case 8:  // ColorDepth::COLOR_8
       return NS_LITERAL_CSTRING("-8bit");
     case 10:  // ColorDepth::COLOR_10
     case 12:  // ColorDepth::COLOR_12
     case 16:  // ColorDepth::COLOR_16
       return NS_LITERAL_CSTRING("-non8bit");
   }
   MOZ_ASSERT_UNREACHABLE("invalid color depth value");
   return NS_LITERAL_CSTRING("");
 }
 
-nsCString CreateStoreKey(const DecoderBenchmarkInfo& aBenchInfo) {
+/* static */
+nsCString KeyUtil::CreateKey(const DecoderBenchmarkInfo& aBenchInfo) {
   nsAutoCString key("Resolution");
   key.Append(FindLevel(PixelLevels, PixelLevelsSize,
                        aBenchInfo.mWidth * aBenchInfo.mHeight));
 
   key.Append("-FrameRate");
   key.Append(
       FindLevel(FrameRateLevels, FrameRateLevelsSize, aBenchInfo.mFrameRate));
 
@@ -161,31 +164,32 @@ nsCString CreateStoreKey(const DecoderBe
 
 void DecoderBenchmark::Store(const DecoderBenchmarkInfo& aBenchInfo,
                              RefPtr<FrameStatistics> aStats) {
   if (!XRE_IsContentProcess()) {
     NS_WARNING(
         "Storing a benchmark is only allowed only from the content process.");
     return;
   }
-  StoreScore(aBenchInfo.mContentType, CreateStoreKey(aBenchInfo), aStats);
+  StoreScore(aBenchInfo.mContentType, KeyUtil::CreateKey(aBenchInfo), aStats);
 }
 
 /* static */
 RefPtr<BenchmarkScorePromise> DecoderBenchmark::Get(
     const DecoderBenchmarkInfo& aBenchInfo) {
   if (!XRE_IsContentProcess()) {
     NS_WARNING(
         "Getting a benchmark is only allowed only from the content process.");
     return BenchmarkScorePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
   // There is no need for any of the data members to query the database, thus
   // it can be a static method.
   auto bench = MakeRefPtr<DecoderBenchmark>();
-  return bench->GetScore(aBenchInfo.mContentType, CreateStoreKey(aBenchInfo));
+  return bench->GetScore(aBenchInfo.mContentType,
+                         KeyUtil::CreateKey(aBenchInfo));
 }
 
 static nsDataHashtable<nsCStringHashKey, int32_t> DecoderVersionTable() {
   nsDataHashtable<nsCStringHashKey, int32_t> decoderVersionTable;
 
   /*
    * For the decoders listed here, the benchmark version number will be checked.
    * If the version number does not exist in the database or is different than
--- a/dom/media/mediacapabilities/DecoderBenchmark.h
+++ b/dom/media/mediacapabilities/DecoderBenchmark.h
@@ -57,11 +57,21 @@ class DecoderBenchmark final {
   // FrameStatistics keep an ever-increasing counter across the entire video and
   // even when there are resolution changes. This code is called whenever there
   // is a resolution change and we need to calculate the benchmark since the
   // last call.
   uint64_t mLastParsedFrames = 0;
   uint64_t mLastDroppedFrames = 0;
 };
 
+class KeyUtil {
+ public:
+  static nsCString CreateKey(const DecoderBenchmarkInfo& aBenchInfo);
+
+ private:
+  static nsCString BitDepthToStr(uint8_t aBitDepth);
+  static nsCString FindLevel(const uint32_t aLevels[], const size_t length,
+                             uint32_t aValue);
+};
+
 }  // namespace mozilla
 
 #endif  // MOZILLA_DECODER_BENCHMARK_H
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -10,37 +10,40 @@ with Files('*'):
 TEST_DIRS += ['test']
 
 DIRS += [ 'featurepolicy' ]
 
 EXPORTS.mozilla.dom += [
     'CSPEvalChecker.h',
     'FramingChecker.h',
     'nsContentSecurityManager.h',
+    'nsContentSecurityUtils.h',
     'nsCSPContext.h',
     'nsCSPService.h',
     'nsCSPUtils.h',
     'nsMixedContentBlocker.h',
     'PolicyTokenizer.h',
     'ReferrerInfo.h',
     'SRICheck.h',
     'SRILogHelper.h',
     'SRIMetadata.h',
 ]
 
 EXPORTS += [
     'nsContentSecurityManager.h',
+    'nsContentSecurityUtils.h',
     'nsMixedContentBlocker.h',
     'ReferrerInfo.h',
 ]
 
 UNIFIED_SOURCES += [
     'CSPEvalChecker.cpp',
     'FramingChecker.cpp',
     'nsContentSecurityManager.cpp',
+    'nsContentSecurityUtils.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
     'nsCSPUtils.cpp',
     'nsMixedContentBlocker.cpp',
     'PolicyTokenizer.cpp',
     'ReferrerInfo.cpp',
     'SRICheck.cpp',
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -407,31 +407,47 @@ void nsContentSecurityManager::AssertEva
     return;
   }
 
   if (!systemPrincipal && !XRE_IsE10sParentProcess()) {
     // Usage of eval we are unconcerned with.
     return;
   }
 
-  // This preferences is a file used for autoconfiguration of Firefox
+  // This preference is a file used for autoconfiguration of Firefox
   // by administrators. It has also been (ab)used by the userChromeJS
   // project to run legacy-style 'extensions', some of which use eval,
   // all of which run in the System Principal context.
-  nsAutoString configPref;
-  Preferences::GetString("general.config.filename", configPref);
-  if (!configPref.IsEmpty()) {
+  nsAutoString jsConfigPref;
+  Preferences::GetString("general.config.filename", jsConfigPref);
+  if (!jsConfigPref.IsEmpty()) {
     MOZ_LOG(
         sCSMLog, LogLevel::Debug,
         ("Allowing eval() %s because of "
          "general.config.filename",
          (systemPrincipal ? "with System Principal" : "in parent process")));
     return;
   }
 
+  // This preference is better known as userchrome.css which allows
+  // customization of the Firefox UI. Believe it or not, you can also
+  // use XBL bindings to get it to run Javascript in the same manner
+  // as userChromeJS above, so even though 99.9% of people using
+  // userchrome.css aren't doing that, we're still going to need to
+  // disable the eval() assertion for them.
+  if (Preferences::GetBool(
+          "toolkit.legacyUserProfileCustomizations.stylesheets")) {
+    MOZ_LOG(
+        sCSMLog, LogLevel::Debug,
+        ("Allowing eval() %s because of "
+         "toolkit.legacyUserProfileCustomizations.stylesheets",
+         (systemPrincipal ? "with System Principal" : "in parent process")));
+    return;
+  }
+
   // We permit these two common idioms to get access to the global JS object
   if (!aScript.IsEmpty() &&
       (aScript == sAllowedEval1 || aScript == sAllowedEval2)) {
     MOZ_LOG(
         sCSMLog, LogLevel::Debug,
         ("Allowing eval() %s because a key string is "
          "provided",
          (systemPrincipal ? "with System Principal" : "in parent process")));
new file mode 100644
--- /dev/null
+++ b/dom/security/nsContentSecurityUtils.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A namespace class for static content security utilities. */
+
+#include "nsContentSecurityUtils.h"
+
+#include "nsIContentSecurityPolicy.h"
+#include "nsIURI.h"
+
+#include "mozilla/dom/Document.h"
+
+#if defined(DEBUG) && !defined(ANDROID)
+/* static */
+void nsContentSecurityUtils::AssertAboutPageHasCSP(Document* aDocument) {
+  // We want to get to a point where all about: pages ship with a CSP. This
+  // assertion ensures that we can not deploy new about: pages without a CSP.
+  // Initially we will whitelist legacy about: pages which not yet have a CSP
+  // attached, but ultimately that whitelist should disappear.
+  // Please note that any about: page should not use inline JS or inline CSS,
+  // and instead should load JS and CSS from an external file (*.js, *.css)
+  // which allows us to apply a strong CSP omitting 'unsafe-inline'. Ideally,
+  // the CSP allows precisely the resources that need to be loaded; but it
+  // should at least be as strong as:
+  // <meta http-equiv="Content-Security-Policy" content="default-src chrome:"/>
+
+  // Check if we are loading an about: URI at all
+  nsCOMPtr<nsIURI> documentURI = aDocument->GetDocumentURI();
+  if (!documentURI->SchemeIs("about") ||
+      Preferences::GetBool("csp.skip_about_page_has_csp_assert")) {
+    return;
+  }
+
+  // Potentially init the legacy whitelist of about URIs without a CSP.
+  static StaticAutoPtr<nsTArray<nsCString>> sLegacyAboutPagesWithNoCSP;
+  if (!sLegacyAboutPagesWithNoCSP ||
+      Preferences::GetBool("csp.overrule_about_uris_without_csp_whitelist")) {
+    sLegacyAboutPagesWithNoCSP = new nsTArray<nsCString>();
+    nsAutoCString legacyAboutPages;
+    Preferences::GetCString("csp.about_uris_without_csp", legacyAboutPages);
+    for (const nsACString& hostString : legacyAboutPages.Split(',')) {
+      // please note that for the actual whitelist we only store the path of
+      // about: URI. Let's reassemble the full about URI here so we don't
+      // have to remove query arguments later.
+      nsCString aboutURI;
+      aboutURI.AppendLiteral("about:");
+      aboutURI.Append(hostString);
+      sLegacyAboutPagesWithNoCSP->AppendElement(aboutURI);
+    }
+    ClearOnShutdown(&sLegacyAboutPagesWithNoCSP);
+  }
+
+  // Check if the about URI is whitelisted
+  nsAutoCString aboutSpec;
+  documentURI->GetSpec(aboutSpec);
+  ToLowerCase(aboutSpec);
+  for (auto& legacyPageEntry : *sLegacyAboutPagesWithNoCSP) {
+    // please note that we perform a substring match here on purpose,
+    // so we don't have to deal and parse out all the query arguments
+    // the various about pages rely on.
+    if (aboutSpec.Find(legacyPageEntry) == 0) {
+      return;
+    }
+  }
+
+  nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
+  bool foundDefaultSrc = false;
+  if (csp) {
+    uint32_t policyCount = 0;
+    csp->GetPolicyCount(&policyCount);
+    nsAutoString parsedPolicyStr;
+    for (uint32_t i = 0; i < policyCount; ++i) {
+      csp->GetPolicyString(i, parsedPolicyStr);
+      if (parsedPolicyStr.Find("default-src") >= 0) {
+        foundDefaultSrc = true;
+        break;
+      }
+    }
+  }
+  if (Preferences::GetBool("csp.overrule_about_uris_without_csp_whitelist")) {
+    NS_ASSERTION(foundDefaultSrc, "about: page must have a CSP");
+    return;
+  }
+  MOZ_ASSERT(foundDefaultSrc,
+             "about: page must contain a CSP including default-src");
+}
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/security/nsContentSecurityUtils.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* A namespace class for static content security utilities. */
+
+#ifndef nsContentSecurityUtils_h___
+#define nsContentSecurityUtils_h___
+
+namespace mozilla {
+namespace dom {
+class Document;
+}  // namespace dom
+}  // namespace mozilla
+
+class nsContentSecurityUtils {
+ public:
+#if defined(DEBUG) && !defined(ANDROID)
+  static void AssertAboutPageHasCSP(Document* aDocument);
+#endif
+};
+
+#endif /* nsContentSecurityUtils_h___ */
--- a/dom/security/test/general/mochitest.ini
+++ b/dom/security/test/general/mochitest.ini
@@ -20,16 +20,17 @@ support-files =
   file_same_site_cookies_blob_iframe_navigation.html
   file_same_site_cookies_blob_iframe_inclusion.html
   file_same_site_cookies_iframe.html
   file_same_site_cookies_iframe.sjs
   file_same_site_cookies_about_navigation.html
   file_same_site_cookies_about_inclusion.html
   file_same_site_cookies_about.sjs
 
+
 [test_contentpolicytype_targeted_link_iframe.html]
 [test_nosniff.html]
 [test_nosniff_navigation.html]
 [test_block_script_wrong_mime.html]
 [test_block_toplevel_data_navigation.html]
 skip-if = toolkit == 'android' # intermittent failure
 [test_block_toplevel_data_img_navigation.html]
 skip-if = toolkit == 'android' # intermittent failure
--- a/gfx/ipc/CrossProcessPaint.cpp
+++ b/gfx/ipc/CrossProcessPaint.cpp
@@ -7,16 +7,17 @@
 #include "CrossProcessPaint.h"
 
 #include "mozilla/dom/ContentProcessManager.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/BrowserParent.h"
 #include "mozilla/dom/PWindowGlobalParent.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowGlobalActorsBinding.h"
 #include "mozilla/gfx/DrawEventRecorder.h"
 #include "mozilla/gfx/InlineTranslator.h"
 #include "mozilla/PresShell.h"
 
 #include "gfxPlatform.h"
 
 #include "nsContentUtils.h"
 #include "nsGlobalWindowInner.h"
@@ -40,17 +41,18 @@ namespace gfx {
 using namespace mozilla::ipc;
 
 /// The minimum scale we allow tabs to be rasterized at.
 static const float kMinPaintScale = 0.05f;
 
 /* static */
 PaintFragment PaintFragment::Record(nsIDocShell* aDocShell,
                                     const Maybe<IntRect>& aRect, float aScale,
-                                    nscolor aBackgroundColor) {
+                                    nscolor aBackgroundColor,
+                                    CrossProcessPaintFlags aFlags) {
   if (!aDocShell) {
     PF_LOG("Couldn't find DocShell.\n");
     return PaintFragment{};
   }
 
   RefPtr<nsPresContext> presContext = aDocShell->GetPresContext();
   if (!presContext) {
     PF_LOG("Couldn't find PresContext.\n");
@@ -109,28 +111,34 @@ PaintFragment PaintFragment::Record(nsID
       gfxPlatform::GetPlatform()->GetSoftwareBackend(), IntSize(1, 1), format);
 
   // TODO: This may OOM crash if the content is complex enough
   RefPtr<DrawEventRecorderMemory> recorder =
       MakeAndAddRef<DrawEventRecorderMemory>(nullptr);
   RefPtr<DrawTarget> dt = Factory::CreateRecordingDrawTarget(
       recorder, referenceDt, IntRect(IntPoint(0, 0), surfaceSize));
 
+  RenderDocumentFlags renderDocFlags = RenderDocumentFlags::None;
+  if (!(aFlags & CrossProcessPaintFlags::DrawView)) {
+    renderDocFlags = (RenderDocumentFlags::IgnoreViewportScrolling |
+                      RenderDocumentFlags::DocumentRelative);
+  }
+
   // Perform the actual rendering
   {
     nsRect r(nsPresContext::CSSPixelsToAppUnits(rect.x),
              nsPresContext::CSSPixelsToAppUnits(rect.y),
              nsPresContext::CSSPixelsToAppUnits(rect.width),
              nsPresContext::CSSPixelsToAppUnits(rect.height));
 
     RefPtr<gfxContext> thebes = gfxContext::CreateOrNull(dt);
     thebes->SetMatrix(Matrix::Scaling(aScale, aScale));
     RefPtr<PresShell> presShell = presContext->PresShell();
-    Unused << presShell->RenderDocument(r, RenderDocumentFlags::None,
-                                        aBackgroundColor, thebes);
+    Unused << presShell->RenderDocument(r, renderDocFlags, aBackgroundColor,
+                                        thebes);
   }
 
   ByteBuf recording = ByteBuf((uint8_t*)recorder->mOutputStream.mData,
                               recorder->mOutputStream.mLength,
                               recorder->mOutputStream.mCapacity);
   recorder->mOutputStream.mData = nullptr;
   recorder->mOutputStream.mLength = 0;
   recorder->mOutputStream.mCapacity = 0;
@@ -151,16 +159,17 @@ PaintFragment::PaintFragment(IntSize aSi
     : mSize(aSize),
       mRecording(std::move(aRecording)),
       mDependencies(std::move(aDependencies)) {}
 
 /* static */
 bool CrossProcessPaint::Start(dom::WindowGlobalParent* aRoot,
                               const dom::DOMRect* aRect, float aScale,
                               nscolor aBackgroundColor,
+                              CrossProcessPaintFlags aFlags,
                               dom::Promise* aPromise) {
   MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
   aScale = std::max(aScale, kMinPaintScale);
 
   CPP_LOG(
       "Starting paint. "
       "[wgp=%p, "
       "scale=%f, "
@@ -188,50 +197,63 @@ bool CrossProcessPaint::Start(dom::Windo
     nsCOMPtr<nsIDocShell> docShell =
         childActor->BrowsingContext()->GetDocShell();
     if (!docShell) {
       return false;
     }
 
     resolver->mPendingFragments += 1;
     resolver->ReceiveFragment(
-        aRoot, PaintFragment::Record(docShell, rect, aScale, aBackgroundColor));
+        aRoot, PaintFragment::Record(docShell, rect, aScale, aBackgroundColor,
+                                     aFlags));
   } else {
-    resolver->QueuePaint(aRoot, rect, aBackgroundColor);
+    resolver->QueuePaint(aRoot, rect, aBackgroundColor, aFlags);
   }
   return true;
 }
 
 CrossProcessPaint::CrossProcessPaint(dom::Promise* aPromise, float aScale,
                                      dom::WindowGlobalParent* aRoot)
     : mPromise{aPromise}, mRoot{aRoot}, mScale{aScale}, mPendingFragments{0} {}
 
 CrossProcessPaint::~CrossProcessPaint() {}
 
+static dom::TabId GetTabId(dom::WindowGlobalParent* aWGP) {
+  // There is no unique TabId for a given WindowGlobalParent, as multiple
+  // WindowGlobalParents share the same PBrowser actor. However, we only
+  // ever queue one paint per PBrowser by just using the current
+  // WindowGlobalParent for a PBrowser. So we can interchange TabId and
+  // WindowGlobalParent when dealing with resolving surfaces.
+  RefPtr<dom::BrowserParent> browserParent = aWGP->GetBrowserParent();
+  return browserParent ? browserParent->GetTabId() : dom::TabId(0);
+}
+
 void CrossProcessPaint::ReceiveFragment(dom::WindowGlobalParent* aWGP,
                                         PaintFragment&& aFragment) {
   if (IsCleared()) {
     CPP_LOG("Ignoring fragment from %p.\n", aWGP);
     return;
   }
 
+  dom::TabId surfaceId = GetTabId(aWGP);
+
   MOZ_ASSERT(mPendingFragments > 0);
-  MOZ_ASSERT(!mReceivedFragments.GetValue(aWGP));
+  MOZ_ASSERT(!mReceivedFragments.GetValue(surfaceId));
   MOZ_ASSERT(!aFragment.IsEmpty());
 
   // Double check our invariants to protect against a compromised content
   // process
-  if (mPendingFragments == 0 || mReceivedFragments.GetValue(aWGP) ||
+  if (mPendingFragments == 0 || mReceivedFragments.GetValue(surfaceId) ||
       aFragment.IsEmpty()) {
     CPP_LOG("Dropping invalid fragment from %p.\n", aWGP);
     LostFragment(aWGP);
     return;
   }
 
-  CPP_LOG("Receiving fragment from %p.\n", aWGP);
+  CPP_LOG("Receiving fragment from %p(%llu).\n", aWGP, (uint64_t)surfaceId);
 
   // Queue paints for child tabs
   for (auto iter = aFragment.mDependencies.Iter(); !iter.Done(); iter.Next()) {
     auto dependency = dom::TabId(iter.Get()->GetKey());
 
     // Get the current WindowGlobalParent of the remote browser that was marked
     // as a dependency
     dom::ContentProcessManager* cpm =
@@ -247,82 +269,76 @@ void CrossProcessPaint::ReceiveFragment(
               (uint64_t)dependency);
       continue;
     }
 
     // TODO: Apply some sort of clipping to visible bounds here (Bug 1562720)
     QueuePaint(wgp, Nothing());
   }
 
-  mReceivedFragments.Put(aWGP, std::move(aFragment));
+  mReceivedFragments.Put(surfaceId, std::move(aFragment));
   mPendingFragments -= 1;
 
   // Resolve this paint if we have received all pending fragments
   MaybeResolve();
 }
 
 void CrossProcessPaint::LostFragment(dom::WindowGlobalParent* aWGP) {
   if (IsCleared()) {
     CPP_LOG("Ignoring lost fragment from %p.\n", aWGP);
     return;
   }
 
-  mPromise->MaybeReject(NS_ERROR_FAILURE);
+  mPromise->MaybeReject(NS_ERROR_LOSS_OF_SIGNIFICANT_DATA);
   Clear();
 }
 
 void CrossProcessPaint::QueuePaint(dom::WindowGlobalParent* aWGP,
                                    const Maybe<IntRect>& aRect,
-                                   nscolor aBackgroundColor) {
-  MOZ_ASSERT(!mReceivedFragments.GetValue(aWGP));
+                                   nscolor aBackgroundColor,
+                                   CrossProcessPaintFlags aFlags) {
+  MOZ_ASSERT(!mReceivedFragments.GetValue(GetTabId(aWGP)));
 
   CPP_LOG("Queueing paint for %p.\n", aWGP);
 
-  // TODO - Don't apply the background color to all paints (Bug 1562722)
-  aWGP->DrawSnapshotInternal(this, aRect, mScale, aBackgroundColor);
+  aWGP->DrawSnapshotInternal(this, aRect, mScale, aBackgroundColor,
+                             (uint32_t)aFlags);
   mPendingFragments += 1;
 }
 
 void CrossProcessPaint::Clear() {
   mPromise = nullptr;
   mPendingFragments = 0;
   mReceivedFragments.Clear();
 }
 
 bool CrossProcessPaint::IsCleared() const { return !mPromise; }
 
-static dom::TabId GetTabId(dom::WindowGlobalParent* aWGP) {
-  // There is no unique TabId for a given WindowGlobalParent, as multiple
-  // WindowGlobalParents share the same PBrowser actor. However, we only
-  // ever queue one paint per PBrowser by just using the current
-  // WindowGlobalParent for a PBrowser. So we can interchange TabId and
-  // WindowGlobalParent when dealing with resolving surfaces.
-  RefPtr<dom::BrowserParent> browserParent = aWGP->GetBrowserParent();
-  return browserParent ? browserParent->GetTabId() : dom::TabId(0);
-}
-
 void CrossProcessPaint::MaybeResolve() {
   // Don't do anything if we aren't ready, experienced an error, or already
   // resolved this paint
   if (IsCleared() || mPendingFragments > 0) {
     CPP_LOG("Not ready to resolve yet, have %u fragments left.\n",
             mPendingFragments);
     return;
   }
 
   CPP_LOG("Starting to resolve fragments.\n");
 
   // Resolve the paint fragments from the bottom up
   ResolvedSurfaceMap resolved;
-  if (!ResolveInternal(mRoot, &resolved)) {
-    CPP_LOG("Couldn't resolve.\n");
+  {
+    nsresult rv = ResolveInternal(GetTabId(mRoot), &resolved);
+    if (NS_FAILED(rv)) {
+      CPP_LOG("Couldn't resolve.\n");
 
-    mPromise->MaybeReject(NS_ERROR_FAILURE);
-    Clear();
-    return;
+      mPromise->MaybeReject(rv);
+      Clear();
+      return;
+    }
   }
 
   // Grab the result from the resolved table.
   RefPtr<SourceSurface> root = resolved.Get(GetTabId(mRoot));
   CPP_LOG("Resolved all fragments.\n");
 
   ErrorResult rv;
   RefPtr<dom::ImageBitmap> bitmap = dom::ImageBitmap::CreateFromSourceSurface(
@@ -333,77 +349,71 @@ void CrossProcessPaint::MaybeResolve() {
     mPromise->MaybeResolve(bitmap);
   } else {
     CPP_LOG("Couldn't create ImageBitmap for SourceSurface.\n");
     mPromise->MaybeReject(rv);
   }
   Clear();
 }
 
-bool CrossProcessPaint::ResolveInternal(dom::WindowGlobalParent* aWGP,
-                                        ResolvedSurfaceMap* aResolved) {
-  // Convert aWGP to an ID we can use for surfaces
-  dom::TabId surfaceId = GetTabId(aWGP);
-
+nsresult CrossProcessPaint::ResolveInternal(dom::TabId aTabId,
+                                            ResolvedSurfaceMap* aResolved) {
   // We should not have resolved this paint already
-  MOZ_ASSERT(!aResolved->GetWeak(surfaceId));
+  MOZ_ASSERT(!aResolved->GetWeak(aTabId));
+
+  CPP_LOG("Resolving fragment %llu.\n", (uint64_t)aTabId);
 
-  CPP_LOG("Resolving fragment %p.\n", aWGP);
-
-  Maybe<PaintFragment> fragment = mReceivedFragments.GetAndRemove(aWGP);
+  Maybe<PaintFragment> fragment = mReceivedFragments.GetAndRemove(aTabId);
+  if (!fragment) {
+    return NS_ERROR_LOSS_OF_SIGNIFICANT_DATA;
+  }
 
   // Rasterize all the dependencies first so that we can resolve this fragment
   for (auto iter = fragment->mDependencies.Iter(); !iter.Done(); iter.Next()) {
     auto dependency = dom::TabId(iter.Get()->GetKey());
 
-    dom::ContentProcessManager* cpm =
-        dom::ContentProcessManager::GetSingleton();
-    dom::ContentParentId cpId = cpm->GetTabProcessId(dependency);
-    RefPtr<dom::BrowserParent> tab =
-        cpm->GetBrowserParentByProcessAndTabId(cpId, dependency);
-    RefPtr<dom::WindowGlobalParent> wgp =
-        tab->GetBrowsingContext()->GetCurrentWindowGlobal();
-
-    if (!ResolveInternal(wgp, aResolved)) {
-      return false;
+    nsresult rv = ResolveInternal(dependency, aResolved);
+    if (NS_FAILED(rv)) {
+      return rv;
     }
   }
 
   // Create the destination draw target
   RefPtr<DrawTarget> drawTarget =
       gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
           fragment->mSize, SurfaceFormat::B8G8R8A8);
   if (!drawTarget || !drawTarget->IsValid()) {
-    CPP_LOG("Couldn't create (%d x %d) surface for fragment %p.\n",
-            fragment->mSize.width, fragment->mSize.height, aWGP);
-    return false;
+    CPP_LOG("Couldn't create (%d x %d) surface for fragment %llu.\n",
+            fragment->mSize.width, fragment->mSize.height, (uint64_t)aTabId);
+    return NS_ERROR_FAILURE;
   }
 
   // Translate the recording using our child tabs
   {
     InlineTranslator translator(drawTarget, nullptr);
     translator.SetExternalSurfaces(aResolved);
     if (!translator.TranslateRecording((char*)fragment->mRecording.mData,
                                        fragment->mRecording.mLen)) {
-      CPP_LOG("Couldn't translate recording for fragment %p.\n", aWGP);
-      return false;
+      CPP_LOG("Couldn't translate recording for fragment %llu.\n",
+              (uint64_t)aTabId);
+      return NS_ERROR_FAILURE;
     }
   }
 
   RefPtr<SourceSurface> snapshot = drawTarget->Snapshot();
   if (!snapshot) {
-    CPP_LOG("Couldn't get snapshot for fragment %p.\n", aWGP);
-    return false;
+    CPP_LOG("Couldn't get snapshot for fragment %llu.\n", (uint64_t)aTabId);
+    return NS_ERROR_FAILURE;
   }
 
   // We are done with the resolved images of our dependencies, let's remove
   // them
   for (auto iter = fragment->mDependencies.Iter(); !iter.Done(); iter.Next()) {
     auto dependency = iter.Get()->GetKey();
     aResolved->Remove(dependency);
   }
 
-  aResolved->Put(surfaceId, snapshot);
-  return true;
+  aResolved->Put(aTabId, snapshot);
+  return NS_OK;
 }
 
 }  // namespace gfx
 }  // namespace mozilla
--- a/gfx/ipc/CrossProcessPaint.h
+++ b/gfx/ipc/CrossProcessPaint.h
@@ -29,16 +29,23 @@ namespace mozilla {
 namespace dom {
 class WindowGlobalParent;
 }  // namespace dom
 
 namespace gfx {
 
 class CrossProcessPaint;
 
+enum class CrossProcessPaintFlags {
+  None = 0,
+  DrawView = 1 << 1,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(CrossProcessPaintFlags)
+
 /**
  * A fragment of a paint of a cross process document tree.
  */
 class PaintFragment final {
  public:
   /// Initializes an empty PaintFragment
   PaintFragment() = default;
 
@@ -54,17 +61,18 @@ class PaintFragment final {
    *   at least kMinPaintScale.
    * @param aBackgroundColor The background color to use.
    *
    * @return A paint fragment. The paint fragment may be `empty` if rendering
    *         was unable to be accomplished for some reason.
    */
   static PaintFragment Record(nsIDocShell* aDocShell,
                               const Maybe<IntRect>& aRect, float aScale,
-                              nscolor aBackgroundColor);
+                              nscolor aBackgroundColor,
+                              CrossProcessPaintFlags aFlags);
 
   /// Returns whether this paint fragment contains a valid recording.
   bool IsEmpty() const;
 
   PaintFragment(PaintFragment&&) = default;
   PaintFragment& operator=(PaintFragment&&) = default;
 
  protected:
@@ -103,47 +111,46 @@ class CrossProcessPaint final {
    *   minimum value.
    * @param aBackgroundColor The background color to use.
    * @param aPromise The promise to resolve with a dom::ImageBitmap.
    *
    * @returns Whether the paint was able to be initiated or not.
    */
   static bool Start(dom::WindowGlobalParent* aRoot, const dom::DOMRect* aRect,
                     float aScale, nscolor aBackgroundColor,
-                    dom::Promise* aPromise);
+                    CrossProcessPaintFlags aFlags, dom::Promise* aPromise);
 
   void ReceiveFragment(dom::WindowGlobalParent* aWGP,
                        PaintFragment&& aFragment);
   void LostFragment(dom::WindowGlobalParent* aWGP);
 
  private:
   typedef nsRefPtrHashtable<nsUint64HashKey, SourceSurface> ResolvedSurfaceMap;
-  typedef nsDataHashtable<nsRefPtrHashKey<dom::WindowGlobalParent>,
-                          PaintFragment>
-      ReceivedFragmentMap;
+  typedef nsDataHashtable<nsUint64HashKey, PaintFragment> ReceivedFragmentMap;
 
   CrossProcessPaint(dom::Promise* aPromise, float aScale,
                     dom::WindowGlobalParent* aRoot);
   ~CrossProcessPaint();
 
-  void QueuePaint(dom::WindowGlobalParent* aWGP, const Maybe<IntRect>& aRect,
-                  nscolor aBackgroundColor = NS_RGBA(0, 0, 0, 0));
+  void QueuePaint(
+      dom::WindowGlobalParent* aWGP, const Maybe<IntRect>& aRect,
+      nscolor aBackgroundColor = NS_RGBA(0, 0, 0, 0),
+      CrossProcessPaintFlags aFlags = CrossProcessPaintFlags::DrawView);
 
   /// Clear the state of this paint so that it cannot be resolved or receive
   /// any paint fragments.
   void Clear();
 
   /// Returns if this paint has been cleared.
   bool IsCleared() const;
 
   /// Resolves the paint fragments if we have none pending and resolves the
   /// promise.
   void MaybeResolve();
-  bool ResolveInternal(dom::WindowGlobalParent* aWGP,
-                       ResolvedSurfaceMap* aResolved);
+  nsresult ResolveInternal(dom::TabId aTabId, ResolvedSurfaceMap* aResolved);
 
   RefPtr<dom::Promise> mPromise;
   RefPtr<dom::WindowGlobalParent> mRoot;
   float mScale;
   uint32_t mPendingFragments;
   ReceivedFragmentMap mReceivedFragments;
 };
 
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -14,17 +14,16 @@
 #include "nsCoord.h"    // for nscoord
 #include "nsError.h"    // for nsresult
 #include "nsPoint.h"    // for nsIntPoint, nsPoint
 #include "nsRect.h"     // for mozilla::gfx::IntRect, nsRect
 #include "nsRectAbsolute.h"
 #include "nsMargin.h"               // for nsIntMargin
 #include "nsRegionFwd.h"            // for nsIntRegion
 #include "nsString.h"               // for nsCString
-#include "xpcom-config.h"           // for CPP_THROW_NEW
 #include "mozilla/ArrayView.h"      // for ArrayView
 #include "mozilla/Move.h"           // for mozilla::Move
 #include "mozilla/gfx/MatrixFwd.h"  // for mozilla::gfx::Matrix4x4
 #include "mozilla/gfx/Logging.h"
 #include "nsTArray.h"
 
 #include "pixman.h"
 
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -2565,16 +2565,23 @@ bool imgLoader::SupportImageWithMimeType
   DecoderType type = DecoderFactory::GetDecoderType(mimeType.get());
   return type != DecoderType::UNKNOWN;
 }
 
 NS_IMETHODIMP
 imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
                                   const uint8_t* aContents, uint32_t aLength,
                                   nsACString& aContentType) {
+  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+  if (channel) {
+    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
+    if (loadInfo->GetSkipContentSniffing()) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
+  }
   return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType);
 }
 
 /* static */
 nsresult imgLoader::GetMimeTypeFromContent(const char* aContents,
                                            uint32_t aLength,
                                            nsACString& aContentType) {
   /* Is it a GIF? */
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -568,25 +568,23 @@ class JS_FRIEND_API ElementAdder {
   bool append(JSContext* cx, JS::HandleValue v);
   void appendHole();
 };
 
 typedef bool (*GetElementsOp)(JSContext* cx, JS::HandleObject obj,
                               uint32_t begin, uint32_t end,
                               ElementAdder* adder);
 
-typedef void (*FinalizeOp)(JSFreeOp* fop, JSObject* obj);
-
 // The special treatment of |finalize| and |trace| is necessary because if we
 // assign either of those hooks to a local variable and then call it -- as is
 // done with the other hooks -- the GC hazard analysis gets confused.
-#define JS_CLASS_MEMBERS(ClassOpsType, FreeOpType)                             \
+#define JS_CLASS_MEMBERS                                                       \
   const char* name;                                                            \
   uint32_t flags;                                                              \
-  const ClassOpsType* cOps;                                                    \
+  const JSClassOps* cOps;                                                      \
                                                                                \
   JSAddPropertyOp getAddProperty() const {                                     \
     return cOps ? cOps->addProperty : nullptr;                                 \
   }                                                                            \
   JSDeletePropertyOp getDelProperty() const {                                  \
     return cOps ? cOps->delProperty : nullptr;                                 \
   }                                                                            \
   JSEnumerateOp getEnumerate() const {                                         \
@@ -605,40 +603,25 @@ typedef void (*FinalizeOp)(JSFreeOp* fop
   }                                                                            \
   JSNative getConstruct() const { return cOps ? cOps->construct : nullptr; }   \
                                                                                \
   bool hasFinalize() const { return cOps && cOps->finalize; }                  \
   bool hasTrace() const { return cOps && cOps->trace; }                        \
                                                                                \
   bool isTrace(JSTraceOp trace) const { return cOps && cOps->trace == trace; } \
                                                                                \
-  void doFinalize(FreeOpType* fop, JSObject* obj) const {                      \
+  void doFinalize(JSFreeOp* fop, JSObject* obj) const {                        \
     MOZ_ASSERT(cOps && cOps->finalize);                                        \
     cOps->finalize(fop, obj);                                                  \
   }                                                                            \
   void doTrace(JSTracer* trc, JSObject* obj) const {                           \
     MOZ_ASSERT(cOps && cOps->trace);                                           \
     cOps->trace(trc, obj);                                                     \
   }
 
-struct MOZ_STATIC_CLASS ClassOps {
-  /* Function pointer members (may be null). */
-  JSAddPropertyOp addProperty;
-  JSDeletePropertyOp delProperty;
-  JSEnumerateOp enumerate;
-  JSNewEnumerateOp newEnumerate;
-  JSResolveOp resolve;
-  JSMayResolveOp mayResolve;
-  FinalizeOp finalize;
-  JSNative call;
-  JSHasInstanceOp hasInstance;
-  JSNative construct;
-  JSTraceOp trace;
-};
-
 /** Callback for the creation of constructor and prototype objects. */
 typedef JSObject* (*ClassObjectCreationOp)(JSContext* cx, JSProtoKey key);
 
 /**
  * Callback for custom post-processing after class initialization via
  * ClassSpec.
  */
 typedef bool (*FinishClassInitOp)(JSContext* cx, JS::HandleObject ctor,
@@ -740,17 +723,17 @@ struct MOZ_STATIC_CLASS JSClassOps {
   JSHasInstanceOp hasInstance;
   JSNative construct;
   JSTraceOp trace;
 };
 
 #define JS_NULL_CLASS_OPS nullptr
 
 struct JSClass {
-  JS_CLASS_MEMBERS(JSClassOps, JSFreeOp);
+  JS_CLASS_MEMBERS;
 
   void* reserved[3];
 };
 
 // Objects have private slot.
 static const uint32_t JSCLASS_HAS_PRIVATE = 1 << 0;
 
 // Class's initialization code will call `SetNewObjectMetadata` itself.
@@ -862,17 +845,17 @@ static const uint32_t JSCLASS_CACHED_PRO
 // Initializer for unused members of statically initialized JSClass structs.
 #define JSCLASS_NO_INTERNAL_MEMBERS \
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
 #define JSCLASS_NO_OPTIONAL_MEMBERS 0, 0, 0, 0, 0, JSCLASS_NO_INTERNAL_MEMBERS
 
 namespace js {
 
 struct MOZ_STATIC_CLASS Class {
-  JS_CLASS_MEMBERS(js::ClassOps, JSFreeOp);
+  JS_CLASS_MEMBERS;
   const ClassSpec* spec;
   const ClassExtension* ext;
   const ObjectOps* oOps;
 
   /*
    * Objects of this class aren't native objects. They don't have Shapes that
    * describe their properties and layout. Classes using this flag must
    * provide their own property behavior, either by being proxy classes (do
@@ -965,46 +948,16 @@ struct MOZ_STATIC_CLASS Class {
   GetElementsOp getOpsGetElements() const {
     return oOps ? oOps->getElements : nullptr;
   }
   JSFunToStringOp getOpsFunToString() const {
     return oOps ? oOps->funToString : nullptr;
   }
 };
 
-static_assert(offsetof(JSClassOps, addProperty) ==
-                  offsetof(ClassOps, addProperty),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, delProperty) ==
-                  offsetof(ClassOps, delProperty),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, enumerate) == offsetof(ClassOps, enumerate),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, newEnumerate) ==
-                  offsetof(ClassOps, newEnumerate),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, resolve) == offsetof(ClassOps, resolve),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, mayResolve) ==
-                  offsetof(ClassOps, mayResolve),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, finalize) == offsetof(ClassOps, finalize),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, call) == offsetof(ClassOps, call),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, construct) == offsetof(ClassOps, construct),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, hasInstance) ==
-                  offsetof(ClassOps, hasInstance),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(offsetof(JSClassOps, trace) == offsetof(ClassOps, trace),
-              "ClassOps and JSClassOps must be consistent");
-static_assert(sizeof(JSClassOps) == sizeof(ClassOps),
-              "ClassOps and JSClassOps must be consistent");
-
 static_assert(offsetof(JSClass, name) == offsetof(Class, name),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, flags) == offsetof(Class, flags),
               "Class and JSClass must be consistent");
 static_assert(offsetof(JSClass, cOps) == offsetof(Class, cOps),
               "Class and JSClass must be consistent");
 static_assert(sizeof(JSClass) == sizeof(Class),
               "Class and JSClass must be consistent");
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -71,28 +71,28 @@ class JS_FRIEND_API Wrapper;
  *     obj.prop                     obj.[[Get]](obj, "prop")
  *     "prop" in obj                obj.[[HasProperty]]("prop")
  *     new obj()                    obj.[[Construct]](<empty argument List>)
  *
  * With regard to the implementation of these internal methods, there are three
  * very different kinds of object in SpiderMonkey.
  *
  * 1.  Native objects cover most objects and contain both internal slots and
- *     properties. ClassOps and ObjectOps may be used to override certain
+ *     properties. JSClassOps and ObjectOps may be used to override certain
  *     default behaviors.
  *
  * 2.  Proxy objects are composed of internal slots and a ProxyHandler. The
  *     handler contains C++ methods that can implement these standard (and
- *     non-standard) internal methods. ClassOps and ObjectOps for the base
+ *     non-standard) internal methods. JSClassOps and ObjectOps for the base
  *     ProxyObject invoke the handler methods as appropriate.
  *
- * 3.  Objects with custom layouts like TypedObjects. These rely on ClassOps
+ * 3.  Objects with custom layouts like TypedObjects. These rely on JSClassOps
  *     and ObjectOps to implement internal methods.
  *
- * Native objects with custom ClassOps / ObjectOps are used when the object
+ * Native objects with custom JSClassOps / ObjectOps are used when the object
  * behaves very similar to a normal object such as the ArrayObject and it's
  * length property. Most usages wrapping a C++ or other type should prefer
  * using a Proxy. Using the proxy approach makes it much easier to create an
  * ECMAScript and JIT compatible object, particularly if using an appropriate
  * base class.
  *
  * Just about anything you do to a proxy will end up going through a C++
  * virtual method call. Possibly several. There's no reason the JITs and ICs
@@ -689,17 +689,17 @@ class JS_FRIEND_API AutoWaivePolicy {
 extern JS_FRIEND_API void assertEnteredPolicy(JSContext* cx, JSObject* obj,
                                               jsid id,
                                               BaseProxyHandler::Action act);
 #else
 inline void assertEnteredPolicy(JSContext* cx, JSObject* obj, jsid id,
                                 BaseProxyHandler::Action act) {}
 #endif
 
-extern JS_FRIEND_DATA const js::ClassOps ProxyClassOps;
+extern JS_FRIEND_DATA const JSClassOps ProxyClassOps;
 extern JS_FRIEND_DATA const js::ClassExtension ProxyClassExtension;
 extern JS_FRIEND_DATA const js::ObjectOps ProxyObjectOps;
 
 template <unsigned Flags>
 constexpr unsigned CheckProxyFlags() {
   // For now assert each Proxy Class has at least 1 reserved slot. This is
   // not a hard requirement, but helps catch Classes that need an explicit
   // JSCLASS_HAS_RESERVED_SLOTS since bug 1360523.
--- a/js/rust/build.rs
+++ b/js/rust/build.rs
@@ -140,16 +140,17 @@ const UNSAFE_IMPL_SYNC_TYPES: &'static [
     "JSTypedMethodJitInfo",
 ];
 
 /// Flags passed through bindgen directly to Clang.
 const EXTRA_CLANG_FLAGS: &'static [&'static str] = &[
     "-x", "c++",
     "-std=gnu++14",
     "-fno-sized-deallocation",
+    "-fno-aligned-new",
     "-DRUST_BINDGEN",
 ];
 
 /// Types which we want to generate bindings for (and every other type they
 /// transitively use).
 const WHITELIST_TYPES: &'static [&'static str] = &[
     "JS::AutoCheckCannotGC",
     "JS::CallArgs",
--- a/js/src/builtin/Array.cpp
+++ b/js/src/builtin/Array.cpp
@@ -3966,17 +3966,17 @@ static bool array_proto_finish(JSContext
   }
 
   RootedId id(cx, SYMBOL_TO_JSID(
                       cx->wellKnownSymbols().get(JS::SymbolCode::unscopables)));
   value.setObject(*unscopables);
   return DefineDataProperty(cx, proto, id, value, JSPROP_READONLY);
 }
 
-static const ClassOps ArrayObjectClassOps = {
+static const JSClassOps ArrayObjectClassOps = {
     array_addProperty, nullptr, /* delProperty */
     nullptr,                    /* enumerate */
     nullptr,                    /* resolve */
     nullptr,                    /* mayResolve */
     nullptr,                    /* finalize */
     nullptr,                    /* call */
     nullptr,                    /* hasInstance */
     nullptr,                    /* construct */
--- a/js/src/builtin/DataViewObject.cpp
+++ b/js/src/builtin/DataViewObject.cpp
@@ -976,27 +976,27 @@ bool DataViewObject::byteOffsetGetter(JS
   return CallNonGenericMethod<is, byteOffsetGetterImpl>(cx, args);
 }
 
 JSObject* DataViewObject::CreatePrototype(JSContext* cx, JSProtoKey key) {
   return GlobalObject::createBlankPrototype(cx, cx->global(),
                                             &DataViewObject::protoClass_);
 }
 
-static const ClassOps DataViewObjectClassOps = {nullptr, /* addProperty */
-                                                nullptr, /* delProperty */
-                                                nullptr, /* enumerate */
-                                                nullptr, /* newEnumerate */
-                                                nullptr, /* resolve */
-                                                nullptr, /* mayResolve */
-                                                nullptr, /* finalize */
-                                                nullptr, /* call */
-                                                nullptr, /* hasInstance */
-                                                nullptr, /* construct */
-                                                ArrayBufferViewObject::trace};
+static const JSClassOps DataViewObjectClassOps = {nullptr, /* addProperty */
+                                                  nullptr, /* delProperty */
+                                                  nullptr, /* enumerate */
+                                                  nullptr, /* newEnumerate */
+                                                  nullptr, /* resolve */
+                                                  nullptr, /* mayResolve */
+                                                  nullptr, /* finalize */
+                                                  nullptr, /* call */
+                                                  nullptr, /* hasInstance */
+                                                  nullptr, /* construct */
+                                                  ArrayBufferViewObject::trace};
 
 const ClassSpec DataViewObject::classSpec_ = {
     GenericCreateConstructor<DataViewObject::construct, 1,
                              gc::AllocKind::FUNCTION>,
     GenericCreatePrototype<DataViewObject>,
     nullptr,
     nullptr,
     DataViewObject::methods,
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -119,23 +119,24 @@ HashableValue HashableValue::trace(JSTra
   TraceEdge(trc, &hv.value, "key");
   return hv;
 }
 
 /*** MapIterator ************************************************************/
 
 namespace {} /* anonymous namespace */
 
-static const ClassOps MapIteratorObjectClassOps = {nullptr, /* addProperty */
-                                                   nullptr, /* delProperty */
-                                                   nullptr, /* enumerate */
-                                                   nullptr, /* newEnumerate */
-                                                   nullptr, /* resolve */
-                                                   nullptr, /* mayResolve */
-                                                   MapIteratorObject::finalize};
+static const JSClassOps MapIteratorObjectClassOps = {
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    MapIteratorObject::finalize};
 
 static const ClassExtension MapIteratorObjectClassExtension = {
     MapIteratorObject::objectMoved};
 
 const Class MapIteratorObject::class_ = {
     "Map Iterator",
     JSCLASS_HAS_RESERVED_SLOTS(MapIteratorObject::SlotCount) |
         JSCLASS_FOREGROUND_FINALIZE | JSCLASS_SKIP_NURSERY_FINALIZE,
@@ -365,27 +366,27 @@ JSObject* MapIteratorObject::createResul
   // See comments in MapIteratorObject::next.
   AddTypePropertyId(cx, resultPairObj, JSID_VOID, TypeSet::UnknownType());
 
   return resultPairObj;
 }
 
 /*** Map ********************************************************************/
 
-const ClassOps MapObject::classOps_ = {nullptr,  // addProperty
-                                       nullptr,  // delProperty
-                                       nullptr,  // enumerate
-                                       nullptr,  // newEnumerate
-                                       nullptr,  // resolve
-                                       nullptr,  // mayResolve
-                                       finalize,
-                                       nullptr,  // call
-                                       nullptr,  // hasInstance
-                                       nullptr,  // construct
-                                       trace};
+const JSClassOps MapObject::classOps_ = {nullptr,  // addProperty
+                                         nullptr,  // delProperty
+                                         nullptr,  // enumerate
+                                         nullptr,  // newEnumerate
+                                         nullptr,  // resolve
+                                         nullptr,  // mayResolve
+                                         finalize,
+                                         nullptr,  // call
+                                         nullptr,  // hasInstance
+                                         nullptr,  // construct
+                                         trace};
 
 const ClassSpec MapObject::classSpec_ = {
     GenericCreateConstructor<MapObject::construct, 0, gc::AllocKind::FUNCTION>,
     GenericCreatePrototype<MapObject>,
     nullptr,
     MapObject::staticProperties,
     MapObject::methods,
     MapObject::properties,
@@ -900,23 +901,24 @@ bool MapObject::clear(JSContext* cx, Han
     ReportOutOfMemory(cx);
     return false;
   }
   return true;
 }
 
 /*** SetIterator ************************************************************/
 
-static const ClassOps SetIteratorObjectClassOps = {nullptr, /* addProperty */
-                                                   nullptr, /* delProperty */
-                                                   nullptr, /* enumerate */
-                                                   nullptr, /* newEnumerate */
-                                                   nullptr, /* resolve */
-                                                   nullptr, /* mayResolve */
-                                                   SetIteratorObject::finalize};
+static const JSClassOps SetIteratorObjectClassOps = {
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    SetIteratorObject::finalize};
 
 static const ClassExtension SetIteratorObjectClassExtension = {
     SetIteratorObject::objectMoved};
 
 const Class SetIteratorObject::class_ = {
     "Set Iterator",
     JSCLASS_HAS_RESERVED_SLOTS(SetIteratorObject::SlotCount) |
         JSCLASS_FOREGROUND_FINALIZE | JSCLASS_SKIP_NURSERY_FINALIZE,
@@ -1114,27 +1116,27 @@ JSObject* SetIteratorObject::createResul
   // See comments in SetIteratorObject::next.
   AddTypePropertyId(cx, resultObj, JSID_VOID, TypeSet::UnknownType());
 
   return resultObj;
 }
 
 /*** Set ********************************************************************/
 
-const ClassOps SetObject::classOps_ = {nullptr,  // addProperty
-                                       nullptr,  // delProperty
-                                       nullptr,  // enumerate
-                                       nullptr,  // newEnumerate
-                                       nullptr,  // resolve
-                                       nullptr,  // mayResolve
-                                       finalize,
-                                       nullptr,  // call
-                                       nullptr,  // hasInstance
-                                       nullptr,  // construct
-                                       trace};
+const JSClassOps SetObject::classOps_ = {nullptr,  // addProperty
+                                         nullptr,  // delProperty
+                                         nullptr,  // enumerate
+                                         nullptr,  // newEnumerate
+                                         nullptr,  // resolve
+                                         nullptr,  // mayResolve
+                                         finalize,
+                                         nullptr,  // call
+                                         nullptr,  // hasInstance
+                                         nullptr,  // construct
+                                         trace};
 
 const ClassSpec SetObject::classSpec_ = {
     GenericCreateConstructor<SetObject::construct, 0, gc::AllocKind::FUNCTION>,
     GenericCreatePrototype<SetObject>,
     nullptr,
     SetObject::staticProperties,
     SetObject::methods,
     SetObject::properties,
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -138,17 +138,17 @@ class MapObject : public NativeObject {
   using UnbarrieredTable =
       OrderedHashMap<Value, Value, UnbarrieredHashPolicy, ZoneAllocPolicy>;
   friend class OrderedHashTableRef<MapObject>;
 
   static void sweepAfterMinorGC(JSFreeOp* fop, MapObject* mapobj);
 
  private:
   static const ClassSpec classSpec_;
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSPropertySpec staticProperties[];
   ValueMap* getData() { return static_cast<ValueMap*>(getPrivate()); }
   static ValueMap& extract(HandleObject o);
   static ValueMap& extract(const CallArgs& args);
   static void trace(JSTracer* trc, JSObject* obj);
@@ -252,17 +252,17 @@ class SetObject : public NativeObject {
   using UnbarrieredTable =
       OrderedHashSet<Value, UnbarrieredHashPolicy, ZoneAllocPolicy>;
   friend class OrderedHashTableRef<SetObject>;
 
   static void sweepAfterMinorGC(JSFreeOp* fop, SetObject* setobj);
 
  private:
   static const ClassSpec classSpec_;
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static const JSPropertySpec properties[];
   static const JSFunctionSpec methods[];
   static const JSPropertySpec staticProperties[];
 
   ValueSet* getData() { return static_cast<ValueSet*>(getPrivate()); }
   static ValueSet& extract(HandleObject o);
   static ValueSet& extract(const CallArgs& args);
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -697,17 +697,17 @@ FunctionDeclaration::FunctionDeclaration
 void FunctionDeclaration::trace(JSTracer* trc) {
   TraceEdge(trc, &name, "FunctionDeclaration name");
   TraceEdge(trc, &fun, "FunctionDeclaration fun");
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // ModuleObject
 
-/* static */ const ClassOps ModuleObject::classOps_ = {
+/* static */ const JSClassOps ModuleObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate   */
     nullptr, /* newEnumerate */
     nullptr, /* resolve     */
     nullptr, /* mayResolve  */
     ModuleObject::finalize,
     nullptr, /* call        */
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -314,17 +314,17 @@ class ModuleObject : public NativeObject
                       MutableHandleValue rval);
 
   // For intrinsic_NewModuleNamespace.
   static ModuleNamespaceObject* createNamespace(JSContext* cx,
                                                 HandleModuleObject self,
                                                 HandleObject exports);
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static void trace(JSTracer* trc, JSObject* obj);
   static void finalize(JSFreeOp* fop, JSObject* obj);
 
   bool hasImportBindings() const;
   FunctionDeclarationVector* functionDeclarations();
 };
 
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -4107,17 +4107,17 @@ bool js::AsyncFromSyncIteratorMethod(JSC
         return AbruptRejectPromise(cx, args, resultPromise, nullptr);
       }
 
       // Step 7.c: Return promiseCapability.[[Promise]].
       args.rval().setObject(*resultPromise);
       return true;
     }
   } else {
-    // throw() steps 5-7.
+    // noexcept(true) steps 5-7.
     MOZ_ASSERT(completionKind == CompletionKind::Throw);
 
     // Step 5: Let throw be GetMethod(syncIterator, "throw").
     // Step 6: IfAbruptRejectPromise(throw, promiseCapability).
     if (!GetProperty(cx, iter, iter, cx->names().throw_, &func)) {
       return AbruptRejectPromise(cx, args, resultPromise, nullptr);
     }
 
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -3655,17 +3655,17 @@ static void ReadableByteStreamController
 
   if (!controller.hasExternalSource()) {
     return;
   }
 
   controller.externalSource()->finalize();
 }
 
-static const ClassOps ReadableByteStreamControllerClassOps = {
+static const JSClassOps ReadableByteStreamControllerClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     ReadableByteStreamControllerFinalize,
     nullptr, /* call        */
--- a/js/src/builtin/String.cpp
+++ b/js/src/builtin/String.cpp
@@ -434,17 +434,17 @@ static bool str_resolve(JSContext* cx, H
                            STRING_ELEMENT_ATTRS | JSPROP_RESOLVING)) {
       return false;
     }
     *resolvedp = true;
   }
   return true;
 }
 
-static const ClassOps StringObjectClassOps = {
+static const JSClassOps StringObjectClassOps = {
     nullptr,                /* addProperty */
     nullptr,                /* delProperty */
     str_enumerate, nullptr, /* newEnumerate */
     str_resolve,   str_mayResolve};
 
 const Class StringObject::class_ = {
     js_String_str,
     JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS) |
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3147,23 +3147,24 @@ class CloneBufferObject : public NativeO
     return CallNonGenericMethod<is, getCloneBufferAsArrayBuffer_impl>(cx, args);
   }
 
   static void Finalize(JSFreeOp* fop, JSObject* obj) {
     obj->as<CloneBufferObject>().discard();
   }
 };
 
-static const ClassOps CloneBufferObjectClassOps = {nullptr, /* addProperty */
-                                                   nullptr, /* delProperty */
-                                                   nullptr, /* enumerate */
-                                                   nullptr, /* newEnumerate */
-                                                   nullptr, /* resolve */
-                                                   nullptr, /* mayResolve */
-                                                   CloneBufferObject::Finalize};
+static const JSClassOps CloneBufferObjectClassOps = {
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    CloneBufferObject::Finalize};
 
 const Class CloneBufferObject::class_ = {
     "CloneBuffer",
     JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
     &CloneBufferObjectClassOps};
 
 const JSPropertySpec CloneBufferObject::props_[] = {
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -200,24 +200,24 @@ const Class js::TypedProto::class_ = {
  * Scalar type objects
  *
  * Scalar type objects like `uint8`, `uint16`, are all instances of
  * the ScalarTypeDescr class. Like all type objects, they have a reserved
  * slot pointing to a TypeRepresentation object, which is used to
  * distinguish which scalar type object this actually is.
  */
 
-static const ClassOps ScalarTypeDescrClassOps = {nullptr, /* addProperty */
-                                                 nullptr, /* delProperty */
-                                                 nullptr, /* enumerate */
-                                                 nullptr, /* newEnumerate */
-                                                 nullptr, /* resolve */
-                                                 nullptr, /* mayResolve */
-                                                 TypeDescr::finalize,
-                                                 ScalarTypeDescr::call};
+static const JSClassOps ScalarTypeDescrClassOps = {nullptr, /* addProperty */
+                                                   nullptr, /* delProperty */
+                                                   nullptr, /* enumerate */
+                                                   nullptr, /* newEnumerate */
+                                                   nullptr, /* resolve */
+                                                   nullptr, /* mayResolve */
+                                                   TypeDescr::finalize,
+                                                   ScalarTypeDescr::call};
 
 const Class js::ScalarTypeDescr::class_ = {
     "Scalar",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     &ScalarTypeDescrClassOps};
 
 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
@@ -353,24 +353,25 @@ TypeDescr* GlobalObject::getOrCreateRefe
  *
  * Reference type objects like `Any` or `Object` basically work the
  * same way that the scalar type objects do. There is one class with
  * many instances, and each instance has a reserved slot with a
  * TypeRepresentation object, which is used to distinguish which
  * reference type object this actually is.
  */
 
-static const ClassOps ReferenceTypeDescrClassOps = {nullptr, /* addProperty */
-                                                    nullptr, /* delProperty */
-                                                    nullptr, /* enumerate */
-                                                    nullptr, /* newEnumerate */
-                                                    nullptr, /* resolve */
-                                                    nullptr, /* mayResolve */
-                                                    TypeDescr::finalize,
-                                                    ReferenceTypeDescr::call};
+static const JSClassOps ReferenceTypeDescrClassOps = {
+    nullptr, /* addProperty */
+    nullptr, /* delProperty */
+    nullptr, /* enumerate */
+    nullptr, /* newEnumerate */
+    nullptr, /* resolve */
+    nullptr, /* mayResolve */
+    TypeDescr::finalize,
+    ReferenceTypeDescr::call};
 
 const Class js::ReferenceTypeDescr::class_ = {
     "Reference",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     &ReferenceTypeDescrClassOps};
 
 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
@@ -493,26 +494,26 @@ static TypedProto* CreatePrototypeObject
   if (!ctorPrototypePrototype) {
     return nullptr;
   }
 
   return NewObjectWithGivenProto<TypedProto>(cx, ctorPrototypePrototype,
                                              SingletonObject);
 }
 
-static const ClassOps ArrayTypeDescrClassOps = {nullptr, /* addProperty */
-                                                nullptr, /* delProperty */
-                                                nullptr, /* enumerate */
-                                                nullptr, /* newEnumerate */
-                                                nullptr, /* resolve */
-                                                nullptr, /* mayResolve */
-                                                TypeDescr::finalize,
-                                                nullptr, /* call */
-                                                nullptr, /* hasInstance */
-                                                TypedObject::construct};
+static const JSClassOps ArrayTypeDescrClassOps = {nullptr, /* addProperty */
+                                                  nullptr, /* delProperty */
+                                                  nullptr, /* enumerate */
+                                                  nullptr, /* newEnumerate */
+                                                  nullptr, /* resolve */
+                                                  nullptr, /* mayResolve */
+                                                  TypeDescr::finalize,
+                                                  nullptr, /* call */
+                                                  nullptr, /* hasInstance */
+                                                  TypedObject::construct};
 
 const Class ArrayTypeDescr::class_ = {
     "ArrayType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     &ArrayTypeDescrClassOps};
 
 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {JS_PS_END};
 
@@ -742,26 +743,26 @@ bool js::IsTypedObjectArray(JSObject& ob
   TypeDescr& d = obj.as<TypedObject>().typeDescr();
   return d.is<ArrayTypeDescr>();
 }
 
 /*********************************
  * StructType class
  */
 
-static const ClassOps StructTypeDescrClassOps = {nullptr, /* addProperty */
-                                                 nullptr, /* delProperty */
-                                                 nullptr, /* enumerate */
-                                                 nullptr, /* newEnumerate */
-                                                 nullptr, /* resolve */
-                                                 nullptr, /* mayResolve */
-                                                 TypeDescr::finalize,
-                                                 StructTypeDescr::call,
-                                                 nullptr, /* hasInstance */
-                                                 TypedObject::construct};
+static const JSClassOps StructTypeDescrClassOps = {nullptr, /* addProperty */
+                                                   nullptr, /* delProperty */
+                                                   nullptr, /* enumerate */
+                                                   nullptr, /* newEnumerate */
+                                                   nullptr, /* resolve */
+                                                   nullptr, /* mayResolve */
+                                                   TypeDescr::finalize,
+                                                   StructTypeDescr::call,
+                                                   nullptr, /* hasInstance */
+                                                   TypedObject::construct};
 
 const Class StructTypeDescr::class_ = {
     "StructType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     &StructTypeDescrClassOps};
 
 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {JS_PS_END};
 
@@ -2274,17 +2275,17 @@ const ObjectOps TypedObject::objectOps_ 
     TypedObject::obj_setProperty,
     TypedObject::obj_getOwnPropertyDescriptor,
     TypedObject::obj_deleteProperty,
     nullptr, /* getElements */
     nullptr, /* thisValue */
 };
 
 #define DEFINE_TYPEDOBJ_CLASS(Name, Trace, Moved)                          \
-  static const ClassOps Name##ClassOps = {                                 \
+  static const JSClassOps Name##ClassOps = {                               \
       nullptr, /* addProperty */                                           \
       nullptr, /* delProperty */                                           \
       nullptr, /* enumerate   */                                           \
       TypedObject::obj_newEnumerate,                                       \
       nullptr, /* resolve     */                                           \
       nullptr, /* mayResolve  */                                           \
       nullptr, /* finalize    */                                           \
       nullptr, /* call        */                                           \
--- a/js/src/builtin/WeakMapObject.cpp
+++ b/js/src/builtin/WeakMapObject.cpp
@@ -250,27 +250,27 @@ bool WeakMapObject::construct(JSContext*
       return false;
     }
   }
 
   args.rval().setObject(*obj);
   return true;
 }
 
-const ClassOps WeakCollectionObject::classOps_ = {nullptr, /* addProperty */
-                                                  nullptr, /* delProperty */
-                                                  nullptr, /* enumerate */
-                                                  nullptr, /* newEnumerate */
-                                                  nullptr, /* resolve */
-                                                  nullptr, /* mayResolve */
-                                                  WeakCollection_finalize,
-                                                  nullptr, /* call */
-                                                  nullptr, /* hasInstance */
-                                                  nullptr, /* construct */
-                                                  WeakCollection_trace};
+const JSClassOps WeakCollectionObject::classOps_ = {nullptr, /* addProperty */
+                                                    nullptr, /* delProperty */
+                                                    nullptr, /* enumerate */
+                                                    nullptr, /* newEnumerate */
+                                                    nullptr, /* resolve */
+                                                    nullptr, /* mayResolve */
+                                                    WeakCollection_finalize,
+                                                    nullptr, /* call */
+                                                    nullptr, /* hasInstance */
+                                                    nullptr, /* construct */
+                                                    WeakCollection_trace};
 
 const ClassSpec WeakMapObject::classSpec_ = {
     GenericCreateConstructor<WeakMapObject::construct, 0,
                              gc::AllocKind::FUNCTION>,
     GenericCreatePrototype<WeakMapObject>,
     nullptr,
     nullptr,
     WeakMapObject::methods,
--- a/js/src/builtin/WeakMapObject.h
+++ b/js/src/builtin/WeakMapObject.h
@@ -26,17 +26,17 @@ class WeakCollectionObject : public Nati
     return map ? map->sizeOfIncludingThis(aMallocSizeOf) : 0;
   }
 
   static MOZ_MUST_USE bool nondeterministicGetKeys(
       JSContext* cx, Handle<WeakCollectionObject*> obj,
       MutableHandleObject ret);
 
  protected:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 };
 
 class WeakMapObject : public WeakCollectionObject {
  public:
   static const Class class_;
   static const Class protoClass_;
 
  private:
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -34,23 +34,23 @@ using namespace js;
 using JS::AutoStableStringChars;
 
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 using js::intl::ReportInternalError;
 using js::intl::SharedIntlData;
 using js::intl::StringsAreEqual;
 
-const ClassOps CollatorObject::classOps_ = {nullptr, /* addProperty */
-                                            nullptr, /* delProperty */
-                                            nullptr, /* enumerate */
-                                            nullptr, /* newEnumerate */
-                                            nullptr, /* resolve */
-                                            nullptr, /* mayResolve */
-                                            CollatorObject::finalize};
+const JSClassOps CollatorObject::classOps_ = {nullptr, /* addProperty */
+                                              nullptr, /* delProperty */
+                                              nullptr, /* enumerate */
+                                              nullptr, /* newEnumerate */
+                                              nullptr, /* resolve */
+                                              nullptr, /* mayResolve */
+                                              CollatorObject::finalize};
 
 const Class CollatorObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(CollatorObject::SLOT_COUNT) |
         JSCLASS_FOREGROUND_FINALIZE,
     &CollatorObject::classOps_};
 
 static bool collator_toSource(JSContext* cx, unsigned argc, Value* vp) {
--- a/js/src/builtin/intl/Collator.h
+++ b/js/src/builtin/intl/Collator.h
@@ -43,17 +43,17 @@ class CollatorObject : public NativeObje
     return static_cast<UCollator*>(slot.toPrivate());
   }
 
   void setCollator(UCollator* collator) {
     setFixedSlot(UCOLLATOR_SLOT, PrivateValue(collator));
   }
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 extern JSObject* CreateCollatorPrototype(JSContext* cx,
                                          JS::Handle<JSObject*> Intl,
                                          JS::Handle<GlobalObject*> global);
 
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -41,17 +41,17 @@ using JS::TimeClip;
 using js::intl::CallICU;
 using js::intl::DateTimeFormatOptions;
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 using js::intl::INITIAL_CHAR_BUFFER_SIZE;
 using js::intl::SharedIntlData;
 using js::intl::StringsAreEqual;
 
-const ClassOps DateTimeFormatObject::classOps_ = {
+const JSClassOps DateTimeFormatObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     DateTimeFormatObject::finalize};
 
--- a/js/src/builtin/intl/DateTimeFormat.h
+++ b/js/src/builtin/intl/DateTimeFormat.h
@@ -41,17 +41,17 @@ class DateTimeFormatObject : public Nati
     return static_cast<UDateFormat*>(slot.toPrivate());
   }
 
   void setDateFormat(UDateFormat* dateFormat) {
     setFixedSlot(UDATE_FORMAT_SLOT, PrivateValue(dateFormat));
   }
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 extern JSObject* CreateDateTimeFormatPrototype(
     JSContext* cx, JS::Handle<JSObject*> Intl, JS::Handle<GlobalObject*> global,
     JS::MutableHandle<JSObject*> constructor,
     intl::DateTimeFormatOptions dtfOptions);
--- a/js/src/builtin/intl/NumberFormat.cpp
+++ b/js/src/builtin/intl/NumberFormat.cpp
@@ -53,23 +53,23 @@ using mozilla::SpecificNaN;
 using js::intl::CallICU;
 using js::intl::DateTimeFormatOptions;
 using js::intl::FieldType;
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 
 using JS::AutoStableStringChars;
 
-const ClassOps NumberFormatObject::classOps_ = {nullptr, /* addProperty */
-                                                nullptr, /* delProperty */
-                                                nullptr, /* enumerate */
-                                                nullptr, /* newEnumerate */
-                                                nullptr, /* resolve */
-                                                nullptr, /* mayResolve */
-                                                NumberFormatObject::finalize};
+const JSClassOps NumberFormatObject::classOps_ = {nullptr, /* addProperty */
+                                                  nullptr, /* delProperty */
+                                                  nullptr, /* enumerate */
+                                                  nullptr, /* newEnumerate */
+                                                  nullptr, /* resolve */
+                                                  nullptr, /* mayResolve */
+                                                  NumberFormatObject::finalize};
 
 const Class NumberFormatObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(NumberFormatObject::SLOT_COUNT) |
         JSCLASS_FOREGROUND_FINALIZE,
     &NumberFormatObject::classOps_};
 
 static bool numberFormat_toSource(JSContext* cx, unsigned argc, Value* vp) {
--- a/js/src/builtin/intl/NumberFormat.h
+++ b/js/src/builtin/intl/NumberFormat.h
@@ -58,17 +58,17 @@ class NumberFormatObject : public Native
     return static_cast<UFormattedNumber*>(slot.toPrivate());
   }
 
   void setFormattedNumber(UFormattedNumber* formatted) {
     setFixedSlot(UFORMATTED_NUMBER_SLOT, PrivateValue(formatted));
   }
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 extern JSObject* CreateNumberFormatPrototype(JSContext* cx, HandleObject Intl,
                                              Handle<GlobalObject*> global,
                                              MutableHandleObject constructor);
 
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -29,23 +29,23 @@
 using namespace js;
 
 using mozilla::AssertedCast;
 
 using js::intl::CallICU;
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 
-const ClassOps PluralRulesObject::classOps_ = {nullptr, /* addProperty */
-                                               nullptr, /* delProperty */
-                                               nullptr, /* enumerate */
-                                               nullptr, /* newEnumerate */
-                                               nullptr, /* resolve */
-                                               nullptr, /* mayResolve */
-                                               PluralRulesObject::finalize};
+const JSClassOps PluralRulesObject::classOps_ = {nullptr, /* addProperty */
+                                                 nullptr, /* delProperty */
+                                                 nullptr, /* enumerate */
+                                                 nullptr, /* newEnumerate */
+                                                 nullptr, /* resolve */
+                                                 nullptr, /* mayResolve */
+                                                 PluralRulesObject::finalize};
 
 const Class PluralRulesObject::class_ = {
     js_Object_str,
     JSCLASS_HAS_RESERVED_SLOTS(PluralRulesObject::SLOT_COUNT) |
         JSCLASS_FOREGROUND_FINALIZE,
     &PluralRulesObject::classOps_};
 
 static bool pluralRules_toSource(JSContext* cx, unsigned argc, Value* vp) {
--- a/js/src/builtin/intl/PluralRules.h
+++ b/js/src/builtin/intl/PluralRules.h
@@ -66,17 +66,17 @@ class PluralRulesObject : public NativeO
     return static_cast<UFormattedNumber*>(slot.toPrivate());
   }
 
   void setFormattedNumber(UFormattedNumber* formatted) {
     setFixedSlot(UFORMATTED_NUMBER_SLOT, PrivateValue(formatted));
   }
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 extern JSObject* CreatePluralRulesPrototype(JSContext* cx,
                                             JS::Handle<JSObject*> Intl,
                                             JS::Handle<GlobalObject*> global);
 
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -27,17 +27,17 @@
 using namespace js;
 
 using js::intl::CallICU;
 using js::intl::GetAvailableLocales;
 using js::intl::IcuLocale;
 
 /**************** RelativeTimeFormat *****************/
 
-const ClassOps RelativeTimeFormatObject::classOps_ = {
+const JSClassOps RelativeTimeFormatObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     RelativeTimeFormatObject::finalize};
 
--- a/js/src/builtin/intl/RelativeTimeFormat.h
+++ b/js/src/builtin/intl/RelativeTimeFormat.h
@@ -39,17 +39,17 @@ class RelativeTimeFormatObject : public 
     return static_cast<URelativeDateTimeFormatter*>(slot.toPrivate());
   }
 
   void setRelativeDateTimeFormatter(URelativeDateTimeFormatter* rtf) {
     setFixedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(rtf));
   }
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 extern JSObject* CreateRelativeTimeFormatPrototype(
     JSContext* cx, JS::Handle<JSObject*> Intl,
     JS::Handle<GlobalObject*> global);
 
--- a/js/src/debugger/Debugger.cpp
+++ b/js/src/debugger/Debugger.cpp
@@ -525,17 +525,17 @@ DebuggerMemory& Debugger::memory() const
 
 static void GlobalDebuggerVectorHolder_finalize(JSFreeOp* fop, JSObject* obj) {
   MOZ_ASSERT(fop->maybeOnHelperThread());
   void* ptr = obj->as<NativeObject>().getPrivate();
   auto debuggers = static_cast<GlobalObject::DebuggerVector*>(ptr);
   fop->delete_(obj, debuggers, MemoryUse::GlobalDebuggerVector);
 }
 
-static const ClassOps GlobalDebuggerVectorHolder_classOps = {
+static const JSClassOps GlobalDebuggerVectorHolder_classOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     GlobalDebuggerVectorHolder_finalize};
 
@@ -3849,27 +3849,27 @@ bool DebuggerWeakMap<UnbarrieredKey, Wra
         !SweepZonesInSameGroup(debuggerZone, keyZone)) {
       return false;
     }
   }
 
   return true;
 }
 
-const ClassOps Debugger::classOps_ = {nullptr, /* addProperty */
-                                      nullptr, /* delProperty */
-                                      nullptr, /* enumerate   */
-                                      nullptr, /* newEnumerate */
-                                      nullptr, /* resolve     */
-                                      nullptr, /* mayResolve  */
-                                      nullptr, /* finalize    */
-                                      nullptr, /* call        */
-                                      nullptr, /* hasInstance */
-                                      nullptr, /* construct   */
-                                      Debugger::traceObject};
+const JSClassOps Debugger::classOps_ = {nullptr, /* addProperty */
+                                        nullptr, /* delProperty */
+                                        nullptr, /* enumerate   */
+                                        nullptr, /* newEnumerate */
+                                        nullptr, /* resolve     */
+                                        nullptr, /* mayResolve  */
+                                        nullptr, /* finalize    */
+                                        nullptr, /* call        */
+                                        nullptr, /* hasInstance */
+                                        nullptr, /* construct   */
+                                        Debugger::traceObject};
 
 const Class Debugger::class_ = {
     "Debugger",
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
     &Debugger::classOps_};
 
 static Debugger* Debugger_fromThisValue(JSContext* cx, const CallArgs& args,
                                         const char* fnname) {
--- a/js/src/debugger/Debugger.h
+++ b/js/src/debugger/Debugger.h
@@ -829,17 +829,17 @@ class Debugger : private mozilla::Linked
   static void traceObject(JSTracer* trc, JSObject* obj);
 
   void trace(JSTracer* trc);
   friend struct js::GCManagedDeletePolicy<Debugger>;
 
   void traceForMovingGC(JSTracer* trc);
   void traceCrossCompartmentEdges(JSTracer* tracer);
 
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
  public:
   static const Class class_;
 
  private:
   static MOZ_MUST_USE bool getHookImpl(JSContext* cx, CallArgs& args,
                                        Debugger& dbg, Hook which);
   static MOZ_MUST_USE bool setHookImpl(JSContext* cx, CallArgs& args,
--- a/js/src/debugger/Environment.cpp
+++ b/js/src/debugger/Environment.cpp
@@ -46,17 +46,17 @@ class GlobalObject;
 
 using namespace js;
 
 using js::frontend::IsIdentifier;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::Some;
 
-const ClassOps DebuggerEnvironment::classOps_ = {
+const JSClassOps DebuggerEnvironment::classOps_ = {
     nullptr,                              /* addProperty */
     nullptr,                              /* delProperty */
     nullptr,                              /* enumerate   */
     nullptr,                              /* newEnumerate */
     nullptr,                              /* resolve     */
     nullptr,                              /* mayResolve  */
     nullptr,                              /* finalize    */
     nullptr,                              /* call        */
--- a/js/src/debugger/Environment.h
+++ b/js/src/debugger/Environment.h
@@ -22,17 +22,16 @@
 class JSObject;
 class JSTracer;
 struct JSContext;
 
 namespace js {
 
 class GlobalObject;
 struct Class;
-struct ClassOps;
 
 enum class DebuggerEnvironmentType { Declarative, With, Object };
 
 class DebuggerEnvironment : public NativeObject {
  public:
   enum { OWNER_SLOT };
 
   static const unsigned RESERVED_SLOTS = 1;
@@ -68,17 +67,17 @@ class DebuggerEnvironment : public Nativ
   static MOZ_MUST_USE bool getVariable(JSContext* cx,
                                        HandleDebuggerEnvironment environment,
                                        HandleId id, MutableHandleValue result);
   static MOZ_MUST_USE bool setVariable(JSContext* cx,
                                        HandleDebuggerEnvironment environment,
                                        HandleId id, HandleValue value);
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static const JSPropertySpec properties_[];
   static const JSFunctionSpec methods_[];
 
   Env* referent() const {
     Env* env = static_cast<Env*>(getPrivate());
     MOZ_ASSERT(env);
     return env;
--- a/js/src/debugger/Frame.cpp
+++ b/js/src/debugger/Frame.cpp
@@ -167,17 +167,17 @@ bool ScriptedOnPopHandler::onPop(JSConte
 
 size_t ScriptedOnPopHandler::allocSize() const { return sizeof(*this); }
 
 inline js::Debugger* js::DebuggerFrame::owner() const {
   JSObject* dbgobj = &getReservedSlot(OWNER_SLOT).toObject();
   return Debugger::fromJSObject(dbgobj);
 }
 
-const ClassOps DebuggerFrame::classOps_ = {
+const JSClassOps DebuggerFrame::classOps_ = {
     nullptr,                        /* addProperty */
     nullptr,                        /* delProperty */
     nullptr,                        /* enumerate   */
     nullptr,                        /* newEnumerate */
     nullptr,                        /* resolve     */
     nullptr,                        /* mayResolve  */
     finalize,                       /* finalize */
     nullptr,                        /* call        */
--- a/js/src/debugger/Frame.h
+++ b/js/src/debugger/Frame.h
@@ -245,17 +245,17 @@ class DebuggerFrame : public NativeObjec
    * Called after a generator/async frame is resumed, before exposing this
    * Debugger.Frame object to any hooks.
    */
   bool resume(const FrameIter& iter);
 
   bool hasAnyLiveHooks() const;
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static const JSPropertySpec properties_[];
   static const JSFunctionSpec methods_[];
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 
   static AbstractFramePtr getReferent(HandleDebuggerFrame frame);
   static MOZ_MUST_USE bool getFrameIter(JSContext* cx,
--- a/js/src/debugger/Object.cpp
+++ b/js/src/debugger/Object.cpp
@@ -66,17 +66,17 @@
 
 using namespace js;
 
 using JS::AutoStableStringChars;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::Some;
 
-const ClassOps DebuggerObject::classOps_ = {
+const JSClassOps DebuggerObject::classOps_ = {
     nullptr,                         /* addProperty */
     nullptr,                         /* delProperty */
     nullptr,                         /* enumerate   */
     nullptr,                         /* newEnumerate */
     nullptr,                         /* resolve     */
     nullptr,                         /* mayResolve  */
     nullptr,                         /* finalize    */
     nullptr,                         /* call        */
--- a/js/src/debugger/Object.h
+++ b/js/src/debugger/Object.h
@@ -169,17 +169,17 @@ class DebuggerObject : public NativeObje
   double promiseLifetime() const;
   double promiseTimeToResolution() const;
 
  private:
   enum { OWNER_SLOT };
 
   static const unsigned RESERVED_SLOTS = 1;
 
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static const JSPropertySpec properties_[];
   static const JSPropertySpec promiseProperties_[];
   static const JSFunctionSpec methods_[];
 
   JSObject* referent() const {
     JSObject* obj = (JSObject*)getPrivate();
     MOZ_ASSERT(obj);
--- a/js/src/debugger/Script.cpp
+++ b/js/src/debugger/Script.cpp
@@ -52,17 +52,17 @@
 #include "vm/ObjectOperations-inl.h"  // for GetProperty
 #include "vm/Realm-inl.h"             // for AutoRealm::AutoRealm
 
 using namespace js;
 
 using mozilla::Maybe;
 using mozilla::Some;
 
-const ClassOps DebuggerScript::classOps_ = {
+const JSClassOps DebuggerScript::classOps_ = {
     nullptr,                         /* addProperty */
     nullptr,                         /* delProperty */
     nullptr,                         /* enumerate   */
     nullptr,                         /* newEnumerate */
     nullptr,                         /* resolve     */
     nullptr,                         /* mayResolve  */
     nullptr,                         /* finalize    */
     nullptr,                         /* call        */
--- a/js/src/debugger/Script.h
+++ b/js/src/debugger/Script.h
@@ -99,17 +99,17 @@ class DebuggerScript : public NativeObje
                                                Value* vp, const char* name,
                                                bool successor);
 
   Value getInstrumentationId() const {
     return getSlot(INSTRUMENTATION_ID_SLOT);
   }
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static const JSPropertySpec properties_[];
   static const JSFunctionSpec methods_[];
 
   class SetPrivateMatcher;
   struct GetStartLineMatcher;
   struct GetStartColumnMatcher;
   struct GetLineCountMatcher;
--- a/js/src/debugger/Source.cpp
+++ b/js/src/debugger/Source.cpp
@@ -44,17 +44,17 @@ class GlobalObject;
 using namespace js;
 
 using JS::AutoStableStringChars;
 using mozilla::AsVariant;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::Some;
 
-const ClassOps DebuggerSource::classOps_ = {
+const JSClassOps DebuggerSource::classOps_ = {
     nullptr,                         /* addProperty */
     nullptr,                         /* delProperty */
     nullptr,                         /* enumerate   */
     nullptr,                         /* newEnumerate */
     nullptr,                         /* resolve     */
     nullptr,                         /* mayResolve  */
     nullptr,                         /* finalize    */
     nullptr,                         /* call        */
--- a/js/src/debugger/Source.h
+++ b/js/src/debugger/Source.h
@@ -60,17 +60,17 @@ class DebuggerSource : public NativeObje
   static bool getElementProperty(JSContext* cx, unsigned argc, Value* vp);
   static bool getIntroductionScript(JSContext* cx, unsigned argc, Value* vp);
   static bool getIntroductionOffset(JSContext* cx, unsigned argc, Value* vp);
   static bool getIntroductionType(JSContext* cx, unsigned argc, Value* vp);
   static bool setSourceMapURL(JSContext* cx, unsigned argc, Value* vp);
   static bool getSourceMapURL(JSContext* cx, unsigned argc, Value* vp);
 
  private:
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static const JSPropertySpec properties_[];
   static const JSFunctionSpec methods_[];
 };
 
 } /* namespace js */
 
 #endif /* dbg_Source_h */
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -87,18 +87,18 @@ var ignoreClasses = {
     "malloc_hook_table_t": true, // replace_malloc
 };
 
 // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
 // a function pointer field named FIELD.
 var ignoreCallees = {
     "js::Class.trace" : true,
     "js::Class.finalize" : true,
-    "js::ClassOps.trace" : true,
-    "js::ClassOps.finalize" : true,
+    "JSClassOps.trace" : true,
+    "JSClassOps.finalize" : true,
     "JSRuntime.destroyPrincipals" : true,
     "icu_50::UObject.__deleting_dtor" : true, // destructors in ICU code can't cause GC
     "mozilla::CycleCollectedJSRuntime.DescribeCustomObjects" : true, // During tracing, cannot GC.
     "mozilla::CycleCollectedJSRuntime.NoteCustomGCThingXPCOMChildren" : true, // During tracing, cannot GC.
     "PLDHashTableOps.hashKey" : true,
     "z_stream_s.zfree" : true,
     "z_stream_s.zalloc" : true,
     "GrGLInterface.fCallback" : true,
--- a/js/src/gc/ArenaList-inl.h
+++ b/js/src/gc/ArenaList-inl.h
@@ -307,21 +307,17 @@ void js::gc::ArenaLists::unmarkPreMarked
     freeLists().unmarkPreMarkedFreeCells(i);
   }
 }
 
 void js::gc::ArenaLists::checkEmptyFreeLists() {
   MOZ_ASSERT(freeLists().allEmpty());
 }
 
-bool js::gc::ArenaLists::checkEmptyArenaLists() {
-  bool empty = true;
+void js::gc::ArenaLists::checkEmptyArenaLists() {
 #ifdef DEBUG
   for (auto i : AllAllocKinds()) {
-    if (!checkEmptyArenaList(i)) {
-      empty = false;
-    }
+    checkEmptyArenaList(i);
   }
 #endif
-  return empty;
 }
 
 #endif  // gc_ArenaList_inl_h
--- a/js/src/gc/ArenaList.h
+++ b/js/src/gc/ArenaList.h
@@ -324,20 +324,20 @@ class ArenaLists {
   inline void unmarkPreMarkedFreeCells();
 
   MOZ_ALWAYS_INLINE TenuredCell* allocateFromFreeList(AllocKind thingKind);
 
   /* Moves all arenas from |fromArenaLists| into |this|. */
   void adoptArenas(ArenaLists* fromArenaLists, bool targetZoneIsCollecting);
 
   inline void checkEmptyFreeLists();
-  inline bool checkEmptyArenaLists();
+  inline void checkEmptyArenaLists();
   inline void checkEmptyFreeList(AllocKind kind);
 
-  bool checkEmptyArenaList(AllocKind kind);
+  void checkEmptyArenaList(AllocKind kind);
 
   bool relocateArenas(Arena*& relocatedListOut, JS::GCReason reason,
                       js::SliceBudget& sliceBudget, gcstats::Statistics& stats);
 
   void queueForegroundObjectsForSweep(JSFreeOp* fop);
   void queueForegroundThingsForSweep();
 
   void releaseForegroundSweptEmptyArenas();
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -1019,19 +1019,16 @@ GCRuntime::GCRuntime(JSRuntime* rt)
       nextScheduled(0),
       deterministicOnly(false),
       incrementalLimit(0),
 #endif
       fullCompartmentChecks(false),
       gcCallbackDepth(0),
       alwaysPreserveCode(false),
       lowMemoryState(false),
-#ifdef DEBUG
-      arenasEmptyAtShutdown(true),
-#endif
       lock(mutexid::GCLock),
       allocTask(rt, emptyChunks_.ref()),
       sweepTask(rt),
       freeTask(rt),
       decommitTask(rt),
       nursery_(rt),
       storeBuffer_(rt, nursery()) {
   setGCMode(JSGC_MODE_GLOBAL);
@@ -4018,17 +4015,16 @@ void GCRuntime::deleteEmptyZone(Zone* zo
       return;
     }
   }
   MOZ_CRASH("Zone not found");
 }
 
 void GCRuntime::sweepZones(JSFreeOp* fop, bool destroyingRuntime) {
   MOZ_ASSERT_IF(destroyingRuntime, numActiveZoneIters == 0);
-  MOZ_ASSERT_IF(destroyingRuntime, arenasEmptyAtShutdown);
 
   if (numActiveZoneIters) {
     return;
   }
 
   assertBackgroundSweepingFinished();
 
   Zone** read = zones().begin();
@@ -4037,93 +4033,34 @@ void GCRuntime::sweepZones(JSFreeOp* fop
 
   while (read < end) {
     Zone* zone = *read++;
 
     if (zone->wasGCStarted()) {
       MOZ_ASSERT(!zone->isQueuedForBackgroundSweep());
       const bool zoneIsDead =
           zone->arenas.arenaListsAreEmpty() && !zone->hasMarkedRealms();
-      if (zoneIsDead || destroyingRuntime) {
-        {
-          // We have just finished sweeping, so we should have freed any
-          // empty arenas back to their Chunk for future allocation.
-          zone->arenas.checkEmptyFreeLists();
-        }
-
-        // We are about to delete the Zone; this will leave the Zone*
-        // in the arena header dangling if there are any arenas
-        // remaining at this point.
-#ifdef DEBUG
-        if (!zone->arenas.checkEmptyArenaLists()) {
-          arenasEmptyAtShutdown = false;
-        }
-#endif
-
+      MOZ_ASSERT_IF(destroyingRuntime, zoneIsDead);
+      if (zoneIsDead) {
+        zone->arenas.checkEmptyFreeLists();
         zone->sweepCompartments(fop, false, destroyingRuntime);
         MOZ_ASSERT(zone->compartments().empty());
-        MOZ_ASSERT_IF(arenasEmptyAtShutdown, zone->typeDescrObjects().empty());
+        MOZ_ASSERT(zone->typeDescrObjects().empty());
         zone->destroy(fop);
         continue;
       }
       zone->sweepCompartments(fop, true, destroyingRuntime);
     }
     *write++ = zone;
   }
   zones().shrinkTo(write - zones().begin());
 }
 
-#ifdef DEBUG
-static const char* AllocKindToAscii(AllocKind kind) {
-  switch (kind) {
-#  define MAKE_CASE(allocKind, traceKind, type, sizedType, bgFinal, nursery, \
-                    compact)                                                 \
-    case AllocKind::allocKind:                                               \
-      return #allocKind;
-    FOR_EACH_ALLOCKIND(MAKE_CASE)
-#  undef MAKE_CASE
-
-    default:
-      MOZ_CRASH("Unknown AllocKind in AllocKindToAscii");
-  }
-}
-#endif  // DEBUG
-
-bool ArenaLists::checkEmptyArenaList(AllocKind kind) {
-  bool isEmpty = true;
-#ifdef DEBUG
-  size_t numLive = 0;
-  if (!arenaLists(kind).isEmpty()) {
-    isEmpty = false;
-    size_t maxCells = 5;
-    char* env = getenv("JS_GC_MAX_LIVE_CELLS");
-    if (env && *env) {
-      maxCells = atol(env);
-    }
-    for (Arena* current = arenaLists(kind).head(); current;
-         current = current->next) {
-      for (ArenaCellIterUnderGC i(current); !i.done(); i.next()) {
-        TenuredCell* t = i.getCell();
-        MOZ_ASSERT(t->isMarkedAny(),
-                   "unmarked cells should have been finalized");
-        if (++numLive <= maxCells) {
-          fprintf(stderr,
-                  "ERROR: GC found live Cell %p of kind %s at shutdown\n", t,
-                  AllocKindToAscii(kind));
-        }
-      }
-    }
-    if (numLive > 0) {
-      fprintf(stderr, "ERROR: GC found %zu live Cells at shutdown\n", numLive);
-    } else {
-      fprintf(stderr, "ERROR: GC found empty Arenas at shutdown\n");
-    }
-  }
-#endif  // DEBUG
-  return isEmpty;
+void ArenaLists::checkEmptyArenaList(AllocKind kind) {
+  MOZ_ASSERT(arenaLists(kind).isEmpty());
 }
 
 class MOZ_RAII js::gc::AutoRunParallelTask : public GCParallelTask {
   gcstats::PhaseKind phase_;
   AutoLockHelperThreadState& lock_;
 
  public:
   AutoRunParallelTask(JSRuntime* rt, TaskFunc func, gcstats::PhaseKind phase,
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -320,20 +320,16 @@ class GCRuntime {
     MOZ_ASSERT(nextCellUniqueId_ > 0);
     uint64_t uid = ++nextCellUniqueId_;
     return uid;
   }
 
   void setLowMemoryState(bool newState) { lowMemoryState = newState; }
   bool systemHasLowMemory() const { return lowMemoryState; }
 
-#ifdef DEBUG
-  bool shutdownCollectedEverything() const { return arenasEmptyAtShutdown; }
-#endif
-
  public:
   // Internal public interface
   State state() const { return incrementalState; }
   bool isHeapCompacting() const { return state() == State::Compact; }
   bool isForegroundSweeping() const { return state() == State::Sweep; }
   bool isBackgroundSweeping() const { return sweepTask.isRunning(); }
   void waitBackgroundSweepEnd();
   void waitBackgroundAllocEnd() { allocTask.cancelAndWait(); }
@@ -1007,20 +1003,16 @@ class GCRuntime {
   CallbackVector<JSTraceDataOp> blackRootTracers;
   Callback<JSTraceDataOp> grayRootTracer;
 
   /* Always preserve JIT code during GCs, for testing. */
   MainThreadData<bool> alwaysPreserveCode;
 
   MainThreadData<bool> lowMemoryState;
 
-#ifdef DEBUG
-  MainThreadData<bool> arenasEmptyAtShutdown;
-#endif
-
   /* Synchronize GC heap access among GC helper threads and the main thread. */
   friend class js::AutoLockGC;
   friend class js::AutoLockGCBgAlloc;
   js::Mutex lock;
 
   friend class BackgroundSweepTask;
   friend class BackgroundFreeTask;
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -36,22 +36,20 @@ ZoneAllocator::ZoneAllocator(JSRuntime* 
       gcJitBytes(nullptr),
       gcJitThreshold(jit::MaxCodeBytesPerProcess * 0.8) {
   AutoLockGC lock(rt);
   updateGCThresholds(rt->gc, GC_NORMAL, lock);
 }
 
 ZoneAllocator::~ZoneAllocator() {
 #ifdef DEBUG
-  if (runtimeFromAnyThread()->gc.shutdownCollectedEverything()) {
-    gcMallocTracker.checkEmptyOnDestroy();
-    MOZ_ASSERT(zoneSize.gcBytes() == 0);
-    MOZ_ASSERT(gcMallocBytes.gcBytes() == 0);
-    MOZ_ASSERT(gcJitBytes.gcBytes() == 0);
-  }
+  gcMallocTracker.checkEmptyOnDestroy();
+  MOZ_ASSERT(zoneSize.gcBytes() == 0);
+  MOZ_ASSERT(gcMallocBytes.gcBytes() == 0);
+  MOZ_ASSERT(gcJitBytes.gcBytes() == 0);
 #endif
 }
 
 void ZoneAllocator::fixupAfterMovingGC() {
 #ifdef DEBUG
   gcMallocTracker.fixupAfterMovingGC();
 #endif
 }
@@ -128,33 +126,26 @@ JS::Zone::Zone(JSRuntime* rt)
       listNext_(NotOnList) {
   /* Ensure that there are no vtables to mess us up here. */
   MOZ_ASSERT(reinterpret_cast<JS::shadow::Zone*>(this) ==
              static_cast<JS::shadow::Zone*>(this));
 }
 
 Zone::~Zone() {
   MOZ_ASSERT(helperThreadUse_ == HelperThreadUse::None);
+  MOZ_ASSERT(gcWeakMapList().isEmpty());
+  MOZ_ASSERT_IF(regExps_.ref(), regExps().empty());
 
   JSRuntime* rt = runtimeFromAnyThread();
   if (this == rt->gc.systemZone) {
     rt->gc.systemZone = nullptr;
   }
 
   js_delete(debuggers.ref());
   js_delete(jitZone_.ref());
-
-#ifdef DEBUG
-  // Avoid assertions failures warning that not everything has been destroyed
-  // if the embedding leaked GC things.
-  if (!rt->gc.shutdownCollectedEverything()) {
-    gcWeakMapList().clear();
-    regExps().clear();
-  }
-#endif
 }
 
 bool Zone::init(bool isSystemArg) {
   isSystem = isSystemArg;
   regExps_.ref() = make_unique<RegExpZone>(this);
   return regExps_.ref() && gcWeakKeys().init() && gcNurseryWeakKeys().init();
 }
 
@@ -528,17 +519,17 @@ bool Zone::addTypeDescrObject(JSContext*
     return false;
   }
 
   return true;
 }
 
 void Zone::deleteEmptyCompartment(JS::Compartment* comp) {
   MOZ_ASSERT(comp->zone() == this);
-  MOZ_ASSERT(arenas.checkEmptyArenaLists());
+  arenas.checkEmptyArenaLists();
 
   MOZ_ASSERT(compartments().length() == 1);
   MOZ_ASSERT(compartments()[0] == comp);
   MOZ_ASSERT(comp->realms().length() == 1);
 
   Realm* realm = comp->realms()[0];
   JSFreeOp* fop = runtimeFromMainThread()->defaultFreeOp();
   realm->destroy(fop);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -12742,18 +12742,18 @@ void CodeGenerator::emitIsCallableOrCons
 
   masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
                  ImmPtr(nullptr), &hasCOps);
   masm.move32(Imm32(0), output);
   masm.jump(&done);
 
   masm.bind(&hasCOps);
   masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
-  size_t opsOffset = mode == Callable ? offsetof(js::ClassOps, call)
-                                      : offsetof(js::ClassOps, construct);
+  size_t opsOffset = mode == Callable ? offsetof(JSClassOps, call)
+                                      : offsetof(JSClassOps, construct);
   masm.cmpPtrSet(Assembler::NonZero, Address(output, opsOffset),
                  ImmPtr(nullptr), output);
 
   masm.bind(&done);
 }
 
 void CodeGenerator::visitIsCallableO(LIsCallableO* ins) {
   Register object = ToRegister(ins->object());
--- a/js/src/jit/ExecutableAllocator.cpp
+++ b/js/src/jit/ExecutableAllocator.cpp
@@ -86,18 +86,17 @@ size_t ExecutablePool::available() const
 }
 
 ExecutableAllocator::~ExecutableAllocator() {
   for (size_t i = 0; i < m_smallPools.length(); i++) {
     m_smallPools[i]->release(/* willDestroy = */ true);
   }
 
   // If this asserts we have a pool leak.
-  MOZ_ASSERT_IF(TlsContext.get()->runtime()->gc.shutdownCollectedEverything(),
-                m_pools.empty());
+  MOZ_ASSERT(m_pools.empty());
 }
 
 ExecutablePool* ExecutableAllocator::poolForSize(size_t n) {
   // Try to fit in an existing small allocator.  Use the pool with the
   // least available space that is big enough (best-fit).  This is the
   // best strategy because (a) it maximizes the chance of the next
   // allocation fitting in a small pool, and (b) it minimizes the
   // potential waste when a small pool is next abandoned.
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -4367,19 +4367,19 @@ MCompare* jit::ConvertLinearInequality(T
 
   MCompare* compare = MCompare::New(alloc, lhsDef, rhsDef, op);
   block->insertAtEnd(compare);
   compare->setCompareType(MCompare::Compare_Int32);
 
   return compare;
 }
 
-static bool AnalyzePoppedThis(JSContext* cx, ObjectGroup* group,
-                              MDefinition* thisValue, MInstruction* ins,
-                              bool definitelyExecuted,
+static bool AnalyzePoppedThis(JSContext* cx, DPAConstraintInfo& constraintInfo,
+                              ObjectGroup* group, MDefinition* thisValue,
+                              MInstruction* ins, bool definitelyExecuted,
                               HandlePlainObject baseobj,
                               Vector<TypeNewScriptInitializer>* initializerList,
                               Vector<PropertyName*>* accessedProperties,
                               bool* phandled) {
   // Determine the effect that a use of the |this| value when calling |new|
   // on a script has on the properties definitely held by the new object.
 
   if (ins->isCallSetProperty()) {
@@ -4410,17 +4410,22 @@ static bool AnalyzePoppedThis(JSContext*
     }
 
     // Assignments to new properties must always execute.
     if (!definitelyExecuted) {
       return true;
     }
 
     RootedId id(cx, NameToId(setprop->name()));
-    if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) {
+    bool added = false;
+    if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo,
+                                                       group, id, &added)) {
+      return false;
+    }
+    if (!added) {
       // The prototype chain already contains a getter/setter for this
       // property, or type information is too imprecise.
       return true;
     }
 
     // Add the property to the object, being careful not to update type
     // information.
     DebugOnly<unsigned> slotSpan = baseobj->slotSpan();
@@ -4476,17 +4481,22 @@ static bool AnalyzePoppedThis(JSContext*
      *   added to the object at the point of its creation, reading a
      *   definite property before it is assigned could incorrectly hit.
      */
     RootedId id(cx, NameToId(get->name()));
     if (!baseobj->lookup(cx, id) && !accessedProperties->append(get->name())) {
       return false;
     }
 
-    if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, group, id)) {
+    bool added = false;
+    if (!AddClearDefiniteGetterSetterForPrototypeChain(cx, constraintInfo,
+                                                       group, id, &added)) {
+      return false;
+    }
+    if (!added) {
       // The |this| value can escape if any property reads it does go
       // through a getter.
       return true;
     }
 
     *phandled = true;
     return true;
   }
@@ -4500,18 +4510,18 @@ static bool AnalyzePoppedThis(JSContext*
 }
 
 static int CmpInstructions(const void* a, const void* b) {
   return (*static_cast<MInstruction* const*>(a))->id() -
          (*static_cast<MInstruction* const*>(b))->id();
 }
 
 bool jit::AnalyzeNewScriptDefiniteProperties(
-    JSContext* cx, HandleFunction fun, ObjectGroup* group,
-    HandlePlainObject baseobj,
+    JSContext* cx, DPAConstraintInfo& constraintInfo, HandleFunction fun,
+    ObjectGroup* group, HandlePlainObject baseobj,
     Vector<TypeNewScriptInitializer>* initializerList) {
   MOZ_ASSERT(cx->zone()->types.activeAnalysis);
 
   // When invoking 'new' on the specified script, try to find some properties
   // which will definitely be added to the created object before it has a
   // chance to escape and be accessed elsewhere.
 
   RootedScript script(cx, JSFunction::getOrCreateScript(cx, fun));
@@ -4671,19 +4681,19 @@ bool jit::AnalyzeNewScriptDefiniteProper
     // times then we can get confused when rolling back objects while
     // clearing the new script information.
     if (ins->block()->loopDepth() != 0) {
       definitelyExecuted = false;
     }
 
     bool handled = false;
     size_t slotSpan = baseobj->slotSpan();
-    if (!AnalyzePoppedThis(cx, group, thisValue, ins, definitelyExecuted,
-                           baseobj, initializerList, &accessedProperties,
-                           &handled)) {
+    if (!AnalyzePoppedThis(cx, constraintInfo, group, thisValue, ins,
+                           definitelyExecuted, baseobj, initializerList,
+                           &accessedProperties, &handled)) {
       return false;
     }
     if (!handled) {
       break;
     }
 
     if (slotSpan != baseobj->slotSpan()) {
       MOZ_ASSERT(ins->block()->id() >= lastAddedBlock);
@@ -4691,30 +4701,29 @@ bool jit::AnalyzeNewScriptDefiniteProper
     }
   }
 
   if (baseobj->slotSpan() != 0) {
     // We found some definite properties, but their correctness is still
     // contingent on the correct frames being inlined. Add constraints to
     // invalidate the definite properties if additional functions could be
     // called at the inline frame sites.
-    Vector<MBasicBlock*> exitBlocks(cx);
     for (MBasicBlockIterator block(graph.begin()); block != graph.end();
          block++) {
       // Inlining decisions made after the last new property was added to
       // the object don't need to be frozen.
       if (block->id() > lastAddedBlock) {
         break;
       }
       if (MResumePoint* rp = block->callerResumePoint()) {
         if (block->numPredecessors() == 1 &&
             block->getPredecessor(0) == rp->block()) {
-          JSScript* script = rp->block()->info().script();
-          if (!AddClearDefiniteFunctionUsesInScript(cx, group, script,
-                                                    block->info().script())) {
+          JSScript* caller = rp->block()->info().script();
+          JSScript* callee = block->info().script();
+          if (!constraintInfo.addInliningConstraint(caller, callee)) {
             return false;
           }
         }
       }
     }
   }
 
   return true;
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -158,18 +158,18 @@ MDefinition* ConvertLinearSum(TempAlloca
                               bool convertConstant = false);
 
 // Convert the test 'sum >= 0' to a comparison, adding any necessary
 // instructions to the end of block.
 MCompare* ConvertLinearInequality(TempAllocator& alloc, MBasicBlock* block,
                                   const LinearSum& sum);
 
 MOZ_MUST_USE bool AnalyzeNewScriptDefiniteProperties(
-    JSContext* cx, HandleFunction fun, ObjectGroup* group,
-    HandlePlainObject baseobj,
+    JSContext* cx, DPAConstraintInfo& constraintInfo, HandleFunction fun,
+    ObjectGroup* group, HandlePlainObject baseobj,
     Vector<TypeNewScriptInitializer>* initializerList);
 
 MOZ_MUST_USE bool AnalyzeArgumentsUsage(JSContext* cx, JSScript* script);
 
 bool DeadIfUnused(const MDefinition* def);
 
 bool IsDiscardable(const MDefinition* def);
 
--- a/js/src/jit/JitAllocPolicy.h
+++ b/js/src/jit/JitAllocPolicy.h
@@ -138,17 +138,17 @@ class AutoJitContextAlloc {
   ~AutoJitContextAlloc() {
     MOZ_ASSERT(jcx_->temp == &tempAlloc_);
     jcx_->temp = prevAlloc_;
   }
 };
 
 struct TempObject {
   inline void* operator new(size_t nbytes,
-                            TempAllocator::Fallible view) throw() {
+                            TempAllocator::Fallible view) noexcept(true) {
     return view.alloc.allocate(nbytes);
   }
   inline void* operator new(size_t nbytes, TempAllocator& alloc) {
     return alloc.allocateInfallible(nbytes);
   }
   template <class T>
   inline void* operator new(size_t nbytes, T* pos) {
     static_assert(mozilla::IsConvertible<T*, TempObject*>::value,
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1014,17 +1014,17 @@ class MInstruction : public MDefinition,
 
  protected:
   // All MInstructions are using the "MFoo::New(alloc)" notation instead of
   // the TempObject new operator. This code redefines the new operator as
   // protected, and delegates to the TempObject new operator. Thus, the
   // following code prevents calls to "new(alloc) MFoo" outside the MFoo
   // members.
   inline void* operator new(size_t nbytes,
-                            TempAllocator::Fallible view) throw() {
+                            TempAllocator::Fallible view) noexcept(true) {
     return TempObject::operator new(nbytes, view);
   }
   inline void* operator new(size_t nbytes, TempAllocator& alloc) {
     return TempObject::operator new(nbytes, alloc);
   }
   template <class T>
   inline void* operator new(size_t nbytes, T* pos) {
     return TempObject::operator new(nbytes, pos);
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1433,17 +1433,17 @@ void MacroAssembler::typeOfObject(Regist
   branchTest32(Assembler::NonZero, flags, Imm32(JSCLASS_EMULATES_UNDEFINED),
                isUndefined);
 
   // Handle classes with a call hook.
   branchPtr(Assembler::Equal, Address(scratch, offsetof(js::Class, cOps)),
             ImmPtr(nullptr), isObject);
 
   loadPtr(Address(scratch, offsetof(js::Class, cOps)), scratch);
-  branchPtr(Assembler::Equal, Address(scratch, offsetof(js::ClassOps, call)),
+  branchPtr(Assembler::Equal, Address(scratch, offsetof(JSClassOps, call)),
             ImmPtr(nullptr), isObject);
 
   jump(isCallable);
 }
 
 void MacroAssembler::loadJSContext(Register dest) {
   JitContext* jcx = GetJitContext();
   movePtr(ImmPtr(jcx->runtime->mainContextPtr()), dest);
--- a/js/src/jsapi-tests/testWeakMap.cpp
+++ b/js/src/jsapi-tests/testWeakMap.cpp
@@ -187,17 +187,17 @@ JSObject* newCCW(JS::HandleObject source
   // In order to test the SCC algorithm, we need the wrapper/wrappee to be
   // tenured.
   cx->runtime()->gc.evictNursery();
 
   return object;
 }
 
 JSObject* newDelegate() {
-  static const js::ClassOps delegateClassOps = {
+  static const JSClassOps delegateClassOps = {
       nullptr, /* addProperty */
       nullptr, /* delProperty */
       nullptr, /* enumerate */
       nullptr, /* newEnumerate */
       nullptr, /* resolve */
       nullptr, /* mayResolve */
       nullptr, /* finalize */
       nullptr, /* call */
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -136,17 +136,17 @@ const ClassSpec ErrorObject::classSpecs[
     js_Error_str, /* yes, really */                                   \
         JSCLASS_HAS_CACHED_PROTO(JSProto_##name) |                    \
             JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS) | \
             JSCLASS_BACKGROUND_FINALIZE,                              \
         &ErrorObjectClassOps,                                         \
         &ErrorObject::classSpecs[JSProto_##name - JSProto_Error]      \
   }
 
-static const ClassOps ErrorObjectClassOps = {
+static const JSClassOps ErrorObjectClassOps = {
     nullptr,               /* addProperty */
     nullptr,               /* delProperty */
     nullptr,               /* enumerate */
     nullptr,               /* newEnumerate */
     nullptr,               /* resolve */
     nullptr,               /* mayResolve */
     exn_finalize, nullptr, /* call        */
     nullptr,               /* hasInstance */
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -1428,17 +1428,16 @@ AC_CACHE_CHECK(for tm_zone tm_gmtoff in 
                     [struct tm tm; tm.tm_zone = 0; tm.tm_gmtoff = 1;],
                     [ac_cv_struct_tm_zone_tm_gmtoff="yes"],
                     [ac_cv_struct_tm_zone_tm_gmtoff="no"])])
 if test "$ac_cv_struct_tm_zone_tm_gmtoff" = "yes" ; then
     AC_DEFINE(HAVE_TM_ZONE_TM_GMTOFF)
 fi
 fi # ! SKIP_COMPILER_CHECKS
 
-AC_DEFINE(CPP_THROW_NEW, [throw()])
 AC_LANG_C
 
 MOZ_EXPAND_LIBS
 
 dnl ========================================================
 dnl = Link js shell to system readline
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(readline,
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -737,17 +737,17 @@ size_t js::proxy_ObjectMoved(JSObject* o
     // inline ProxyValueArray.
     MOZ_ASSERT(old->as<ProxyObject>().usingInlineValueArray());
     proxy.setInlineValueArray();
   }
 
   return proxy.handler()->objectMoved(obj, old);
 }
 
-const ClassOps js::ProxyClassOps = {
+const JSClassOps js::ProxyClassOps = {
     nullptr,            /* addProperty */
     nullptr,            /* delProperty */
     nullptr,            /* enumerate   */
     nullptr,            /* newEnumerate */
     nullptr,            /* resolve     */
     nullptr,            /* mayResolve  */
     proxy_Finalize,     /* finalize    */
     nullptr,            /* call        */
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -451,17 +451,17 @@ class FileObject : public NativeObject {
   }
 
   RCFile* rcFile() {
     return reinterpret_cast<RCFile*>(
         js::GetReservedSlot(this, FILE_SLOT).toPrivate());
   }
 };
 
-static const js::ClassOps FileObjectClassOps = {
+static const JSClassOps FileObjectClassOps = {
     nullptr,              /* addProperty */
     nullptr,              /* delProperty */
     nullptr,              /* enumerate */
     nullptr,              /* newEnumerate */
     nullptr,              /* resolve */
     nullptr,              /* mayResolve */
     FileObject::finalize, /* finalize */
     nullptr,              /* call */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -6972,17 +6972,17 @@ class StreamCacheEntry : public AtomicRe
     return optimized_.lock().get();
   }
 };
 
 typedef RefPtr<StreamCacheEntry> StreamCacheEntryPtr;
 
 class StreamCacheEntryObject : public NativeObject {
   static const unsigned CACHE_ENTRY_SLOT = 0;
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
   static const JSPropertySpec properties_;
 
   static void finalize(JSFreeOp*, JSObject* obj) {
     obj->as<StreamCacheEntryObject>().cache().Release();
   }
 
   static bool cachedGetter(JSContext* cx, unsigned argc, Value* vp) {
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -7069,17 +7069,17 @@ class StreamCacheEntryObject : public Na
     return true;
   }
 
   StreamCacheEntry& cache() const {
     return *(StreamCacheEntry*)getReservedSlot(CACHE_ENTRY_SLOT).toPrivate();
   }
 };
 
-const ClassOps StreamCacheEntryObject::classOps_ = {
+const JSClassOps StreamCacheEntryObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     StreamCacheEntryObject::finalize};
 
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -979,17 +979,17 @@ size_t ArgumentsObject::objectMoved(JSOb
 }
 
 /*
  * The classes below collaborate to lazily reflect and synchronize actual
  * argument values, argument count, and callee function object stored in a
  * stack frame with their corresponding property values in the frame's
  * arguments object.
  */
-const ClassOps MappedArgumentsObject::classOps_ = {
+const JSClassOps MappedArgumentsObject::classOps_ = {
     nullptr, /* addProperty */
     ArgumentsObject::obj_delProperty,
     MappedArgumentsObject::obj_enumerate,
     nullptr, /* newEnumerate */
     MappedArgumentsObject::obj_resolve,
     ArgumentsObject::obj_mayResolve,
     ArgumentsObject::finalize,
     nullptr, /* call        */
@@ -1015,17 +1015,17 @@ const Class MappedArgumentsObject::class
     nullptr,
     &MappedArgumentsObject::classExt_,
     &MappedArgumentsObject::objectOps_};
 
 /*
  * Unmapped arguments is significantly less magical than mapped arguments, so
  * it is represented by a different class while sharing some functionality.
  */
-const ClassOps UnmappedArgumentsObject::classOps_ = {
+const JSClassOps UnmappedArgumentsObject::classOps_ = {
     nullptr, /* addProperty */
     ArgumentsObject::obj_delProperty,
     UnmappedArgumentsObject::obj_enumerate,
     nullptr, /* newEnumerate */
     UnmappedArgumentsObject::obj_resolve,
     ArgumentsObject::obj_mayResolve,
     ArgumentsObject::finalize,
     nullptr, /* call        */
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -408,17 +408,17 @@ class ArgumentsObject : public NativeObj
                                        ArgumentsData* data);
   static void MaybeForwardToCallObject(jit::JitFrameLayout* frame,
                                        HandleObject callObj,
                                        ArgumentsObject* obj,
                                        ArgumentsData* data);
 };
 
 class MappedArgumentsObject : public ArgumentsObject {
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
   static const ClassExtension classExt_;
   static const ObjectOps objectOps_;
 
  public:
   static const Class class_;
 
   JSFunction& callee() const {
     return getFixedSlot(CALLEE_SLOT).toObject().as<JSFunction>();
@@ -440,17 +440,17 @@ class MappedArgumentsObject : public Arg
   static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id,
                           bool* resolvedp);
   static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                  Handle<JS::PropertyDescriptor> desc,
                                  ObjectOpResult& result);
 };
 
 class UnmappedArgumentsObject : public ArgumentsObject {
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
   static const ClassExtension classExt_;
 
  public:
   static const Class class_;
 
  private:
   static bool obj_enumerate(JSContext* cx, HandleObject obj);
   static bool obj_resolve(JSContext* cx, HandleObject obj, HandleId id,
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -282,17 +282,17 @@ void js::UnmapBufferMemory(void* base, s
  * access.  It can be created explicitly and passed to a TypedArrayObject, or
  * can be created implicitly by constructing a TypedArrayObject with a size.
  */
 
 /*
  * ArrayBufferObject (base)
  */
 
-static const ClassOps ArrayBufferObjectClassOps = {
+static const JSClassOps ArrayBufferObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     ArrayBufferObject::finalize,
     nullptr, /* call        */
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -353,17 +353,17 @@ const ObjectOps ModuleEnvironmentObject:
     ModuleEnvironmentObject::hasProperty,
     ModuleEnvironmentObject::getProperty,
     ModuleEnvironmentObject::setProperty,
     ModuleEnvironmentObject::getOwnPropertyDescriptor,
     ModuleEnvironmentObject::deleteProperty,
     nullptr, /* getElements */
     nullptr};
 
-const ClassOps ModuleEnvironmentObject::classOps_ = {
+const JSClassOps ModuleEnvironmentObject::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     ModuleEnvironmentObject::newEnumerate};
 
 const Class ModuleEnvironmentObject::class_ = {
     "ModuleEnvironmentObject",
     JSCLASS_HAS_RESERVED_SLOTS(ModuleEnvironmentObject::RESERVED_SLOTS),
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -394,17 +394,17 @@ class VarEnvironmentObject : public Envi
 
   bool isForEval() const { return scope().is<EvalScope>(); }
 };
 
 class ModuleEnvironmentObject : public EnvironmentObject {
   static const uint32_t MODULE_SLOT = 1;
 
   static const ObjectOps objectOps_;
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
  public:
   static const Class class_;
 
   static const uint32_t RESERVED_SLOTS = 2;
 
   static ModuleEnvironmentObject* create(JSContext* cx,
                                          HandleModuleObject module);
--- a/js/src/vm/Instrumentation.cpp
+++ b/js/src/vm/Instrumentation.cpp
@@ -41,17 +41,17 @@ void RealmInstrumentation::holderFinaliz
 }
 
 /* static */
 void RealmInstrumentation::holderTrace(JSTracer* trc, JSObject* obj) {
   RealmInstrumentation* instrumentation = GetInstrumentation(obj);
   instrumentation->trace(trc);
 }
 
-static const ClassOps InstrumentationHolderClassOps = {
+static const JSClassOps InstrumentationHolderClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     RealmInstrumentation::holderFinalize,
     nullptr, /* call */
--- a/js/src/vm/Iteration.cpp
+++ b/js/src/vm/Iteration.cpp
@@ -1080,27 +1080,27 @@ void PropertyIteratorObject::trace(JSTra
 
 void PropertyIteratorObject::finalize(JSFreeOp* fop, JSObject* obj) {
   if (NativeIterator* ni =
           obj->as<PropertyIteratorObject>().getNativeIterator()) {
     fop->free_(obj, ni, ni->allocationSize(), MemoryUse::NativeIterator);
   }
 }
 
-const ClassOps PropertyIteratorObject::classOps_ = {nullptr, /* addProperty */
-                                                    nullptr, /* delProperty */
-                                                    nullptr, /* enumerate */
-                                                    nullptr, /* newEnumerate */
-                                                    nullptr, /* resolve */
-                                                    nullptr, /* mayResolve */
-                                                    finalize,
-                                                    nullptr, /* call        */
-                                                    nullptr, /* hasInstance */
-                                                    nullptr, /* construct   */
-                                                    trace};
+const JSClassOps PropertyIteratorObject::classOps_ = {
+    nullptr,           /* addProperty */
+    nullptr,           /* delProperty */
+    nullptr,           /* enumerate */
+    nullptr,           /* newEnumerate */
+    nullptr,           /* resolve */
+    nullptr,           /* mayResolve */
+    finalize, nullptr, /* call        */
+    nullptr,           /* hasInstance */
+    nullptr,           /* construct   */
+    trace};
 
 const Class PropertyIteratorObject::class_ = {
     "Iterator", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
     &PropertyIteratorObject::classOps_};
 
 static const Class ArrayIteratorPrototypeClass = {"Array Iterator", 0};
 
 enum {
--- a/js/src/vm/Iteration.h
+++ b/js/src/vm/Iteration.h
@@ -344,17 +344,17 @@ struct NativeIterator {
   }
 
   static constexpr size_t offsetOfPrev() {
     return offsetof(NativeIterator, prev_);
   }
 };
 
 class PropertyIteratorObject : public NativeObject {
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
  public:
   static const Class class_;
 
   // We don't use the fixed slot but the JITs use this constant to load the
   // private value (the NativeIterator*).
   static const uint32_t NUM_FIXED_SLOTS = 1;
 
--- a/js/src/vm/JSFunction.cpp
+++ b/js/src/vm/JSFunction.cpp
@@ -1189,17 +1189,17 @@ static const JSFunctionSpec function_met
     JS_FN(js_toString_str, fun_toString, 0, 0),
     JS_FN(js_apply_str, fun_apply, 2, 0),
     JS_FN(js_call_str, fun_call, 1, 0),
     JS_SELF_HOSTED_FN("bind", "FunctionBind", 2, 0),
     JS_SYM_FN(hasInstance, fun_symbolHasInstance, 1,
               JSPROP_READONLY | JSPROP_PERMANENT),
     JS_FS_END};
 
-static const ClassOps JSFunctionClassOps = {
+static const JSClassOps JSFunctionClassOps = {
     nullptr,                                /* addProperty */
     nullptr,                                /* delProperty */
     fun_enumerate, nullptr,                 /* newEnumerate */
     fun_resolve,   fun_mayResolve, nullptr, /* finalize    */
     nullptr,                                /* call        */
     nullptr,       nullptr,                 /* construct   */
     fun_trace,
 };
--- a/js/src/vm/JSObject.h
+++ b/js/src/vm/JSObject.h
@@ -1117,19 +1117,19 @@ inline bool IsObjectValueInCompartment(c
   return v.toObject().compartment() == comp;
 }
 #endif
 
 /*
  * A generic trace hook that calls the object's 'trace' method.
  *
  * If you are introducing a new JSObject subclass, MyObject, that needs a custom
- * js::ClassOps::trace function, it's often helpful to write `trace` as a
+ * JSClassOps::trace function, it's often helpful to write `trace` as a
  * non-static member function, since `this` will the correct type. In this case,
- * you can use `CallTraceMethod<MyObject>` as your js::ClassOps::trace value.
+ * you can use `CallTraceMethod<MyObject>` as your JSClassOps::trace value.
  */
 template <typename ObjectSubclass>
 void CallTraceMethod(JSTracer* trc, JSObject* obj) {
   obj->as<ObjectSubclass>().trace(trc);
 }
 
 } /* namespace js */
 
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -1664,17 +1664,17 @@ void ScriptSourceObject::trace(JSTracer*
   // This can be invoked during allocation of the SSO itself, before we've had a
   // chance to initialize things properly. In that case, there's nothing to
   // trace.
   if (obj->as<ScriptSourceObject>().hasSource()) {
     obj->as<ScriptSourceObject>().source()->trace(trc);
   }
 }
 
-static const ClassOps ScriptSourceObjectClassOps = {
+static const JSClassOps ScriptSourceObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     ScriptSourceObject::finalize,
     nullptr, /* call */
@@ -3662,56 +3662,16 @@ void js::SweepScriptData(JSRuntime* rt) 
     RuntimeScriptData* scriptData = e.front();
     if (scriptData->refCount() == 1) {
       scriptData->Release();
       e.removeFront();
     }
   }
 }
 
-void js::FreeScriptData(JSRuntime* rt) {
-  AutoLockScriptData lock(rt);
-
-  RuntimeScriptDataTable& table = rt->scriptDataTable(lock);
-
-  // The table should be empty unless the embedding leaked GC things.
-  MOZ_ASSERT_IF(rt->gc.shutdownCollectedEverything(), table.empty());
-
-#ifdef DEBUG
-  size_t numLive = 0;
-  size_t maxCells = 5;
-  char* env = getenv("JS_GC_MAX_LIVE_CELLS");
-  if (env && *env) {
-    maxCells = atol(env);
-  }
-#endif
-
-  for (RuntimeScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
-#ifdef DEBUG
-    if (++numLive <= maxCells) {
-      RuntimeScriptData* scriptData = e.front();
-      fprintf(stderr,
-              "ERROR: GC found live RuntimeScriptData %p with ref count %d at "
-              "shutdown\n",
-              scriptData, scriptData->refCount());
-    }
-#endif
-    js_free(e.front());
-  }
-
-#ifdef DEBUG
-  if (numLive > 0) {
-    fprintf(stderr, "ERROR: GC found %zu live RuntimeScriptData at shutdown\n",
-            numLive);
-  }
-#endif
-
-  table.clear();
-}
-
 /* static */
 size_t PrivateScriptData::AllocationSize(uint32_t ngcthings) {
   size_t size = sizeof(PrivateScriptData);
 
   size += ngcthings * sizeof(JS::GCCellPtr);
 
   return size;
 }
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1260,17 +1260,17 @@ class ScriptSourceHolder {
 // realm in the same compartment, so sso->realm() does not necessarily match the
 // script's realm.
 //
 // We need ScriptSourceObject (instead of storing these GC pointers in the
 // ScriptSource itself) to properly account for cross-zone pointers: the
 // canonical SSO will be stored in the wrapper map if necessary so GC will do
 // the right thing.
 class ScriptSourceObject : public NativeObject {
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static ScriptSourceObject* createInternal(JSContext* cx, ScriptSource* source,
                                             HandleObject canonical);
 
   bool isCanonical() const {
     return &getReservedSlot(CANONICAL_SLOT).toObject() == this;
   }
   ScriptSourceObject* unwrappedCanonical() const;
@@ -2118,18 +2118,16 @@ struct RuntimeScriptDataHasher {
 
 class AutoLockScriptData;
 
 using RuntimeScriptDataTable =
     HashSet<RuntimeScriptData*, RuntimeScriptDataHasher, SystemAllocPolicy>;
 
 extern void SweepScriptData(JSRuntime* rt);
 
-extern void FreeScriptData(JSRuntime* rt);
-
 } /* namespace js */
 
 namespace JS {
 
 // Define a GCManagedDeletePolicy to allow deleting type outside of normal
 // sweeping.
 template <>
 struct DeletePolicy<js::PrivateScriptData>
--- a/js/src/vm/PIC.cpp
+++ b/js/src/vm/PIC.cpp
@@ -318,27 +318,27 @@ void js::ForOfPIC::Chain::freeAllStubs(J
 
 static void ForOfPIC_traceObject(JSTracer* trc, JSObject* obj) {
   if (ForOfPIC::Chain* chain =
           ForOfPIC::fromJSObject(&obj->as<NativeObject>())) {
     chain->trace(trc);
   }
 }
 
-static const ClassOps ForOfPICClassOps = {nullptr,
-                                          nullptr,
-                                          nullptr,
-                                          nullptr,
-                                          nullptr,
-                                          nullptr,
-                                          ForOfPIC_finalize,
-                                          nullptr, /* call        */
-                                          nullptr, /* hasInstance */
-                                          nullptr, /* construct   */
-                                          ForOfPIC_traceObject};
+static const JSClassOps ForOfPICClassOps = {nullptr,
+                                            nullptr,
+                                            nullptr,
+                                            nullptr,
+                                            nullptr,
+                                            nullptr,
+                                            ForOfPIC_finalize,
+                                            nullptr, /* call        */
+                                            nullptr, /* hasInstance */
+                                            nullptr, /* construct   */
+                                            ForOfPIC_traceObject};
 
 const Class ForOfPIC::class_ = {
     "ForOfPIC", JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE,
     &ForOfPICClassOps};
 
 /* static */
 NativeObject* js::ForOfPIC::createForOfPICObject(JSContext* cx,
                                                  Handle<GlobalObject*> global) {
--- a/js/src/vm/Realm.cpp
+++ b/js/src/vm/Realm.cpp
@@ -57,27 +57,23 @@ Realm::Realm(Compartment* comp, const JS
   MOZ_ASSERT_IF(creationOptions_.mergeable(),
                 creationOptions_.invisibleToDebugger());
 
   runtime_->numRealms++;
 }
 
 Realm::~Realm() {
   MOZ_ASSERT(!hasBeenEnteredIgnoringJit());
+  MOZ_ASSERT(!isDebuggee());
 
   // Write the code coverage information in a file.
   if (coverage::IsLCovEnabled()) {
     runtime_->lcovOutput().writeLCovResult(lcovOutput);
   }
 
-  // We can have a debuggee realm here only if we are destroying the runtime and
-  // leaked GC things.
-  MOZ_ASSERT_IF(runtime_->gc.shutdownCollectedEverything(), !isDebuggee());
-  unsetIsDebuggee();
-
   MOZ_ASSERT(runtime_->numRealms > 0);
   runtime_->numRealms--;
 }
 
 bool ObjectRealm::init(JSContext* cx) {
   NativeIteratorSentinel sentinel(NativeIterator::allocateSentinel(cx));
   if (!sentinel) {
     return false;
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -161,17 +161,17 @@ static inline bool IsMarkingTrace(JSTrac
 
   return JS::RuntimeHeapIsCollecting() && trc->isMarkingTracer();
 }
 
 void RegExpObject::trace(JSTracer* trc) {
   TraceNullableEdge(trc, &sharedRef(), "RegExpObject shared");
 }
 
-static const ClassOps RegExpObjectClassOps = {
+static const JSClassOps RegExpObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* finalize */
     nullptr, /* call */
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -29,27 +29,27 @@ static void resc_finalize(JSFreeOp* fop,
 
 static void resc_trace(JSTracer* trc, JSObject* obj) {
   void* pdata = obj->as<RegExpStaticsObject>().getPrivate();
   if (pdata) {
     static_cast<RegExpStatics*>(pdata)->trace(trc);
   }
 }
 
-static const ClassOps RegExpStaticsObjectClassOps = {nullptr, /* addProperty */
-                                                     nullptr, /* delProperty */
-                                                     nullptr, /* enumreate */
-                                                     nullptr, /* newEnumerate */
-                                                     nullptr, /* resolve */
-                                                     nullptr, /* mayResolve */
-                                                     resc_finalize,
-                                                     nullptr, /* call */
-                                                     nullptr, /* hasInstance */
-                                                     nullptr, /* construct */
-                                                     resc_trace};
+static const JSClassOps RegExpStaticsObjectClassOps = {
+    nullptr,                /* addProperty */
+    nullptr,                /* delProperty */
+    nullptr,                /* enumreate */
+    nullptr,                /* newEnumerate */
+    nullptr,                /* resolve */
+    nullptr,                /* mayResolve */
+    resc_finalize, nullptr, /* call */
+    nullptr,                /* hasInstance */
+    nullptr,                /* construct */
+    resc_trace};
 
 const Class RegExpStaticsObject::class_ = {
     "RegExpStatics", JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE,
     &RegExpStaticsObjectClassOps};
 
 RegExpStaticsObject* RegExpStatics::create(JSContext* cx) {
   RegExpStaticsObject* obj =
       NewObjectWithGivenProto<RegExpStaticsObject>(cx, nullptr);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -282,21 +282,22 @@ void JSRuntime::destroyRuntime() {
     JS::PrepareForFullGC(cx);
     gc.gc(GC_NORMAL, JS::GCReason::DESTROY_RUNTIME);
   }
 
   AutoNoteSingleThreadedRegion anstr;
 
   MOZ_ASSERT(!hasHelperThreadZones());
 
-  /*
-   * Even though all objects in the compartment are dead, we may have keep
-   * some filenames around because of gcKeepAtoms.
-   */
-  FreeScriptData(this);
+#ifdef DEBUG
+  {
+    AutoLockScriptData lock(this);
+    MOZ_ASSERT(scriptDataTable(lock).empty());
+  }
+#endif
 
 #if !ENABLE_INTL_API
   FinishRuntimeNumberState(this);
 #endif
 
   gc.finish();
 
   defaultLocale = nullptr;
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -342,17 +342,17 @@ void SavedFrame::HashPolicy::rekey(Key& 
 }
 
 /* static */
 bool SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor,
                                       HandleObject proto) {
   return FreezeObject(cx, proto);
 }
 
-static const ClassOps SavedFrameClassOps = {
+static const JSClassOps SavedFrameClassOps = {
     nullptr,               // addProperty
     nullptr,               // delProperty
     nullptr,               // enumerate
     nullptr,               // newEnumerate
     nullptr,               // resolve
     nullptr,               // mayResolve
     SavedFrame::finalize,  // finalize
     nullptr,               // call
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -2634,27 +2634,27 @@ GlobalObject* JSRuntime::createSelfHosti
   options.creationOptions().setNewCompartmentAndZone();
   options.behaviors().setDiscardSource(true);
 
   Realm* realm = NewRealm(cx, nullptr, options);
   if (!realm) {
     return nullptr;
   }
 
-  static const ClassOps shgClassOps = {nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       nullptr,
-                                       JS_GlobalObjectTraceHook};
+  static const JSClassOps shgClassOps = {nullptr,
+                                         nullptr,
+                                         nullptr,
+                                         nullptr,
+                                         nullptr,
+                                         nullptr,
+                                         nullptr,
+                                         nullptr,
+                                         nullptr,
+                                         nullptr,
+                                         JS_GlobalObjectTraceHook};
 
   static const Class shgClass = {"self-hosting-global", JSCLASS_GLOBAL_FLAGS,
                                  &shgClassOps};
 
   AutoRealmUnchecked ar(cx, realm);
   Rooted<GlobalObject*> shg(cx, GlobalObject::createInternal(cx, &shgClass));
   if (!shg) {
     return nullptr;
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -338,17 +338,17 @@ SharedArrayBufferObject* SharedArrayBuff
     return nullptr;
   }
 
   obj->acceptRawBuffer(buffer, initialSize);
 
   return obj;
 }
 
-static const ClassOps SharedArrayBufferObjectClassOps = {
+static const JSClassOps SharedArrayBufferObjectClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     SharedArrayBufferObject::Finalize,
     nullptr, /* call */
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3251,51 +3251,54 @@ class TypeConstraintClearDefiniteGetterS
     *res = zone.typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(
         group);
     return true;
   }
 
   Compartment* maybeCompartment() override { return group->compartment(); }
 };
 
-bool js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx,
-                                                       ObjectGroup* group,
-                                                       HandleId id) {
+bool js::AddClearDefiniteGetterSetterForPrototypeChain(
+    JSContext* cx, DPAConstraintInfo& constraintInfo, ObjectGroup* group,
+    HandleId id, bool* added) {
   /*
    * Ensure that if the properties named here could have a getter, setter or
    * a permanent property in any transitive prototype, the definite
    * properties get cleared from the group.
    */
+
+  *added = false;
+
   RootedObject proto(cx, group->proto().toObjectOrNull());
   while (proto) {
     if (!proto->hasStaticPrototype()) {
-      return false;
+      return true;
     }
     ObjectGroup* protoGroup = JSObject::getGroup(cx, proto);
     if (!protoGroup) {
-      cx->recoverFromOutOfMemory();
       return false;
     }
     AutoSweepObjectGroup sweep(protoGroup);
     if (protoGroup->unknownProperties(sweep)) {
-      return false;
+      return true;
     }
     HeapTypeSet* protoTypes = protoGroup->getProperty(sweep, cx, proto, id);
-    if (!protoTypes || protoTypes->nonDataProperty() ||
-        protoTypes->nonWritableProperty()) {
+    if (!protoTypes) {
       return false;
     }
-    if (!protoTypes->addConstraint(
-            cx,
-            cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(
-                group))) {
+    if (protoTypes->nonDataProperty() || protoTypes->nonWritableProperty()) {
+      return true;
+    }
+    if (!constraintInfo.addProtoConstraint(proto, id)) {
       return false;
     }
     proto = proto->staticPrototype();
   }
+
+  *added = true;
   return true;
 }
 
 /*
  * Constraint which clears definite properties on a group should a type set
  * contain any types other than a single object.
  */
 class TypeConstraintClearDefiniteSingle : public TypeConstraint {
@@ -3722,16 +3725,54 @@ static bool ChangeObjectFixedSlotCount(J
   if (!newShape) {
     return false;
   }
 
   obj->setLastPropertyShrinkFixedSlots(newShape);
   return true;
 }
 
+bool DPAConstraintInfo::finishConstraints(JSContext* cx, ObjectGroup* group) {
+  for (const ProtoConstraint& constraint : protoConstraints_) {
+    ObjectGroup* protoGroup = constraint.proto->group();
+
+    // Note: we rely on the group's type information being unchanged since
+    // AddClearDefiniteGetterSetterForPrototypeChain.
+
+    AutoSweepObjectGroup sweep(protoGroup);
+    bool unknownProperties = protoGroup->unknownProperties(sweep);
+    MOZ_RELEASE_ASSERT(!unknownProperties);
+
+    HeapTypeSet* protoTypes =
+        protoGroup->getProperty(sweep, cx, constraint.proto, constraint.id);
+    MOZ_RELEASE_ASSERT(protoTypes);
+
+    MOZ_ASSERT(!protoTypes->nonDataProperty());
+    MOZ_ASSERT(!protoTypes->nonWritableProperty());
+
+    if (!protoTypes->addConstraint(
+            cx,
+            cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteGetterSetter>(
+                group))) {
+      ReportOutOfMemory(cx);
+      return false;
+    }
+  }
+
+  for (const InliningConstraint& constraint : inliningConstraints_) {
+    if (!AddClearDefiniteFunctionUsesInScript(cx, group, constraint.caller,
+                                              constraint.callee)) {
+      ReportOutOfMemory(cx);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 bool TypeNewScript::maybeAnalyze(JSContext* cx, ObjectGroup* group,
                                  bool* regenerate, bool force) {
   // Perform the new script properties analysis if necessary, returning
   // whether the new group table was updated and group needs to be refreshed.
 
   // Make sure there aren't dead references in preliminaryObjects. This can
   // clear out the new script information on OOM.
   AutoSweepObjectGroup sweep(group);
@@ -3840,20 +3881,22 @@ bool TypeNewScript::maybeAnalyze(JSConte
   templateObject_ =
       NewObjectWithGroup<PlainObject>(cx, groupRoot, kind, TenuredObject);
   if (!templateObject_) {
     return false;
   }
 
   Vector<TypeNewScriptInitializer> initializerVector(cx);
 
+  DPAConstraintInfo constraintInfo(cx);
+
   RootedPlainObject templateRoot(cx, templateObject());
   RootedFunction fun(cx, function());
-  if (!jit::AnalyzeNewScriptDefiniteProperties(cx, fun, group, templateRoot,
-                                               &initializerVector)) {
+  if (!jit::AnalyzeNewScriptDefiniteProperties(
+          cx, constraintInfo, fun, group, templateRoot, &initializerVector)) {
     return false;
   }
 
   if (!group->newScript(sweep)) {
     return true;
   }
 
   MOZ_ASSERT(OnlyHasDataProperties(templateObject()->lastProperty()));
@@ -3911,16 +3954,24 @@ bool TypeNewScript::maybeAnalyze(JSConte
   preliminaryObjects = nullptr;
 
   AddCellMemory(group, gcMallocBytes(), MemoryUse::ObjectGroupAddendum);
 
   if (prefixShape->slotSpan() == templateObject()->slotSpan()) {
     // The definite properties analysis found exactly the properties that
     // are held in common by the preliminary objects. No further analysis
     // is needed.
+
+    if (!constraintInfo.finishConstraints(cx, group)) {
+      return false;
+    }
+    if (!group->newScript(sweep)) {
+      return true;
+    }
+
     group->addDefiniteProperties(cx, templateObject()->lastProperty());
 
     destroyNewScript.release();
     return true;
   }
 
   // There are more properties consistently added to objects of this group
   // than were discovered by the definite properties analysis. Use the
@@ -3934,16 +3985,26 @@ bool TypeNewScript::maybeAnalyze(JSConte
 
   Rooted<TaggedProto> protoRoot(cx, group->proto());
   ObjectGroup* initialGroup = ObjectGroupRealm::makeGroup(
       cx, group->realm(), group->clasp(), protoRoot, initialFlags);
   if (!initialGroup) {
     return false;
   }
 
+  // Add the constraints. Use the initialGroup as group referenced by the
+  // constraints because that's the group that will have the TypeNewScript
+  // associated with it. See the detachNewScript and setNewScript calls below.
+  if (!constraintInfo.finishConstraints(cx, initialGroup)) {
+    return false;
+  }
+  if (!group->newScript(sweep)) {
+    return true;
+  }
+
   initialGroup->addDefiniteProperties(cx, templateObject()->lastProperty());
   group->addDefiniteProperties(cx, prefixShape);
 
   ObjectGroupRealm& realm = ObjectGroupRealm::get(group);
   realm.replaceDefaultNewGroup(nullptr, group->proto(), function(),
                                initialGroup);
 
   templateObject()->setGroup(initialGroup);
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -98,19 +98,68 @@ class MOZ_RAII AutoSweepJitScript : publ
 
   jit::JitScript* jitScript() const { return jitScript_; }
   Zone* zone() const { return zone_; }
 #endif
 };
 
 CompilerConstraintList* NewCompilerConstraintList(jit::TempAllocator& alloc);
 
-bool AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx,
-                                                   ObjectGroup* group,
-                                                   HandleId id);
+// Stack class to record information about constraints that need to be added
+// after finishing the Definite Properties Analysis. When the analysis succeeds
+// the |finishConstraints| method must be called to add the constraints to the
+// TypeSets.
+//
+// There are two constraint types managed here:
+//
+//   1. Proto constraints for HeapTypeSets, to guard against things like getters
+//      and setters on the proto chain.
+//
+//   2. Inlining constraints for StackTypeSets, to invalidate when additional
+//      functions could be called at call sites where we inlined a function.
+//
+// This class uses bare GC-thing pointers because GC is suppressed when the
+// analysis runs.
+class MOZ_RAII DPAConstraintInfo {
+  struct ProtoConstraint {
+    JSObject* proto;
+    jsid id;
+    ProtoConstraint(JSObject* proto, jsid id) : proto(proto), id(id) {}
+  };
+  struct InliningConstraint {
+    JSScript* caller;
+    JSScript* callee;
+    InliningConstraint(JSScript* caller, JSScript* callee)
+        : caller(caller), callee(callee) {}
+  };
+
+  JS::AutoCheckCannotGC nogc_;
+  Vector<ProtoConstraint, 8> protoConstraints_;
+  Vector<InliningConstraint, 4> inliningConstraints_;
+
+ public:
+  explicit DPAConstraintInfo(JSContext* cx)
+      : nogc_(cx), protoConstraints_(cx), inliningConstraints_(cx) {}
+
+  DPAConstraintInfo(const DPAConstraintInfo&) = delete;
+  void operator=(const DPAConstraintInfo&) = delete;
+
+  MOZ_MUST_USE bool addProtoConstraint(JSObject* proto, jsid id) {
+    return protoConstraints_.emplaceBack(proto, id);
+  }
+  MOZ_MUST_USE bool addInliningConstraint(JSScript* caller, JSScript* callee) {
+    return inliningConstraints_.emplaceBack(caller, callee);
+  }
+
+  MOZ_MUST_USE bool finishConstraints(JSContext* cx, ObjectGroup* group);
+};
+
+bool AddClearDefiniteGetterSetterForPrototypeChain(
+    JSContext* cx, DPAConstraintInfo& constraintInfo, ObjectGroup* group,
+    HandleId id, bool* added);
 
 bool AddClearDefiniteFunctionUsesInScript(JSContext* cx, ObjectGroup* group,
                                           JSScript* script,
                                           JSScript* calleeScript);
 
 // For groups where only a small number of objects have been allocated, this
 // structure keeps track of all objects in the group. Once COUNT objects have
 // been allocated, this structure is cleared and the objects are analyzed, to
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -2125,17 +2125,17 @@ bool TypedArrayObject::getElements(JSCon
 /***
  *** JS impl
  ***/
 
 /*
  * TypedArrayObject boilerplate
  */
 
-static const ClassOps TypedArrayClassOps = {
+static const JSClassOps TypedArrayClassOps = {
     nullptr,                      /* addProperty */
     nullptr,                      /* delProperty */
     nullptr,                      /* enumerate   */
     nullptr,                      /* newEnumerate */
     nullptr,                      /* resolve     */
     nullptr,                      /* mayResolve  */
     TypedArrayObject::finalize,   /* finalize    */
     nullptr,                      /* call        */
--- a/js/src/wasm/WasmAST.h
+++ b/js/src/wasm/WasmAST.h
@@ -253,23 +253,23 @@ struct AstNameHasher {
 using AstNameMap = AstHashMap<AstName, uint32_t, AstNameHasher>;
 
 typedef AstVector<AstValType> AstValTypeVector;
 typedef AstVector<AstExpr*> AstExprVector;
 typedef AstVector<AstName> AstNameVector;
 typedef AstVector<AstRef> AstRefVector;
 
 struct AstBase {
-  void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() {
+  void* operator new(size_t numBytes, LifoAlloc& astLifo) noexcept(true) {
     return astLifo.alloc(numBytes);
   }
 };
 
 struct AstNode {
-  void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() {
+  void* operator new(size_t numBytes, LifoAlloc& astLifo) noexcept(true) {
     return astLifo.alloc(numBytes);
   }
 };
 
 class AstFuncType;
 class AstStructType;
 
 class AstTypeDef : public AstNode {
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -652,23 +652,23 @@ static bool GetLimits(JSContext* cx, Han
   }
 
   return true;
 }
 
 // ============================================================================
 // WebAssembly.Module class and methods
 
-const ClassOps WasmModuleObject::classOps_ = {nullptr, /* addProperty */
-                                              nullptr, /* delProperty */
-                                              nullptr, /* enumerate */
-                                              nullptr, /* newEnumerate */
-                                              nullptr, /* resolve */
-                                              nullptr, /* mayResolve */
-                                              WasmModuleObject::finalize};
+const JSClassOps WasmModuleObject::classOps_ = {nullptr, /* addProperty */
+                                                nullptr, /* delProperty */
+                                                nullptr, /* enumerate */
+                                                nullptr, /* newEnumerate */
+                                                nullptr, /* resolve */
+                                                nullptr, /* mayResolve */
+                                                WasmModuleObject::finalize};
 
 const Class WasmModuleObject::class_ = {
     "WebAssembly.Module",
     JSCLASS_DELAY_METADATA_BUILDER |
         JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
     &WasmModuleObject::classOps_,
 };
@@ -1171,27 +1171,27 @@ bool WasmModuleObject::construct(JSConte
 const Module& WasmModuleObject::module() const {
   MOZ_ASSERT(is<WasmModuleObject>());
   return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate();
 }
 
 // ============================================================================
 // WebAssembly.Instance class and methods
 
-const ClassOps WasmInstanceObject::classOps_ = {nullptr, /* addProperty */
-                                                nullptr, /* delProperty */
-                                                nullptr, /* enumerate */
-                                                nullptr, /* newEnumerate */
-                                                nullptr, /* resolve */
-                                                nullptr, /* mayResolve */
-                                                WasmInstanceObject::finalize,
-                                                nullptr, /* call */
-                                                nullptr, /* hasInstance */
-                                                nullptr, /* construct */
-                                                WasmInstanceObject::trace};
+const JSClassOps WasmInstanceObject::classOps_ = {nullptr, /* addProperty */
+                                                  nullptr, /* delProperty */
+                                                  nullptr, /* enumerate */
+                                                  nullptr, /* newEnumerate */
+                                                  nullptr, /* resolve */
+                                                  nullptr, /* mayResolve */
+                                                  WasmInstanceObject::finalize,
+                                                  nullptr, /* call */
+                                                  nullptr, /* hasInstance */
+                                                  nullptr, /* construct */
+                                                  WasmInstanceObject::trace};
 
 const Class WasmInstanceObject::class_ = {
     "WebAssembly.Instance",
     JSCLASS_DELAY_METADATA_BUILDER |
         JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
     &WasmInstanceObject::classOps_,
 };
@@ -1598,23 +1598,23 @@ WasmInstanceObject* wasm::ExportedFuncti
 uint32_t wasm::ExportedFunctionToFuncIndex(JSFunction* fun) {
   Instance& instance = ExportedFunctionToInstanceObject(fun)->instance();
   return instance.code().getFuncIndex(fun);
 }
 
 // ============================================================================
 // WebAssembly.Memory class and methods
 
-const ClassOps WasmMemoryObject::classOps_ = {nullptr, /* addProperty */
-                                              nullptr, /* delProperty */
-                                              nullptr, /* enumerate */
-                                              nullptr, /* newEnumerate */
-                                              nullptr, /* resolve */
-                                              nullptr, /* mayResolve */
-                                              WasmMemoryObject::finalize};
+const JSClassOps WasmMemoryObject::classOps_ = {nullptr, /* addProperty */
+                                                nullptr, /* delProperty */
+                                                nullptr, /* enumerate */
+                                                nullptr, /* newEnumerate */
+                                                nullptr, /* resolve */
+                                                nullptr, /* mayResolve */
+                                                WasmMemoryObject::finalize};
 
 const Class WasmMemoryObject::class_ = {
     "WebAssembly.Memory",
     JSCLASS_DELAY_METADATA_BUILDER |
         JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
     &WasmMemoryObject::classOps_};
 
@@ -1938,27 +1938,27 @@ uint32_t WasmMemoryObject::grow(HandleWa
 bool js::wasm::IsSharedWasmMemoryObject(JSObject* obj) {
   WasmMemoryObject* mobj = obj->maybeUnwrapIf<WasmMemoryObject>();
   return mobj && mobj->isShared();
 }
 
 // ============================================================================
 // WebAssembly.Table class and methods
 
-const ClassOps WasmTableObject::classOps_ = {nullptr, /* addProperty */
-                                             nullptr, /* delProperty */
-                                             nullptr, /* enumerate */
-                                             nullptr, /* newEnumerate */
-                                             nullptr, /* resolve */
-                                             nullptr, /* mayResolve */
-                                             WasmTableObject::finalize,
-                                             nullptr, /* call */
-                                             nullptr, /* hasInstance */
-                                             nullptr, /* construct */
-                                             WasmTableObject::trace};
+const JSClassOps WasmTableObject::classOps_ = {nullptr, /* addProperty */
+                                               nullptr, /* delProperty */
+                                               nullptr, /* enumerate */
+                                               nullptr, /* newEnumerate */
+                                               nullptr, /* resolve */
+                                               nullptr, /* mayResolve */
+                                               WasmTableObject::finalize,
+                                               nullptr, /* call */
+                                               nullptr, /* hasInstance */
+                                               nullptr, /* construct */
+                                               WasmTableObject::trace};
 
 const Class WasmTableObject::class_ = {
     "WebAssembly.Table",
     JSCLASS_DELAY_METADATA_BUILDER |
         JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS) |
         JSCLASS_FOREGROUND_FINALIZE,
     &WasmTableObject::classOps_};
 
@@ -2313,27 +2313,27 @@ const JSFunctionSpec WasmTableObject::st
 
 Table& WasmTableObject::table() const {
   return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
 }
 
 // ============================================================================
 // WebAssembly.global class and methods
 
-const ClassOps WasmGlobalObject::classOps_ = {nullptr, /* addProperty */
-                                              nullptr, /* delProperty */
-                                              nullptr, /* enumerate */
-                                              nullptr, /* newEnumerate */
-                                              nullptr, /* resolve */
-                                              nullptr, /* mayResolve */
-                                              WasmGlobalObject::finalize,
-                                              nullptr, /* call */
-                                              nullptr, /* hasInstance */
-                                              nullptr, /* construct */
-                                              WasmGlobalObject::trace};
+const JSClassOps WasmGlobalObject::classOps_ = {nullptr, /* addProperty */
+                                                nullptr, /* delProperty */
+                                                nullptr, /* enumerate */
+                                                nullptr, /* newEnumerate */
+                                                nullptr, /* resolve */
+                                                nullptr, /* mayResolve */
+                                                WasmGlobalObject::finalize,
+                                                nullptr, /* call */
+                                                nullptr, /* hasInstance */
+                                                nullptr, /* construct */
+                                                WasmGlobalObject::trace};
 
 const Class WasmGlobalObject::class_ = {
     "WebAssembly.Global",
     JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS) |
         JSCLASS_BACKGROUND_FINALIZE,
     &WasmGlobalObject::classOps_};
 
 /* static */
@@ -3417,17 +3417,17 @@ class CompileStreamTask : public Promise
 // A short-lived object that captures the arguments of a
 // WebAssembly.{compileStreaming,instantiateStreaming} while waiting for
 // the Promise<Response> to resolve to a (hopefully) Promise.
 class ResolveResponseClosure : public NativeObject {
   static const unsigned COMPILE_ARGS_SLOT = 0;
   static const unsigned PROMISE_OBJ_SLOT = 1;
   static const unsigned INSTANTIATE_SLOT = 2;
   static const unsigned IMPORT_OBJ_SLOT = 3;
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj) {
     auto& closure = obj->as<ResolveResponseClosure>();
     fop->release(obj, &closure.compileArgs(),
                  MemoryUse::WasmResolveResponseClosure);
   }
 
  public:
@@ -3463,17 +3463,17 @@ class ResolveResponseClosure : public Na
   bool instantiate() const {
     return getReservedSlot(INSTANTIATE_SLOT).toBoolean();
   }
   JSObject* importObj() const {
     return getReservedSlot(IMPORT_OBJ_SLOT).toObjectOrNull();
   }
 };
 
-const ClassOps ResolveResponseClosure::classOps_ = {
+const JSClassOps ResolveResponseClosure::classOps_ = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
     nullptr, /* enumerate */
     nullptr, /* newEnumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     ResolveResponseClosure::finalize};
 
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -121,17 +121,17 @@ extern const Class WebAssemblyClass;
 JSObject* InitWebAssemblyClass(JSContext* cx, Handle<GlobalObject*> global);
 
 // The class of WebAssembly.Module. Each WasmModuleObject owns a
 // wasm::Module. These objects are used both as content-facing JS objects and as
 // internal implementation details of asm.js.
 
 class WasmModuleObject : public NativeObject {
   static const unsigned MODULE_SLOT = 0;
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static bool imports(JSContext* cx, unsigned argc, Value* vp);
   static bool exports(JSContext* cx, unsigned argc, Value* vp);
   static bool customSections(JSContext* cx, unsigned argc, Value* vp);
 
  public:
   static const unsigned RESERVED_SLOTS = 1;
   static const Class class_;
@@ -156,17 +156,17 @@ class WasmModuleObject : public NativeOb
 // the most appropriate representation for Cell::anyref.
 STATIC_ASSERT_ANYREF_IS_JSOBJECT;
 
 class WasmGlobalObject : public NativeObject {
   static const unsigned TYPE_SLOT = 0;
   static const unsigned MUTABLE_SLOT = 1;
   static const unsigned CELL_SLOT = 2;
 
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
   static void finalize(JSFreeOp*, JSObject* obj);
   static void trace(JSTracer* trc, JSObject* obj);
 
   static bool valueGetterImpl(JSContext* cx, const CallArgs& args);
   static bool valueGetter(JSContext* cx, unsigned argc, Value* vp);
   static bool valueSetterImpl(JSContext* cx, const CallArgs& args);
   static bool valueSetter(JSContext* cx, unsigned argc, Value* vp);
 
@@ -209,17 +209,17 @@ class WasmGlobalObject : public NativeOb
 class WasmInstanceObject : public NativeObject {
   static const unsigned INSTANCE_SLOT = 0;
   static const unsigned EXPORTS_OBJ_SLOT = 1;
   static const unsigned EXPORTS_SLOT = 2;
   static const unsigned SCOPES_SLOT = 3;
   static const unsigned INSTANCE_SCOPE_SLOT = 4;
   static const unsigned GLOBALS_SLOT = 5;
 
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
   static bool exportsGetterImpl(JSContext* cx, const CallArgs& args);
   static bool exportsGetter(JSContext* cx, unsigned argc, Value* vp);
   bool isNewborn() const;
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static void trace(JSTracer* trc, JSObject* obj);
 
   // ExportMap maps from function index to exported function object.
   // This allows the instance to lazily create exported function
@@ -281,17 +281,17 @@ class WasmInstanceObject : public Native
 };
 
 // The class of WebAssembly.Memory. A WasmMemoryObject references an ArrayBuffer
 // or SharedArrayBuffer object which owns the actual memory.
 
 class WasmMemoryObject : public NativeObject {
   static const unsigned BUFFER_SLOT = 0;
   static const unsigned OBSERVERS_SLOT = 1;
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static bool bufferGetterImpl(JSContext* cx, const CallArgs& args);
   static bool bufferGetter(JSContext* cx, unsigned argc, Value* vp);
   static bool growImpl(JSContext* cx, const CallArgs& args);
   static bool grow(JSContext* cx, unsigned argc, Value* vp);
   static uint32_t growShared(HandleWasmMemoryObject memory, uint32_t delta);
 
   using InstanceSet =
@@ -340,17 +340,17 @@ class WasmMemoryObject : public NativeOb
 };
 
 // The class of WebAssembly.Table. A WasmTableObject holds a refcount on a
 // wasm::Table, allowing a Table to be shared between multiple Instances
 // (eventually between multiple threads).
 
 class WasmTableObject : public NativeObject {
   static const unsigned TABLE_SLOT = 0;
-  static const ClassOps classOps_;
+  static const JSClassOps classOps_;
   bool isNewborn() const;
   static void finalize(JSFreeOp* fop, JSObject* obj);
   static void trace(JSTracer* trc, JSObject* obj);
   static bool lengthGetterImpl(JSContext* cx, const CallArgs& args);
   static bool lengthGetter(JSContext* cx, unsigned argc, Value* vp);
   static bool getImpl(JSContext* cx, const CallArgs& args);
   static bool get(JSContext* cx, unsigned argc, Value* vp);
   static bool setImpl(JSContext* cx, const CallArgs& args);
--- a/js/src/wasm/cranelift/build.rs
+++ b/js/src/wasm/cranelift/build.rs
@@ -42,16 +42,17 @@ fn main() {
         .rustified_enum("BD_.*|Trap|TypeCode|FuncTypeIdDescKind")
         .whitelist_type("BD_.*|Trap|TypeCode|FuncTypeIdDescKind")
         .header("baldrapi.h")
         .clang_args(&[
             "-x",
             "c++",
             "-std=gnu++14",
             "-fno-sized-deallocation",
+            "-fno-aligned-new",
             "-DRUST_BINDGEN",
         ])
         .clang_arg("-I../..");
 
     match env::var_os("MOZ_TOPOBJDIR") {
         Some(path) => {
             let path = PathBuf::from(path).join("js/src/rust/extra-bindgen-flags");
 
--- a/js/xpconnect/public/xpc_map_end.h
+++ b/js/xpconnect/public/xpc_map_end.h
@@ -30,17 +30,17 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::GetClas
 
 /**************************************************************/
 
 // virtual
 uint32_t XPC_MAP_CLASSNAME::GetScriptableFlags() { return (XPC_MAP_FLAGS); }
 
 // virtual
 const js::Class* XPC_MAP_CLASSNAME::GetClass() {
-  static const js::ClassOps classOps = XPC_MAKE_CLASS_OPS(GetScriptableFlags());
+  static const JSClassOps classOps = XPC_MAKE_CLASS_OPS(GetScriptableFlags());
   static const js::Class klass =
       XPC_MAKE_CLASS(XPC_MAP_QUOTED_CLASSNAME, GetScriptableFlags(), &classOps);
   return &klass;
 }
 
 // virtual
 const JSClass* XPC_MAP_CLASSNAME::GetJSClass() { return Jsvalify(GetClass()); }
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -435,17 +435,17 @@ static size_t sandbox_moved(JSObject* ob
   }
 
   return static_cast<SandboxPrivate*>(sop)->ObjectMoved(obj, old);
 }
 
 #define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT \
   (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
 
-static const js::ClassOps SandboxClassOps = {
+static const JSClassOps SandboxClassOps = {
     nullptr,
     nullptr,
     nullptr,
     JS_NewEnumerateStandardClasses,
     JS_ResolveStandardClass,
     JS_MayResolveStandardClass,
     sandbox_finalize,
     nullptr,
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -639,17 +639,17 @@ static bool XPC_WN_NoHelper_Resolve(JSCo
   }
 
   return DefinePropertyIfFound(
       ccx, obj, id, set, nullptr, nullptr, wrapper->GetScope(), true, wrapper,
       wrapper, nullptr, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,
       resolvedp);
 }
 
-static const js::ClassOps XPC_WN_NoHelper_JSClassOps = {
+static const JSClassOps XPC_WN_NoHelper_JSClassOps = {
     XPC_WN_OnlyIWrite_AddPropertyStub,  // addProperty
     XPC_WN_CannotDeletePropertyStub,    // delProperty
     XPC_WN_Shared_Enumerate,            // enumerate
     nullptr,                            // newEnumerate
     XPC_WN_NoHelper_Resolve,            // resolve
     nullptr,                            // mayResolve
     XPC_WN_NoHelper_Finalize,           // finalize
     nullptr,                            // call
@@ -1081,17 +1081,17 @@ static bool XPC_WN_Proto_Resolve(JSConte
   nsCOMPtr<nsIXPCScriptable> scr = self->GetScriptable();
 
   return DefinePropertyIfFound(
       ccx, obj, id, self->GetSet(), nullptr, nullptr, self->GetScope(), true,
       nullptr, nullptr, scr,
       JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE, resolvedp);
 }
 
-static const js::ClassOps XPC_WN_Proto_JSClassOps = {
+static const JSClassOps XPC_WN_Proto_JSClassOps = {
     XPC_WN_OnlyIWrite_Proto_AddPropertyStub,  // addProperty
     XPC_WN_CannotDeletePropertyStub,          // delProperty
     XPC_WN_Proto_Enumerate,                   // enumerate
     nullptr,                                  // newEnumerate
     XPC_WN_Proto_Resolve,                     // resolve
     nullptr,                                  // mayResolve
     XPC_WN_Proto_Finalize,                    // finalize
     nullptr,                                  // call
@@ -1170,17 +1170,17 @@ static size_t XPC_WN_TearOff_ObjectMoved
 
 // Make sure XPC_WRAPPER_FLAGS has no reserved slots, so our
 // XPC_WN_TEAROFF_RESERVED_SLOTS value is OK.
 
 static_assert(((XPC_WRAPPER_FLAGS >> JSCLASS_RESERVED_SLOTS_SHIFT) &
                JSCLASS_RESERVED_SLOTS_MASK) == 0,
               "XPC_WRAPPER_FLAGS should not include any reserved slots");
 
-static const js::ClassOps XPC_WN_Tearoff_JSClassOps = {
+static const JSClassOps XPC_WN_Tearoff_JSClassOps = {
     XPC_WN_OnlyIWrite_AddPropertyStub,  // addProperty
     XPC_WN_CannotDeletePropertyStub,    // delProperty
     XPC_WN_TearOff_Enumerate,           // enumerate
     nullptr,                            // newEnumerate
     XPC_WN_TearOff_Resolve,             // resolve
     nullptr,                            // mayResolve
     XPC_WN_TearOff_Finalize,            // finalize
     nullptr,                            // call
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1079,17 +1079,17 @@ class XPCNativeInterface final {
   static already_AddRefed<XPCNativeInterface> NewInstance(
       JSContext* cx, const nsXPTInterfaceInfo* aInfo);
 
   XPCNativeInterface() = delete;
   XPCNativeInterface(const nsXPTInterfaceInfo* aInfo, jsid aName)
       : mInfo(aInfo), mName(aName), mMemberCount(0) {}
   ~XPCNativeInterface();
 
-  void* operator new(size_t, void* p) CPP_THROW_NEW { return p; }
+  void* operator new(size_t, void* p) noexcept(true) { return p; }
 
   XPCNativeInterface(const XPCNativeInterface& r) = delete;
   XPCNativeInterface& operator=(const XPCNativeInterface& r) = delete;
 
   static void DestroyInstance(XPCNativeInterface* inst);
 
  private:
   const nsXPTInterfaceInfo* mInfo;
@@ -1200,17 +1200,17 @@ class XPCNativeSet final {
 
  protected:
   static already_AddRefed<XPCNativeSet> NewInstance(
       JSContext* cx, nsTArray<RefPtr<XPCNativeInterface>>&& array);
   static already_AddRefed<XPCNativeSet> NewInstanceMutate(XPCNativeSetKey* key);
 
   XPCNativeSet() : mMemberCount(0), mInterfaceCount(0) {}
   ~XPCNativeSet();
-  void* operator new(size_t, void* p) CPP_THROW_NEW { return p; }
+  void* operator new(size_t, void* p) noexcept(true) { return p; }
 
   static void DestroyInstance(XPCNativeSet* inst);
 
  private:
   uint16_t mMemberCount;
   uint16_t mInterfaceCount;
   // Always last - object sized for array.
   // These are strong references.
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -2714,17 +2714,17 @@ bool RestyleManager::ProcessPostTraversa
       aRestyleState.AddPendingWrapperRestyle(
           ServoRestyleState::TableAwareParentFor(maybeAnonBoxChild));
     }
 
     // If we don't have a ::marker pseudo-element, but need it, then
     // reconstruct the frame.  (The opposite situation implies 'display'
     // changes so doesn't need to be handled explicitly here.)
     if (wasRestyled &&
-        styleFrame->StyleDisplay()->mDisplay == StyleDisplay::ListItem &&
+        styleFrame->StyleDisplay()->IsListItem() &&
         styleFrame->IsBlockFrameOrSubclass() &&
         !nsLayoutUtils::GetMarkerPseudo(aElement)) {
       RefPtr<ComputedStyle> pseudoStyle =
           aRestyleState.StyleSet().ProbePseudoElementStyle(
               *aElement, PseudoStyleType::marker, upToDateStyleIfRestyled);
       if (pseudoStyle) {
         changeHint |= nsChangeHint_ReconstructFrame;
       }
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -325,20 +325,22 @@ static inline nsContainerFrame* GetField
 
 #define FCDATA_DECL(_flags, _func) \
   { _flags, {(FrameCreationFunc)_func}, nullptr, PseudoStyleType::NotPseudo }
 #define FCDATA_WITH_WRAPPING_BLOCK(_flags, _func, _anon_box) \
   {                                                          \
     _flags | FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS,       \
         {(FrameCreationFunc)_func}, nullptr, _anon_box       \
   }
-
-#define UNREACHABLE_FCDATA() \
-  { 0, {(FrameCreationFunc) nullptr}, nullptr, PseudoStyleType::NotPseudo }
-//----------------------------------------------------------------------
+#define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
+#define FULL_CTOR_FCDATA(_flags, _func)                  \
+  {                                                      \
+    _flags | FCDATA_FUNC_IS_FULL_CTOR, {nullptr}, _func, \
+        PseudoStyleType::NotPseudo                       \
+  }
 
 /**
  * True if aFrame is an actual inline frame in the sense of non-replaced
  * display:inline CSS boxes.  In other words, it can be affected by {ib}
  * splitting and can contain first-letter frames.  Basically, this is either an
  * inline frame (positioned or otherwise) or an line frame (this last because
  * it can contain first-letter and because inserting blocks in the middle of it
  * needs to terminate it).
@@ -2357,17 +2359,17 @@ nsIFrame* nsCSSFrameConstructor::Constru
     // function in general.
     // Use a null PendingBinding, since our binding is not in fact pending.
     static const FrameConstructionData rootSVGData = FCDATA_DECL(0, nullptr);
     AutoFrameConstructionItem item(this, &rootSVGData, aDocElement, nullptr,
                                    do_AddRef(computedStyle), true);
 
     contentFrame = static_cast<nsContainerFrame*>(
         ConstructOuterSVG(state, item, mDocElementContainingBlock,
-                          computedStyle->StyleDisplay(), frameList));
+                          display, frameList));
   } else if (display->mDisplay == StyleDisplay::Flex ||
              display->mDisplay == StyleDisplay::WebkitBox ||
              display->mDisplay == StyleDisplay::Grid ||
              (StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
               display->mDisplay == StyleDisplay::MozBox)) {
     auto func = display->mDisplay == StyleDisplay::Grid
                     ? NS_NewGridContainerFrame
                     : NS_NewFlexContainerFrame;
@@ -2393,17 +2395,26 @@ nsIFrame* nsCSSFrameConstructor::Constru
     // Use a null PendingBinding, since our binding is not in fact pending.
     static const FrameConstructionData rootTableData = FCDATA_DECL(0, nullptr);
     AutoFrameConstructionItem item(this, &rootTableData, aDocElement, nullptr,
                                    do_AddRef(computedStyle), true);
 
     // if the document is a table then just populate it.
     contentFrame = static_cast<nsContainerFrame*>(
         ConstructTable(state, item, mDocElementContainingBlock,
-                       computedStyle->StyleDisplay(), frameList));
+                       display, frameList));
+  } else if (display->DisplayInside() == StyleDisplayInside::Ruby) {
+    static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructBlockRubyFrame);
+    AutoFrameConstructionItem item(this, &data, aDocElement, nullptr,
+                                   do_AddRef(computedStyle), true);
+    contentFrame = static_cast<nsContainerFrame*>(
+        ConstructBlockRubyFrame(state, item,
+            state.GetGeometricParent(*display, mDocElementContainingBlock),
+            display, frameList));
   } else {
     MOZ_ASSERT(display->mDisplay == StyleDisplay::Block ||
                    display->mDisplay == StyleDisplay::FlowRoot,
                "Unhandled display type for root element");
     contentFrame = NS_NewBlockFormattingContext(mPresShell, computedStyle);
     // Use a null PendingBinding, since our binding is not in fact pending.
     ConstructBlock(
         state, aDocElement,
@@ -3152,35 +3163,72 @@ nsIFrame* nsCSSFrameConstructor::Constru
   }
 
   // Build a scroll frame to wrap details frame if necessary.
   return ConstructScrollableBlockWithConstructor(aState, aItem, aParentFrame,
                                                  aStyleDisplay, aFrameList,
                                                  NS_NewDetailsFrame);
 }
 
+nsIFrame* nsCSSFrameConstructor::ConstructBlockRubyFrame(
+    nsFrameConstructorState& aState, FrameConstructionItem& aItem,
+    nsContainerFrame* aParentFrame, const nsStyleDisplay* aStyleDisplay,
+    nsFrameList& aFrameList) {
+  nsIContent* const content = aItem.mContent;
+  ComputedStyle* const computedStyle = aItem.mComputedStyle;
+
+  nsBlockFrame* blockFrame = NS_NewBlockFrame(mPresShell, computedStyle);
+  nsContainerFrame* newFrame = blockFrame;
+  if ((aItem.mFCData->mBits & FCDATA_MAY_NEED_SCROLLFRAME) &&
+      aStyleDisplay->IsScrollableOverflow()) {
+    nsContainerFrame* geometricParent =
+        aState.GetGeometricParent(*aStyleDisplay, aParentFrame);
+    nsContainerFrame* scrollframe = nullptr;
+    BuildScrollFrame(aState, content, computedStyle, blockFrame,
+                     geometricParent, scrollframe);
+    newFrame = scrollframe;
+  } else {
+    InitAndRestoreFrame(aState, content, aParentFrame, blockFrame);
+  }
+
+  RefPtr<ComputedStyle> rubyStyle =
+      mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
+          PseudoStyleType::blockRubyContent, computedStyle);
+  nsContainerFrame* rubyFrame = NS_NewRubyFrame(mPresShell, rubyStyle);
+  InitAndRestoreFrame(aState, content, blockFrame, rubyFrame);
+  SetInitialSingleChild(blockFrame, rubyFrame);
+  blockFrame->AddStateBits(NS_FRAME_OWNS_ANON_BOXES);
+
+  aState.AddChild(newFrame, aFrameList, content, aParentFrame);
+
+  if (!mRootElementFrame) {
+    // The frame we're constructing will be the root element frame.
+    SetRootElementFrameAndConstructCanvasAnonContent(newFrame, aState,
+                                                     aFrameList);
+  }
+  nsFrameList childList;
+  ProcessChildren(aState, content, rubyStyle, rubyFrame, true, childList,
+                  false, nullptr);
+  rubyFrame->SetInitialChildList(kPrincipalList, childList);
+
+  return newFrame;
+}
+
 static nsIFrame* FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame) {
   for (nsIFrame* f = aFrame->GetParent(); f; f = f->GetParent()) {
     NS_ASSERTION(f->IsGeneratedContentFrame(),
                  "should not have exited generated content");
     auto pseudo = f->Style()->GetPseudoType();
     if (pseudo == PseudoStyleType::before || pseudo == PseudoStyleType::after ||
         pseudo == PseudoStyleType::marker)
       return f;
   }
   return nullptr;
 }
 
-#define SIMPLE_FCDATA(_func) FCDATA_DECL(0, _func)
-#define FULL_CTOR_FCDATA(_flags, _func)                  \
-  {                                                      \
-    _flags | FCDATA_FUNC_IS_FULL_CTOR, {nullptr}, _func, \
-        PseudoStyleType::NotPseudo                       \
-  }
-
 /* static */
 const nsCSSFrameConstructor::FrameConstructionData*
 nsCSSFrameConstructor::FindTextData(const Text& aTextContent,
                                     nsIFrame* aParentFrame) {
   if (aParentFrame && IsFrameForSVG(aParentFrame)) {
     nsIFrame* ancestorFrame =
         nsSVGUtils::GetFirstNonAAncestorFrame(aParentFrame);
     if (!ancestorFrame || !nsSVGUtils::IsInSVGTextSubtree(ancestorFrame)) {
@@ -3537,17 +3585,16 @@ void nsCSSFrameConstructor::ConstructFra
 
   // Some sets of bits are not compatible with each other
 #define CHECK_ONLY_ONE_BIT(_bit1, _bit2)           \
   NS_ASSERTION(!(bits & _bit1) || !(bits & _bit2), \
                "Only one of these bits should be set")
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
                      FCDATA_FORCE_NULL_ABSPOS_CONTAINER);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_WRAP_KIDS_IN_BLOCKS);
-  CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_MAY_NEED_SCROLLFRAME);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_IS_POPUP);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_SKIP_ABSPOS_PUSH);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
                      FCDATA_DISALLOW_GENERATED_CONTENT);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR, FCDATA_ALLOW_BLOCK_STYLES);
   CHECK_ONLY_ONE_BIT(FCDATA_FUNC_IS_FULL_CTOR,
                      FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS);
   CHECK_ONLY_ONE_BIT(FCDATA_WRAP_KIDS_IN_BLOCKS,
@@ -4009,22 +4056,16 @@ static bool IsXULDisplayType(const nsSty
 // (This is the same as SCROLLABLE_XUL_FCDATA, but w/o FCDATA_SKIP_ABSPOS_PUSH)
 #define SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func) \
   FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_MAY_NEED_SCROLLFRAME, _func)
 
 #define SIMPLE_XUL_CREATE(_tag, _func) \
   { nsGkAtoms::_tag, SIMPLE_XUL_FCDATA(_func) }
 #define SCROLLABLE_XUL_CREATE(_tag, _func) \
   { nsGkAtoms::_tag, SCROLLABLE_XUL_FCDATA(_func) }
-#define SIMPLE_XUL_DISPLAY_CREATE(_display, _func) \
-  FCDATA_FOR_DISPLAY(_display, SIMPLE_XUL_FCDATA(_func))
-#define SCROLLABLE_XUL_DISPLAY_CREATE(_display, _func) \
-  FCDATA_FOR_DISPLAY(_display, SCROLLABLE_XUL_FCDATA(_func))
-#define SCROLLABLE_ABSPOS_CONTAINER_XUL_DISPLAY_CREATE(_display, _func) \
-  FCDATA_FOR_DISPLAY(_display, SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(_func))
 
 static nsIFrame* NS_NewGridBoxFrame(PresShell* aPresShell,
                                     ComputedStyle* aComputedStyle) {
   nsCOMPtr<nsBoxLayout> layout;
   NS_NewGridLayout2(getter_AddRefs(layout));
   return NS_NewBoxFrame(aPresShell, aComputedStyle, false, layout);
 }
 
@@ -4171,79 +4212,16 @@ nsCSSFrameConstructor::FindXULMenubarDat
   static const FrameConstructionData sMenubarData =
       SIMPLE_XUL_FCDATA(NS_NewMenuBarFrame);
   return &sMenubarData;
 }
 #  endif /* XP_MACOSX */
 
 #endif /* MOZ_XUL */
 
-/* static */
-const nsCSSFrameConstructor::FrameConstructionData*
-nsCSSFrameConstructor::FindXULDisplayData(const nsStyleDisplay& aDisplay,
-                                          const Element& aElement) {
-  static const FrameConstructionDataByDisplay sXULDisplayData[] = {
-      SCROLLABLE_ABSPOS_CONTAINER_XUL_DISPLAY_CREATE(StyleDisplay::MozBox,
-                                                     NS_NewBoxFrame),
-      SCROLLABLE_ABSPOS_CONTAINER_XUL_DISPLAY_CREATE(StyleDisplay::MozInlineBox,
-                                                     NS_NewBoxFrame),
-#ifdef MOZ_XUL
-      SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozGrid, NS_NewGridBoxFrame),
-      SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozInlineGrid,
-                                    NS_NewGridBoxFrame),
-      SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozGridGroup,
-                                    NS_NewGridRowGroupFrame),
-      SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozGridLine,
-                                    NS_NewGridRowLeafFrame),
-      SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozStack, NS_NewStackFrame),
-      SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozInlineStack,
-                                    NS_NewStackFrame),
-      SIMPLE_XUL_DISPLAY_CREATE(StyleDisplay::MozDeck, NS_NewDeckFrame),
-      SCROLLABLE_XUL_DISPLAY_CREATE(StyleDisplay::MozGroupbox,
-                                    NS_NewGroupBoxFrame),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::MozPopup,
-          FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |
-                          FCDATA_SKIP_ABSPOS_PUSH,
-                      NS_NewMenuPopupFrame))
-#endif /* MOZ_XUL */
-  };
-
-  if (aDisplay.mDisplay < StyleDisplay::MozBox) {
-    return nullptr;
-  }
-
-  MOZ_ASSERT(aDisplay.mDisplay <= StyleDisplay::MozPopup,
-             "Someone added a new display value?");
-
-  if (aDisplay.mDisplay == StyleDisplay::MozBox ||
-      aDisplay.mDisplay == StyleDisplay::MozInlineBox) {
-    if (!aElement.IsInNativeAnonymousSubtree() &&
-        aElement.OwnerDoc()->IsContentDocument()) {
-      aElement.OwnerDoc()->WarnOnceAbout(Document::eMozBoxOrInlineBoxDisplay);
-    }
-
-    // If we're emulating -moz-box with flexbox, then treat it as non-XUL and
-    // return null (except for scrollcorners which have to be XUL becuase their
-    // parent reflows them with BoxReflow() which means they have to get
-    // actual-XUL frames).
-    if (StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
-        !aElement.IsXULElement(nsGkAtoms::scrollcorner)) {
-      return nullptr;
-    }
-  }
-
-  const FrameConstructionDataByDisplay& data =
-      sXULDisplayData[size_t(aDisplay.mDisplay) - size_t(StyleDisplay::MozBox)];
-  MOZ_ASSERT(aDisplay.mDisplay == data.mDisplay,
-             "Did someone mess with the order?");
-
-  return &data.mData;
-}
-
 already_AddRefed<ComputedStyle> nsCSSFrameConstructor::BeginBuildingScrollFrame(
     nsFrameConstructorState& aState, nsIContent* aContent,
     ComputedStyle* aContentStyle, nsContainerFrame* aParentFrame,
     PseudoStyleType aScrolledPseudo, bool aIsRoot,
     nsContainerFrame*& aNewFrame) {
   nsContainerFrame* gfxScrollFrame = aNewFrame;
 
   nsFrameList anonymousList;
@@ -4366,238 +4344,270 @@ nsCSSFrameConstructor::FindDisplayData(c
                                        const Element& aElement) {
   static_assert(eParentTypeCount < (1 << (32 - FCDATA_PARENT_TYPE_OFFSET)),
                 "Check eParentTypeCount should not overflow");
 
   // The style system ensures that floated and positioned frames are
   // block-level.
   NS_ASSERTION(
       !(aDisplay.IsFloatingStyle() || aDisplay.IsAbsolutelyPositionedStyle()) ||
-          aDisplay.IsBlockOutsideStyle() ||
-          aDisplay.mDisplay == StyleDisplay::Contents,
+          aDisplay.IsBlockOutsideStyle() || IsXULDisplayType(&aDisplay),
       "Style system did not apply CSS2.1 section 9.7 fixups");
 
   // If this is "body", try propagating its scroll style to the viewport
   // Note that we need to do this even if the body is NOT scrollable;
   // it might have dynamically changed from scrollable to not scrollable,
   // and that might need to be propagated.
   // XXXbz is this the right place to do this?  If this code moves,
   // make this function static.
   bool propagatedScrollToViewport = false;
   if (aElement.IsHTMLElement(nsGkAtoms::body)) {
     if (nsPresContext* presContext = mPresShell->GetPresContext()) {
       propagatedScrollToViewport =
           presContext->UpdateViewportScrollStylesOverride() == &aElement;
-    }
-  }
-
-  NS_ASSERTION(!propagatedScrollToViewport ||
-                   !mPresShell->GetPresContext()->IsPaginated(),
-               "Shouldn't propagate scroll in paginated contexts");
-
-  if (aDisplay.IsBlockInsideStyle()) {
-    // If the frame is a block-level frame and is scrollable, then wrap it in a
-    // scroll frame.  Except we don't want to do that for paginated contexts for
-    // frames that are block-outside and aren't frames for native anonymous
-    // stuff.
-    // XXX Ignore tables for the time being (except caption)
-    const uint32_t kCaptionCtorFlags =
-        FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable);
-    bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption;
-    bool suppressScrollFrame = false;
-    bool needScrollFrame =
-        aDisplay.IsScrollableOverflow() && !propagatedScrollToViewport;
-    if (needScrollFrame) {
-      suppressScrollFrame = mPresShell->GetPresContext()->IsPaginated() &&
-                            aDisplay.IsBlockOutsideStyle() &&
-                            !aElement.IsInNativeAnonymousSubtree();
-      if (!suppressScrollFrame) {
-        static const FrameConstructionData sScrollableBlockData[2] = {
-            FULL_CTOR_FCDATA(0,
-                             &nsCSSFrameConstructor::ConstructScrollableBlock),
-            FULL_CTOR_FCDATA(kCaptionCtorFlags,
-                             &nsCSSFrameConstructor::ConstructScrollableBlock)};
-        return &sScrollableBlockData[caption];
-      }
-
-      // If the scrollable frame would have propagated its scrolling to the
-      // viewport, we still want to construct a regular block rather than a
-      // scrollframe so that it paginates correctly, but we don't want to set
-      // the bit on the block that tells it to clip at paint time.
-      if (mPresShell->GetPresContext()->ElementWouldPropagateScrollStyles(
-              aElement)) {
-        suppressScrollFrame = false;
-      }
-    }
-
-    // Handle various non-scrollable blocks.
-    static const FrameConstructionData sNonScrollableBlockData[2][2] = {
-        {FULL_CTOR_FCDATA(0,
-                          &nsCSSFrameConstructor::ConstructNonScrollableBlock),
-         FULL_CTOR_FCDATA(kCaptionCtorFlags,
-                          &nsCSSFrameConstructor::ConstructNonScrollableBlock)},
-        {FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
-                          &nsCSSFrameConstructor::ConstructNonScrollableBlock),
-         FULL_CTOR_FCDATA(
-             FCDATA_FORCED_NON_SCROLLABLE_BLOCK | kCaptionCtorFlags,
-             &nsCSSFrameConstructor::ConstructNonScrollableBlock)}};
-    return &sNonScrollableBlockData[suppressScrollFrame][caption];
-  }
-
-  // If this is for a <body> node and we've propagated the scroll-frame to the
-  // viewport, we need to make sure not to add another layer of scrollbars, so
-  // we use a different FCData struct without FCDATA_MAY_NEED_SCROLLFRAME.
-  if (propagatedScrollToViewport && aDisplay.IsScrollableOverflow()) {
-    if (aDisplay.mDisplay == StyleDisplay::Flex ||
-        aDisplay.mDisplay == StyleDisplay::WebkitBox ||
-        (StaticPrefs::layout_css_emulate_moz_box_with_flex() &&
-         aDisplay.mDisplay == StyleDisplay::MozBox)) {
-      static const FrameConstructionData sNonScrollableFlexData =
-          FCDATA_DECL(0, NS_NewFlexContainerFrame);
-      return &sNonScrollableFlexData;
-    }
-    if (aDisplay.mDisplay == StyleDisplay::Grid) {
-      static const FrameConstructionData sNonScrollableGridData =
-          FCDATA_DECL(0, NS_NewGridContainerFrame);
-      return &sNonScrollableGridData;
-    }
-  }
-
-  // NOTE: Make sure to keep this up to date with the StyleDisplay definition!
-  static const FrameConstructionDataByDisplay sDisplayData[] = {
-      FCDATA_FOR_DISPLAY(StyleDisplay::None, UNREACHABLE_FCDATA()),
-      FCDATA_FOR_DISPLAY(StyleDisplay::Block, UNREACHABLE_FCDATA()),
-      FCDATA_FOR_DISPLAY(StyleDisplay::FlowRoot, UNREACHABLE_FCDATA()),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::Inline,
-          FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
-                           &nsCSSFrameConstructor::ConstructInline)),
-      FCDATA_FOR_DISPLAY(StyleDisplay::InlineBlock, UNREACHABLE_FCDATA()),
-      FCDATA_FOR_DISPLAY(StyleDisplay::ListItem, UNREACHABLE_FCDATA()),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::Table,
-          FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::InlineTable,
-          FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable)),
-      // NOTE: In the unlikely event that we add another table-part here that
-      // has a desired-parent-type (& hence triggers table fixup), we'll need to
-      // also update the flexbox chunk in ComputedStyle::ApplyStyleFixups().
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::TableRowGroup,
-          FULL_CTOR_FCDATA(
-              FCDATA_IS_TABLE_PART |
-                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
-              &nsCSSFrameConstructor::ConstructTableRowOrRowGroup)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::TableColumn,
-          FULL_CTOR_FCDATA(
-              FCDATA_IS_TABLE_PART |
-                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
-              &nsCSSFrameConstructor::ConstructTableCol)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::TableColumnGroup,
-          FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
-                          FCDATA_SKIP_ABSPOS_PUSH |
-                          FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
-                      NS_NewTableColGroupFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::TableHeaderGroup,
-          FULL_CTOR_FCDATA(
-              FCDATA_IS_TABLE_PART |
-                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
-              &nsCSSFrameConstructor::ConstructTableRowOrRowGroup)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::TableFooterGroup,
-          FULL_CTOR_FCDATA(
-              FCDATA_IS_TABLE_PART |
-                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
-              &nsCSSFrameConstructor::ConstructTableRowOrRowGroup)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::TableRow,
-          FULL_CTOR_FCDATA(
-              FCDATA_IS_TABLE_PART |
-                  FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
-              &nsCSSFrameConstructor::ConstructTableRowOrRowGroup)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::TableCell,
-          FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
-                               FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
-                           &nsCSSFrameConstructor::ConstructTableCell)),
-      FCDATA_FOR_DISPLAY(StyleDisplay::TableCaption, UNREACHABLE_FCDATA()),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::Flex,
-          FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::InlineFlex,
-          FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::Grid,
-          FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::InlineGrid,
-          FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::Ruby,
-          FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewRubyFrame)),
-      FCDATA_FOR_DISPLAY(StyleDisplay::RubyBase,
-                         FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
-                                         FCDATA_DESIRED_PARENT_TYPE_TO_BITS(
-                                             eTypeRubyBaseContainer),
-                                     NS_NewRubyBaseFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::RubyBaseContainer,
-          FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
-                          FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
-                      NS_NewRubyBaseContainerFrame)),
-      FCDATA_FOR_DISPLAY(StyleDisplay::RubyText,
-                         FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
-                                         FCDATA_DESIRED_PARENT_TYPE_TO_BITS(
-                                             eTypeRubyTextContainer),
-                                     NS_NewRubyTextFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::RubyTextContainer,
-          FCDATA_DECL(FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
-                      NS_NewRubyTextContainerFrame)),
-      FCDATA_FOR_DISPLAY(StyleDisplay::Contents, UNREACHABLE_FCDATA()),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::WebkitBox,
-          FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::WebkitInlineBox,
-          FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::MozBox,
-          FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
-      FCDATA_FOR_DISPLAY(
-          StyleDisplay::MozInlineBox,
-          FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame)),
-  };
-  static_assert(
-      ArrayLength(sDisplayData) == size_t(StyleDisplay::MozInlineBox) + 1,
-      "Be sure to update sDisplayData if you touch StyleDisplay");
-  MOZ_ASSERT(StaticPrefs::layout_css_emulate_moz_box_with_flex() ||
-                 (aDisplay.mDisplay != StyleDisplay::MozBox &&
-                  aDisplay.mDisplay != StyleDisplay::MozInlineBox),
-             "-moz-{inline-}box as XUL should have already been handled");
-  MOZ_ASSERT(size_t(aDisplay.mDisplay) < ArrayLength(sDisplayData),
-             "XUL display data should have already been handled");
-
-  // See the mDisplay fixup code in StyleAdjuster::adjust.
-  MOZ_ASSERT(aDisplay.mDisplay != StyleDisplay::Contents ||
-                 !aElement.IsRootOfNativeAnonymousSubtree(),
-             "display:contents on anonymous content is unsupported");
-
-  const FrameConstructionDataByDisplay& data =
-      sDisplayData[size_t(aDisplay.mDisplay)];
-
-  MOZ_ASSERT(data.mDisplay == aDisplay.mDisplay,
-             "Someone messed up the order in the display values");
-
-  return &data.mData;
+      MOZ_ASSERT(!propagatedScrollToViewport ||
+                     !mPresShell->GetPresContext()->IsPaginated(),
+                 "Shouldn't propagate scroll in paginated contexts");
+    }
+  }
+
+  switch (aDisplay.DisplayInside()) {
+    case StyleDisplayInside::Block:
+    case StyleDisplayInside::FlowRoot: {
+      // If the frame is a block-level frame and is scrollable, then wrap it in a
+      // scroll frame.  Except we don't want to do that for paginated contexts for
+      // frames that are block-outside and aren't frames for native anonymous
+      // stuff.
+      // XXX Ignore tables for the time being (except caption)
+      const uint32_t kCaptionCtorFlags =
+          FCDATA_IS_TABLE_PART | FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable);
+      bool caption = aDisplay.mDisplay == StyleDisplay::TableCaption;
+      bool suppressScrollFrame = false;
+      bool needScrollFrame =
+          aDisplay.IsScrollableOverflow() && !propagatedScrollToViewport;
+      if (needScrollFrame) {
+        suppressScrollFrame = mPresShell->GetPresContext()->IsPaginated() &&
+                              aDisplay.IsBlockOutsideStyle() &&
+                              !aElement.IsInNativeAnonymousSubtree();
+        if (!suppressScrollFrame) {
+          static const FrameConstructionData sScrollableBlockData[2] = {
+              FULL_CTOR_FCDATA(0,
+                               &nsCSSFrameConstructor::ConstructScrollableBlock),
+              FULL_CTOR_FCDATA(kCaptionCtorFlags,
+                               &nsCSSFrameConstructor::ConstructScrollableBlock)};
+          return &sScrollableBlockData[caption];
+        }
+
+        // If the scrollable frame would have propagated its scrolling to the
+        // viewport, we still want to construct a regular block rather than a
+        // scrollframe so that it paginates correctly, but we don't want to set
+        // the bit on the block that tells it to clip at paint time.
+        if (mPresShell->GetPresContext()->ElementWouldPropagateScrollStyles(
+                aElement)) {
+          suppressScrollFrame = false;
+        }
+      }
+
+      // Handle various non-scrollable blocks.
+      static const FrameConstructionData sNonScrollableBlockData[2][2] = {
+          {FULL_CTOR_FCDATA(0,
+                            &nsCSSFrameConstructor::ConstructNonScrollableBlock),
+           FULL_CTOR_FCDATA(kCaptionCtorFlags,
+                            &nsCSSFrameConstructor::ConstructNonScrollableBlock)},
+          {FULL_CTOR_FCDATA(FCDATA_FORCED_NON_SCROLLABLE_BLOCK,
+                            &nsCSSFrameConstructor::ConstructNonScrollableBlock),
+           FULL_CTOR_FCDATA(
+               FCDATA_FORCED_NON_SCROLLABLE_BLOCK | kCaptionCtorFlags,
+               &nsCSSFrameConstructor::ConstructNonScrollableBlock)}};
+      return &sNonScrollableBlockData[suppressScrollFrame][caption];
+    }
+    case StyleDisplayInside::Inline: {
+      static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(FCDATA_IS_INLINE | FCDATA_IS_LINE_PARTICIPANT,
+                         &nsCSSFrameConstructor::ConstructInline);
+      return &data;
+    }
+    case StyleDisplayInside::Table: {
+      static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(0, &nsCSSFrameConstructor::ConstructTable);
+      return &data;
+    }
+    // NOTE: In the unlikely event that we add another table-part here that
+    // has a desired-parent-type (& hence triggers table fixup), we'll need to
+    // also update the flexbox chunk in ComputedStyle::ApplyStyleFixups().
+    case StyleDisplayInside::TableRowGroup: {
+      static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(
+            FCDATA_IS_TABLE_PART |
+                FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
+            &nsCSSFrameConstructor::ConstructTableRowOrRowGroup);
+      return &data;
+    }
+    case StyleDisplayInside::TableColumn: {
+      static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(
+            FCDATA_IS_TABLE_PART |
+                FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeColGroup),
+            &nsCSSFrameConstructor::ConstructTableCol);
+      return &data;
+    }
+    case StyleDisplayInside::TableColumnGroup: {
+      static const FrameConstructionData data =
+        FCDATA_DECL(FCDATA_IS_TABLE_PART | FCDATA_DISALLOW_OUT_OF_FLOW |
+                        FCDATA_SKIP_ABSPOS_PUSH |
+                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
+                    NS_NewTableColGroupFrame);
+      return &data;
+    }
+    case StyleDisplayInside::TableHeaderGroup: {
+      static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(
+            FCDATA_IS_TABLE_PART |
+                FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
+            &nsCSSFrameConstructor::ConstructTableRowOrRowGroup);
+      return &data;
+    }
+    case StyleDisplayInside::TableFooterGroup: {
+      static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(
+            FCDATA_IS_TABLE_PART |
+                FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeTable),
+            &nsCSSFrameConstructor::ConstructTableRowOrRowGroup);
+      return &data;
+    }
+    case StyleDisplayInside::TableRow: {
+      static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(
+            FCDATA_IS_TABLE_PART |
+                FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRowGroup),
+            &nsCSSFrameConstructor::ConstructTableRowOrRowGroup);
+      return &data;
+    }
+    case StyleDisplayInside::TableCell: {
+      static const FrameConstructionData data =
+        FULL_CTOR_FCDATA(FCDATA_IS_TABLE_PART |
+                             FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRow),
+                         &nsCSSFrameConstructor::ConstructTableCell);
+      return &data;
+    }
+    case StyleDisplayInside::MozBox:
+    case StyleDisplayInside::MozInlineBox: {
+      if (!aElement.IsInNativeAnonymousSubtree() &&
+          aElement.OwnerDoc()->IsContentDocument()) {
+        aElement.OwnerDoc()->WarnOnceAbout(Document::eMozBoxOrInlineBoxDisplay);
+      }
+
+      // If we're emulating -moz-box with flexbox, then treat it as non-XUL and
+      // fall through (except for scrollcorners which have to be XUL becuase their
+      // parent reflows them with BoxReflow() which means they have to get
+      // actual-XUL frames).
+      if (!StaticPrefs::layout_css_emulate_moz_box_with_flex() ||
+          aElement.IsXULElement(nsGkAtoms::scrollcorner)) {
+        static const FrameConstructionData data =
+          SCROLLABLE_ABSPOS_CONTAINER_XUL_FCDATA(NS_NewBoxFrame);
+        return &data;
+      }
+      MOZ_FALLTHROUGH;
+    }
+    case StyleDisplayInside::Flex:
+    case StyleDisplayInside::WebkitBox: {
+      static const FrameConstructionData nonScrollableData =
+        FCDATA_DECL(0, NS_NewFlexContainerFrame);
+      static const FrameConstructionData data =
+        FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewFlexContainerFrame);
+      return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData : &data;
+    }
+    case StyleDisplayInside::Grid: {
+      static const FrameConstructionData nonScrollableData =
+        FCDATA_DECL(0, NS_NewGridContainerFrame);
+      static const FrameConstructionData data =
+        FCDATA_DECL(FCDATA_MAY_NEED_SCROLLFRAME, NS_NewGridContainerFrame);
+      return MOZ_UNLIKELY(propagatedScrollToViewport) ? &nonScrollableData : &data;
+    }
+    case StyleDisplayInside::Ruby: {
+      static const FrameConstructionData data[] = {
+        FULL_CTOR_FCDATA(FCDATA_MAY_NEED_SCROLLFRAME,
+                         &nsCSSFrameConstructor::ConstructBlockRubyFrame),
+        FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT, NS_NewRubyFrame),
+      };
+      bool isInline = aDisplay.DisplayOutside() == StyleDisplayOutside::Inline;
+      return &data[isInline];
+    }
+    case StyleDisplayInside::RubyBase: {
+      static const FrameConstructionData data =
+        FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
+                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer),
+                    NS_NewRubyBaseFrame);
+      return &data;
+    }
+    case StyleDisplayInside::RubyBaseContainer: {
+      static const FrameConstructionData data =
+        FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
+                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
+                    NS_NewRubyBaseContainerFrame);
+      return &data;
+    }
+    case StyleDisplayInside::RubyText: {
+      static const FrameConstructionData data =
+        FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
+                        FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyTextContainer),
+                    NS_NewRubyTextFrame);
+      return &data;
+    }
+    case StyleDisplayInside::RubyTextContainer: {
+      static const FrameConstructionData data =
+        FCDATA_DECL(FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRuby),
+                    NS_NewRubyTextContainerFrame);
+      return &data;
+    }
+#ifdef MOZ_XUL
+    case StyleDisplayInside::MozGrid:
+    case StyleDisplayInside::MozInlineGrid: {
+      static const FrameConstructionData data =
+        SCROLLABLE_XUL_FCDATA(NS_NewGridBoxFrame);
+      return &data;
+    }
+    case StyleDisplayInside::MozGridGroup: {
+      static const FrameConstructionData data =
+        SCROLLABLE_XUL_FCDATA(NS_NewGridRowGroupFrame);
+      return &data;
+    }
+    case StyleDisplayInside::MozGridLine: {
+      static const FrameConstructionData data =
+        SCROLLABLE_XUL_FCDATA(NS_NewGridRowLeafFrame);
+      return &data;
+    }
+    case StyleDisplayInside::MozStack:
+    case StyleDisplayInside::MozInlineStack: {
+      static const FrameConstructionData data =
+        SCROLLABLE_XUL_FCDATA(NS_NewStackFrame);
+      return &data;
+    }
+    case StyleDisplayInside::MozDeck: {
+      static const FrameConstructionData data =
+        SIMPLE_XUL_FCDATA(NS_NewDeckFrame);
+      return &data;
+    }
+    case StyleDisplayInside::MozGroupbox: {
+      static const FrameConstructionData data =
+        SCROLLABLE_XUL_FCDATA(NS_NewGroupBoxFrame);
+      return &data;
+    }
+    case StyleDisplayInside::MozPopup: {
+      static const FrameConstructionData data =
+        FCDATA_DECL(FCDATA_DISALLOW_OUT_OF_FLOW | FCDATA_IS_POPUP |
+                        FCDATA_SKIP_ABSPOS_PUSH,
+                    NS_NewMenuPopupFrame);
+      return &data;
+    }
+#endif /* MOZ_XUL */
+    default:
+      MOZ_ASSERT_UNREACHABLE("unknown 'display' value");
+      return nullptr;
+  }
 }
 
 nsIFrame* nsCSSFrameConstructor::ConstructScrollableBlock(
     nsFrameConstructorState& aState, FrameConstructionItem& aItem,
     nsContainerFrame* aParentFrame, const nsStyleDisplay* aDisplay,
     nsFrameList& aFrameList) {
   return ConstructScrollableBlockWithConstructor(aState, aItem, aParentFrame,
                                                  aDisplay, aFrameList,
@@ -5337,20 +5347,16 @@ nsCSSFrameConstructor::FindElementData(c
   // 'display' values other than 'none' or 'contents').
   if (ShouldCreateImageFrameForContent(aElement, aStyle)) {
     static const FrameConstructionData sImgData =
         SIMPLE_FCDATA(NS_NewImageFrameForContentProperty);
     return &sImgData;
   }
 
   const auto& display = *aStyle.StyleDisplay();
-  if (auto* data = FindXULDisplayData(display, aElement)) {
-    return data;
-  }
-
   return FindDisplayData(display, aElement);
 }
 
 const nsCSSFrameConstructor::FrameConstructionData*
 nsCSSFrameConstructor::FindElementTagData(const Element& aElement,
                                           ComputedStyle& aStyle,
                                           nsIFrame* aParentFrame,
                                           uint32_t aFlags) {
@@ -5453,25 +5459,27 @@ void nsCSSFrameConstructor::AddFrameCons
     if (isGeneratedContent && !item) {
       MOZ_ASSERT(!IsDisplayContents(aContent),
                  "This would need to change if we support display: contents "
                  "in generated content");
       aContent->UnbindFromTree();
     }
   });
 
+  // 'display:none' elements never creates any frames at all.
   const nsStyleDisplay& display = *aComputedStyle->StyleDisplay();
-
-  // Pre-check for display "none" - if we find that, don't create
-  // any frame at all
   if (display.mDisplay == StyleDisplay::None) {
     return;
   }
 
   if (display.mDisplay == StyleDisplay::Contents) {
+    // See the mDisplay fixup code in StyleAdjuster::adjust.
+    MOZ_ASSERT(!aContent->AsElement()->IsRootOfNativeAnonymousSubtree(),
+               "display:contents on anonymous content is unsupported");
+
     CreateGeneratedContentItem(aState, aParentFrame, *aContent->AsElement(),
                                *aComputedStyle, PseudoStyleType::before,
                                aItems);
 
     FlattenedChildIterator iter(aContent);
     InsertionPoint insertion(aParentFrame, aContent);
     for (nsIContent* child = iter.GetNextChild(); child;
          child = iter.GetNextChild()) {
@@ -7198,25 +7206,24 @@ void nsCSSFrameConstructor::ContentRange
   nsContainerFrame* containingBlock = state.mFloatedList.containingBlock;
   bool haveFirstLetterStyle = false;
   bool haveFirstLineStyle = false;
 
   // In order to shave off some cycles, we only dig up the
   // containing block haveFirst* flags if the parent frame where
   // the insertion/append is occurring is an inline or block
   // container. For other types of containers this isn't relevant.
-  StyleDisplay parentDisplay = insertion.mParentFrame->GetDisplay();
+  StyleDisplayInside parentDisplayInside =
+    insertion.mParentFrame->StyleDisplay()->DisplayInside();
 
   // Examine the insertion.mParentFrame where the insertion is taking
   // place. If it's a certain kind of container then some special
   // processing is done.
-  if ((StyleDisplay::Block == parentDisplay) ||
-      (StyleDisplay::ListItem == parentDisplay) ||
-      (StyleDisplay::Inline == parentDisplay) ||
-      (StyleDisplay::InlineBlock == parentDisplay)) {
+  if (StyleDisplayInside::Block == parentDisplayInside ||
+      StyleDisplayInside::Inline == parentDisplayInside) {
     // Recover the special style flags for the containing block
     if (containingBlock) {
       haveFirstLetterStyle = HasFirstLetterStyle(containingBlock);
       haveFirstLineStyle = ShouldHaveFirstLineStyle(
           containingBlock->GetContent(), containingBlock->Style());
     }
 
     if (haveFirstLetterStyle) {
@@ -9188,17 +9195,18 @@ void nsCSSFrameConstructor::CreateNeeded
     nsFrameConstructorState& aState, FrameConstructionItemList& aItems,
     nsIFrame* aParentFrame) {
   const ParentType ourParentType = GetParentType(aParentFrame);
   if (!IsRubyParentType(ourParentType) ||
       aItems.AllWantParentType(ourParentType)) {
     return;
   }
 
-  if (!IsRubyPseudo(aParentFrame)) {
+  if (!IsRubyPseudo(aParentFrame) ||
+      ourParentType == eTypeRuby /* for 'display:block ruby' */) {
     // Normally, ruby pseudo frames start from and end at some elements,
     // which means they don't have leading and trailing whitespaces at
     // all.  But there are two cases where they do actually have leading
     // or trailing whitespaces:
     // 1. It is an inter-segment whitespace which in an individual ruby
     //    base container.
     // 2. The pseudo frame starts from or ends at consecutive inline
     //    content, which is not pure whitespace, but includes some.
@@ -9415,22 +9423,24 @@ void nsCSSFrameConstructor::CreateNeeded
  * after the wrapper.
  */
 void nsCSSFrameConstructor::WrapItemsInPseudoParent(
     nsIContent* aParentContent, ComputedStyle* aParentStyle,
     ParentType aWrapperType, FCItemIterator& aIter,
     const FCItemIterator& aEndIter) {
   const PseudoParentData& pseudoData = sPseudoParentData[aWrapperType];
   PseudoStyleType pseudoType = pseudoData.mPseudoType;
-  StyleDisplay parentDisplay = aParentStyle->StyleDisplay()->mDisplay;
-
+  auto parentDisplayInside = aParentStyle->StyleDisplay()->DisplayInside();
+
+  // XXXmats should we use IsInlineInsideStyle() here instead? seems odd to
+  // exclude RubyBaseContainer/RubyTextContainer...
   if (pseudoType == PseudoStyleType::table &&
-      (parentDisplay == StyleDisplay::Inline ||
-       parentDisplay == StyleDisplay::RubyBase ||
-       parentDisplay == StyleDisplay::RubyText)) {
+      (parentDisplayInside == StyleDisplayInside::Inline ||
+       parentDisplayInside == StyleDisplayInside::RubyBase ||
+       parentDisplayInside == StyleDisplayInside::RubyText)) {
     pseudoType = PseudoStyleType::inlineTable;
   }
 
   RefPtr<ComputedStyle> wrapperStyle;
   if (pseudoData.mFCData.mBits & FCDATA_IS_WRAPPER_ANON_BOX) {
     wrapperStyle = mPresShell->StyleSet()->ResolveInheritingAnonymousBoxStyle(
         pseudoType, aParentStyle);
   } else {
@@ -9583,18 +9593,17 @@ inline void nsCSSFrameConstructor::Const
     NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
                  "Needed pseudos didn't get created; expect bad things");
     // display:list-item boxes affects the start value of the "list-item"
     // counter when an <ol reversed> element doesn't have an explicit start
     // value.
     if (!listItemListIsDirty &&
         iter.item().mComputedStyle->StyleList()->mMozListReversed ==
             StyleMozListReversed::True &&
-        iter.item().mComputedStyle->StyleDisplay()->mDisplay ==
-            StyleDisplay::ListItem) {
+        iter.item().mComputedStyle->StyleDisplay()->IsListItem()) {
       auto* list = mCounterManager.CounterListFor(nsGkAtoms::list_item);
       list->SetDirty();
       CountersDirty();
       listItemListIsDirty = true;
     }
     ConstructFramesFromItem(aState, iter, aParentFrame, aFrameList);
   }
 
@@ -9714,17 +9723,17 @@ void nsCSSFrameConstructor::ProcessChild
     // block styles because in some cases involving table pseudo-frames it has
     // nothing to do with the parent frame's desired behavior.
     ComputedStyle* computedStyle;
 
     if (aCanHaveGeneratedContent) {
       auto* styleParentFrame =
           nsFrame::CorrectStyleParentFrame(aFrame, PseudoStyleType::NotPseudo);
       computedStyle = styleParentFrame->Style();
-      if (computedStyle->StyleDisplay()->mDisplay == StyleDisplay::ListItem &&
+      if (computedStyle->StyleDisplay()->IsListItem() &&
           (listItem = do_QueryFrame(aFrame)) &&
           !styleParentFrame->IsFieldSetFrame()) {
         isOutsideMarker = computedStyle->StyleList()->mListStylePosition ==
                           NS_STYLE_LIST_STYLE_POSITION_OUTSIDE;
         CreateGeneratedContentItem(aState, aFrame, *aContent->AsElement(),
                                    *computedStyle, PseudoStyleType::marker,
                                    itemsToConstruct);
       }
@@ -11233,16 +11242,21 @@ void nsCSSFrameConstructor::BuildInlineC
   // length?  Maybe even to parentContent->GetChildCount()?
   nsFrameConstructorState::PendingBindingAutoPusher pusher(
       aState, aParentItem.mPendingBinding);
 
   ComputedStyle* const parentComputedStyle = aParentItem.mComputedStyle;
   nsIContent* const parentContent = aParentItem.mContent;
 
   if (!aItemIsWithinSVGText) {
+    if (parentComputedStyle->StyleDisplay()->IsListItem()) {
+      CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
+                                 *parentComputedStyle, PseudoStyleType::marker,
+                                 aParentItem.mChildItems);
+    }
     // Probe for generated content before
     CreateGeneratedContentItem(aState, nullptr, *parentContent->AsElement(),
                                *parentComputedStyle, PseudoStyleType::before,
                                aParentItem.mChildItems);
   }
 
   uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK;
   if (aItemIsWithinSVGText) {
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -752,24 +752,16 @@ class nsCSSFrameConstructor final : publ
 
   struct FrameConstructionDataByDisplay {
 #ifdef DEBUG
     const mozilla::StyleDisplay mDisplay;
 #endif
     const FrameConstructionData mData;
   };
 
-#ifdef DEBUG
-#  define FCDATA_FOR_DISPLAY(_display, _fcdata) \
-    { _display, _fcdata }
-#else
-#  define FCDATA_FOR_DISPLAY(_display, _fcdata) \
-    { _fcdata }
-#endif
-
   /* Structure that has a FrameConstructionData and style pseudo-type
      for a table pseudo-frame */
   struct PseudoParentData {
     const FrameConstructionData mFCData;
     mozilla::PseudoStyleType const mPseudoType;
   };
   /* Array of such structures that we use to properly construct table
      pseudo-frames as needed */
@@ -1364,16 +1356,23 @@ class nsCSSFrameConstructor final : publ
   // ConstructDetailsFrame puts the new frame in aFrameList and
   // handles the kids of the details.
   nsIFrame* ConstructDetailsFrame(nsFrameConstructorState& aState,
                                   FrameConstructionItem& aItem,
                                   nsContainerFrame* aParentFrame,
                                   const nsStyleDisplay* aStyleDisplay,
                                   nsFrameList& aFrameList);
 
+  // Creates a block frame wrapping an anonymous ruby frame.
+  nsIFrame* ConstructBlockRubyFrame(nsFrameConstructorState& aState,
+                                    FrameConstructionItem& aItem,
+                                    nsContainerFrame* aParentFrame,
+                                    const nsStyleDisplay* aStyleDisplay,
+                                    nsFrameList& aFrameList);
+
   void ConstructTextFrame(const FrameConstructionData* aData,
                           nsFrameConstructorState& aState, nsIContent* aContent,
                           nsContainerFrame* aParentFrame,
                           ComputedStyle* aComputedStyle,
                           nsFrameList& aFrameList);
 
   // If aPossibleTextContent is a text node and doesn't have a frame, append a
   // frame construction item for it to aItems.
@@ -1506,24 +1505,16 @@ class nsCSSFrameConstructor final : publ
   static const FrameConstructionData* FindXULDescriptionData(const Element&,
                                                              ComputedStyle&);
 #  ifdef XP_MACOSX
   static const FrameConstructionData* FindXULMenubarData(const Element&,
                                                          ComputedStyle&);
 #  endif /* XP_MACOSX */
 #endif   /* MOZ_XUL */
 
-  // Function to find FrameConstructionData for an element using one of the XUL
-  // display types.  Will return null if the style doesn't have a XUL display
-  // type.  This function performs no other checks, so should only be called if
-  // we know for sure that the element is not something that should get a frame
-  // constructed by tag.
-  static const FrameConstructionData* FindXULDisplayData(const nsStyleDisplay&,
-                                                         const Element&);
-
   /**
    * Constructs an outer frame, an anonymous child that wraps its real
    * children, and its descendant frames.  This is used by both
    * ConstructOuterSVG and ConstructMarker, which both want an anonymous block
    * child for their children to go in to.
    */
   nsContainerFrame* ConstructFrameWithAnonymousChild(
       nsFrameConstructorState& aState, FrameConstructionItem& aItem,
--- a/layout/base/nsCounterManager.cpp
+++ b/layout/base/nsCounterManager.cpp
@@ -198,17 +198,17 @@ bool nsCounterManager::AddCounterChanges
   // 'counter-increment:list-item' unless 'counter-increment' already has a
   // value for 'list-item'.
   //
   // https://drafts.csswg.org/css-lists-3/#declaring-a-list-item
   //
   // We inherit `display` for some anonymous boxes, but we don't want them to
   // increment the list-item counter.
   const bool requiresListItemIncrement =
-      aFrame->StyleDisplay()->mDisplay == StyleDisplay::ListItem &&
+      aFrame->StyleDisplay()->IsListItem() &&
       !aFrame->Style()->IsAnonBox();
 
   const nsStyleContent* styleContent = aFrame->StyleContent();
 
   if (!requiresListItemIncrement && !HasCounters(*styleContent)) {
     MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE));
     return false;
   }
--- a/layout/generic/ReflowInput.cpp
+++ b/layout/generic/ReflowInput.cpp
@@ -889,56 +889,45 @@ void ReflowInput::InitFrameType(LayoutFr
     } else if (disp->IsFloating(mFrame)) {
       frameType = NS_CSS_FRAME_TYPE_FLOATING;
     } else {
       NS_ASSERTION(disp->mDisplay == StyleDisplay::MozPopup,
                    "unknown out of flow frame type");
       frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
     }
   } else {
-    switch (GetDisplay()) {
-      case StyleDisplay::Block:
-      case StyleDisplay::ListItem:
-      case StyleDisplay::Table:
-      case StyleDisplay::TableCaption:
-      case StyleDisplay::Flex:
-      case StyleDisplay::WebkitBox:
-      case StyleDisplay::Grid:
-      case StyleDisplay::FlowRoot:
-      case StyleDisplay::RubyTextContainer:
+    switch (disp->DisplayOutside()) {
+      case StyleDisplayOutside::Block:
+      case StyleDisplayOutside::TableCaption:
         frameType = NS_CSS_FRAME_TYPE_BLOCK;
         break;
 
-      case StyleDisplay::Inline:
-      case StyleDisplay::InlineBlock:
-      case StyleDisplay::InlineTable:
-      case StyleDisplay::MozInlineBox:
-      case StyleDisplay::MozInlineGrid:
-      case StyleDisplay::MozInlineStack:
-      case StyleDisplay::InlineFlex:
-      case StyleDisplay::WebkitInlineBox:
-      case StyleDisplay::InlineGrid:
-      case StyleDisplay::Ruby:
-      case StyleDisplay::RubyBase:
-      case StyleDisplay::RubyText:
-      case StyleDisplay::RubyBaseContainer:
+      case StyleDisplayOutside::Inline:
         frameType = NS_CSS_FRAME_TYPE_INLINE;
         break;
 
-      case StyleDisplay::TableCell:
-      case StyleDisplay::TableRowGroup:
-      case StyleDisplay::TableColumn:
-      case StyleDisplay::TableColumnGroup:
-      case StyleDisplay::TableHeaderGroup:
-      case StyleDisplay::TableFooterGroup:
-      case StyleDisplay::TableRow:
+      case StyleDisplayOutside::InternalTable:
         frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
         break;
 
-      case StyleDisplay::None:
+      case StyleDisplayOutside::InternalRuby:
+        switch (disp->DisplayInside()) {
+          case StyleDisplayInside::RubyTextContainer:
+            frameType = NS_CSS_FRAME_TYPE_BLOCK;
+            break;
+          case StyleDisplayInside::RubyBase:
+          case StyleDisplayInside::RubyText:
+          case StyleDisplayInside::RubyBaseContainer:
+            frameType = NS_CSS_FRAME_TYPE_INLINE;
+            break;
+          default:
+            MOZ_ASSERT_UNREACHABLE("unexpected inside for InternalRuby");
+        }
+        break;
+
       default:
         frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
         break;
     }
   }
 
   // See if the frame is replaced
   if (mFrame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
--- a/layout/generic/RubyUtils.h
+++ b/layout/generic/RubyUtils.h
@@ -70,17 +70,18 @@ class RubyUtils {
   }
 
   static inline bool IsExpandableRubyBox(nsIFrame* aFrame) {
     mozilla::LayoutFrameType type = aFrame->Type();
     return IsRubyContentBox(type) || IsRubyContainerBox(type);
   }
 
   static inline bool IsRubyPseudo(PseudoStyleType aPseudo) {
-    return aPseudo == PseudoStyleType::ruby ||
+    return aPseudo == PseudoStyleType::blockRubyContent ||
+           aPseudo == PseudoStyleType::ruby ||
            aPseudo == PseudoStyleType::rubyBase ||
            aPseudo == PseudoStyleType::rubyText ||
            aPseudo == PseudoStyleType::rubyBaseContainer ||
            aPseudo == PseudoStyleType::rubyTextContainer;
   }
 
   static void SetReservedISize(nsIFrame* aFrame, nscoord aISize);
   static void ClearReservedISize(nsIFrame* aFrame);
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -19,17 +19,16 @@
 #include "mozilla/PresShell.h"
 #include "mozilla/ToString.h"
 #include "mozilla/UniquePtr.h"
 
 #include "nsCOMPtr.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "nsBlockReflowContext.h"
 #include "BlockReflowInput.h"
-#include "nsBulletFrame.h"
 #include "nsFontMetrics.h"
 #include "nsGenericHTMLElement.h"
 #include "nsLineBox.h"
 #include "nsLineLayout.h"
 #include "nsPlaceholderFrame.h"
 #include "nsStyleConsts.h"
 #include "nsFrameManager.h"
 #include "nsPresContext.h"
@@ -61,18 +60,16 @@
 #include "nsFlexContainerFrame.h"
 
 #include "nsBidiPresUtils.h"
 
 #include <inttypes.h>
 
 static const int MIN_LINES_NEEDING_CURSOR = 20;
 
-static const char16_t kDiscCharacter = 0x2022;
-
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 using namespace mozilla::layout;
 using ShapeType = nsFloatManager::ShapeType;
 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
 
 static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock) {
@@ -5617,16 +5614,40 @@ void nsBlockFrame::AddFrames(nsFrameList
   }
 
 #ifdef DEBUG
   MOZ_ASSERT(aFrameList.IsEmpty());
   VerifyLines(true);
 #endif
 }
 
+nsContainerFrame* nsBlockFrame::GetRubyContentPseudoFrame() {
+  auto* firstChild = PrincipalChildList().FirstChild();
+  if (firstChild && firstChild->IsRubyFrame() &&
+      firstChild->Style()->GetPseudoType() == mozilla::PseudoStyleType::blockRubyContent) {
+    return static_cast<nsContainerFrame*>(firstChild);
+  }
+  return nullptr;
+}
+
+nsContainerFrame* nsBlockFrame::GetContentInsertionFrame() {
+  // 'display:block ruby' use the inner (Ruby) frame for insertions.
+  if (auto* rubyContentPseudoFrame = GetRubyContentPseudoFrame()) {
+    return rubyContentPseudoFrame;
+  }
+  return this;
+}
+
+void nsBlockFrame::AppendDirectlyOwnedAnonBoxes(
+    nsTArray<OwnedAnonBox>& aResult) {
+  if (auto* rubyContentPseudoFrame = GetRubyContentPseudoFrame()) {
+    aResult.AppendElement(OwnedAnonBox(rubyContentPseudoFrame));
+  }
+}
+
 void nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat) {
   // Find which line contains the float, so we can update
   // the float cache.
   LineIterator line = LinesBegin(), line_end = LinesEnd();
   for (; line != line_end; ++line) {
     if (line->IsInline() && line->RemoveFloat(aFloat)) {
       break;
     }
@@ -7100,50 +7121,25 @@ void nsBlockFrame::SetMarkerFrameForList
   } else {
     SetProperty(OutsideMarkerProperty(),
                 new (PresShell()) nsFrameList(aMarkerFrame, aMarkerFrame));
     AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_MARKER);
   }
 }
 
 bool nsBlockFrame::MarkerIsEmpty() const {
-  NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay ==
-                       mozilla::StyleDisplay::ListItem &&
+  NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->IsListItem() &&
                    HasOutsideMarker(),
                "should only care when we have an outside ::marker");
   nsIFrame* marker = GetMarker();
   const nsStyleList* list = marker->StyleList();
   return list->mCounterStyle.IsNone() && !list->GetListStyleImage() &&
          marker->StyleContent()->ContentCount() == 0;
 }
 
-#ifdef ACCESSIBILITY
-void nsBlockFrame::GetSpokenMarkerText(nsAString& aText) const {
-  const nsStyleList* myList = StyleList();
-  if (myList->GetListStyleImage()) {
-    aText.Assign(kDiscCharacter);
-    aText.Append(' ');
-  } else {
-    if (nsIFrame* marker = GetMarker()) {
-      if (nsBulletFrame* bullet = do_QueryFrame(marker)) {
-        bullet->GetSpokenText(aText);
-      } else {
-        ErrorResult err;
-        marker->GetContent()->GetTextContent(aText, err);
-        if (err.Failed()) {
-          aText.Truncate();
-        }
-      }
-    } else {
-      aText.Truncate();
-    }
-  }
-}
-#endif
-
 void nsBlockFrame::ReflowOutsideMarker(nsIFrame* aMarkerFrame,
                                        BlockReflowInput& aState,
                                        ReflowOutput& aMetrics,
                                        nscoord aLineTop) {
   const ReflowInput& ri = aState.mReflowInput;
 
   WritingMode markerWM = aMarkerFrame->GetWritingMode();
   LogicalSize availSize(markerWM);
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -113,16 +113,18 @@ class nsBlockFrame : public nsContainerF
             nsIFrame* aPrevInFlow) override;
   void SetInitialChildList(ChildListID aListID,
                            nsFrameList& aChildList) override;
   void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override;
   void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
                     const nsLineList::iterator* aPrevFrameLine,
                     nsFrameList& aFrameList) override;
   void RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) override;
+  nsContainerFrame* GetContentInsertionFrame() override;
+  void AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) override;
   const nsFrameList& GetChildList(ChildListID aListID) const override;
   void GetChildLists(nsTArray<ChildList>* aLists) const override;
   nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
   bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
                                 nscoord* aBaseline) const override {
     NS_ASSERTION(!aWM.IsOrthogonalTo(GetWritingMode()),
                  "You should only call this on frames with a WM that's "
                  "parallel to aWM");
@@ -231,23 +233,16 @@ class nsBlockFrame : public nsContainerF
   bool CachedIsEmpty() override;
   bool IsSelfEmpty() override;
 
   // Given that we have a ::marker frame, does it actually draw something, i.e.,
   // do we have either a 'list-style-type' or 'list-style-image' that is
   // not 'none', and no 'content'?
   bool MarkerIsEmpty() const;
 
-#ifdef ACCESSIBILITY
-  /**
-   * Return the ::marker text equivalent, without flushing.
-   */
-  void GetSpokenMarkerText(nsAString& aText) const;
-#endif
-
   /**
    * Return true if this frame has a ::marker frame.
    */
   bool HasMarker() const { return HasOutsideMarker() || HasInsideMarker(); }
 
   /**
    * @return true if this frame has an inside ::marker frame.
    */
@@ -487,16 +482,20 @@ class nsBlockFrame : public nsContainerF
    * This function will clear aFrameList.
    *
    * aPrevSiblingLine, if present, must be the line containing aPrevSibling.
    * Providing it will make this function faster.
    */
   void AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling,
                  const nsLineList::iterator* aPrevSiblingLine);
 
+  // Return the :-moz-block-ruby-content child frame, if any.
+  // (It's non-null only if this block frame is for 'display:block ruby'.)
+  nsContainerFrame* GetRubyContentPseudoFrame();
+
   /**
    * Perform Bidi resolution on this frame
    */
   nsresult ResolveBidi();
 
   /**
    * Test whether the frame is a form control in a visual Bidi page.
    * This is necessary for backwards-compatibility, because most visual
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -1725,16 +1725,42 @@ uint16_t nsContainerFrame::CSSAlignmentF
       "Child classes that use css box alignment for abspos children "
       "should provide their own implementation of this method!");
 
   // In the unexpected/unlikely event that this implementation gets invoked,
   // just use "start" alignment.
   return NS_STYLE_ALIGN_START;
 }
 
+#ifdef ACCESSIBILITY
+void nsContainerFrame::GetSpokenMarkerText(nsAString& aText) const {
+  const nsStyleList* myList = StyleList();
+  if (myList->GetListStyleImage()) {
+    char16_t kDiscCharacter = 0x2022;
+    aText.Assign(kDiscCharacter);
+    aText.Append(' ');
+  } else {
+    nsIContent* markerPseudo = nsLayoutUtils::GetMarkerPseudo(GetContent());
+    if (nsIFrame* marker = markerPseudo->GetPrimaryFrame()) {
+      if (nsBulletFrame* bullet = do_QueryFrame(marker)) {
+        bullet->GetSpokenText(aText);
+      } else {
+        ErrorResult err;
+        markerPseudo->GetTextContent(aText, err);
+        if (err.Failed()) {
+          aText.Truncate();
+        }
+      }
+    } else {
+      aText.Truncate();
+    }
+  }
+}
+#endif
+
 nsOverflowContinuationTracker::nsOverflowContinuationTracker(
     nsContainerFrame* aFrame, bool aWalkOOFFrames,
     bool aSkipOverflowContainerChildren)
     : mOverflowContList(nullptr),
       mPrevOverflowCont(nullptr),
       mSentry(nullptr),
       mParent(aFrame),
       mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -429,16 +429,23 @@ class nsContainerFrame : public nsSplitt
    * @param aChildRI A ReflowInput for the positioned child frame that's being
    *                 aligned.
    * @param aLogicalAxis The axis (of this container frame) in which the caller
    *                     would like to align the child frame.
    */
   virtual uint16_t CSSAlignmentForAbsPosChild(
       const ReflowInput& aChildRI, mozilla::LogicalAxis aLogicalAxis) const;
 
+#ifdef ACCESSIBILITY
+  /**
+   * Return the ::marker text equivalent, without flushing.
+   */
+  void GetSpokenMarkerText(nsAString& aText) const;
+#endif
+
 #define NS_DECLARE_FRAME_PROPERTY_FRAMELIST(prop) \
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR_NEVER_CALLED(prop, nsFrameList)
 
   typedef PropertyDescriptor<nsFrameList> FrameListPropertyDescriptor;
 
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty)
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty)
   NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty)
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -44,17 +44,17 @@ nsFloatManager::nsFloatManager(PresShell
       mSplitLeftFloatAcrossBreak(false),
       mSplitRightFloatAcrossBreak(false) {
   MOZ_COUNT_CTOR(nsFloatManager);
 }
 
 nsFloatManager::~nsFloatManager() { MOZ_COUNT_DTOR(nsFloatManager); }
 
 // static
-void* nsFloatManager::operator new(size_t aSize) CPP_THROW_NEW {
+void* nsFloatManager::operator new(size_t aSize) noexcept(true) {
   if (sCachedFloatManagerCount > 0) {
     // We have cached unused instances of this class, return a cached
     // instance in stead of always creating a new one.
     return sCachedFloatManagers[--sCachedFloatManagerCount];
   }
 
   // The cache is empty, this means we have to create a new instance using
   // the global |operator new|.
--- a/layout/generic/nsFloatManager.h
+++ b/layout/generic/nsFloatManager.h
@@ -90,17 +90,17 @@ struct nsFlowAreaRect {
  * [2] https://drafts.csswg.org/css-writing-modes/#logical-to-physical
  */
 class nsFloatManager {
  public:
   explicit nsFloatManager(mozilla::PresShell* aPresShell,
                           mozilla::WritingMode aWM);
   ~nsFloatManager();
 
-  void* operator new(size_t aSize) CPP_THROW_NEW;
+  void* operator new(size_t aSize) noexcept(true);
   void operator delete(void* aPtr, size_t aSize);
 
   static void Shutdown();
 
   /**
    * Get float region stored on the frame. (Defaults to mRect if it's
    * not there.) The float region is the area impacted by this float;
    * the coordinates are relative to the containing block frame.
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -541,17 +541,17 @@ static bool IsFontSizeInflationContainer
   // The root frame should always be an inflation container.
   if (!aFrame->GetParent()) {
     return true;
   }
 
   nsIContent* content = aFrame->GetContent();
   LayoutFrameType frameType = aFrame->Type();
   bool isInline =
-      (aFrame->GetDisplay() == StyleDisplay::Inline ||
+      (nsStyleDisplay::DisplayInside(aFrame->GetDisplay()) == StyleDisplayInside::Inline ||
        RubyUtils::IsRubyBox(frameType) ||
        (aFrame->IsFloating() && frameType == LayoutFrameType::Letter) ||
        // Given multiple frames for the same node, only the
        // outer one should be considered a container.
        // (Important, e.g., for nsSelectsAreaFrame.)
        (aFrame->GetParent()->GetContent() == content) ||
        (content &&
         (content->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup,
@@ -5256,17 +5256,17 @@ static nsIFrame::ContentOffsets OffsetsF
     offsets.secondaryOffset = range.end;
     offsets.associate = CARET_ASSOCIATE_AFTER;
     return offsets;
   }
 
   // Figure out whether the offsets should be over, after, or before the frame
   nsRect rect(nsPoint(0, 0), aFrame->GetSize());
 
-  bool isBlock = aFrame->GetDisplay() != StyleDisplay::Inline;
+  bool isBlock = nsStyleDisplay::DisplayInside(aFrame->GetDisplay()) != StyleDisplayInside::Inline;
   bool isRtl =
       (aFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL);
   if ((isBlock && rect.y < aPoint.y) ||
       (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
                     (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
     offsets.offset = range.end;
     if (rect.Contains(aPoint))
       offsets.secondaryOffset = range.start;
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -131,17 +131,17 @@ class nsFrame : public nsBox {
    * Create a new "empty" frame that maps a given piece of content into a
    * 0,0 area.
    */
   friend nsIFrame* NS_NewEmptyFrame(mozilla::PresShell* aShell,
                                     ComputedStyle* aStyle);
 
  private:
   // Left undefined; nsFrame objects are never allocated from the heap.
-  void* operator new(size_t sz) CPP_THROW_NEW;
+  void* operator new(size_t sz) noexcept(true);
 
  protected:
   // Overridden to prevent the global delete from being called, since
   // the memory came out of an arena instead of the heap.
   //
   // Ideally this would be private and undefined, like the normal
   // operator new.  Unfortunately, the C++ standard requires an
   // overridden operator delete to be accessible to any subclass that
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -5179,17 +5179,17 @@ void nsTextFrame::GetTextDecorations(
       }
     }
 
     // In all modes, if we're on an inline-block or inline-table (or
     // inline-stack, inline-box, inline-grid), we're done.
     // If we're on a ruby frame other than ruby text container, we
     // should continue.
     mozilla::StyleDisplay display = f->GetDisplay();
-    if (display != mozilla::StyleDisplay::Inline &&
+    if (nsStyleDisplay::DisplayInside(display) != StyleDisplayInside::Inline &&
         (!nsStyleDisplay::IsRubyDisplayType(display) ||
          display == mozilla::StyleDisplay::RubyTextContainer) &&
         nsStyleDisplay::IsDisplayTypeInlineOutside(display)) {
       break;
     }
 
     // In quirks mode, if we're on an HTML table element, we're done.
     if (compatMode == eCompatibility_NavQuirks &&
--- a/layout/inspector/tests/test_bug877690.html
+++ b/layout/inspector/tests/test_bug877690.html
@@ -84,23 +84,20 @@ function do_test() {
   var expected = [ "initial", "inherit", "unset", "revert" ];
   ok(testValues(values, expected), "property padding-bottom's values.");
 
   // test proprety
   var prop = "display";
   var values = InspectorUtils.getCSSValuesForProperty(prop);
   var expected = [ "initial", "inherit", "unset", "revert", "none", "inline", "block", "inline-block", "list-item",
       "table", "inline-table", "table-row-group", "table-header-group", "table-footer-group", "table-row",
-      "table-column-group", "table-column", "table-cell", "table-caption", "-moz-box", "-moz-inline-box",
-      "-moz-grid", "-moz-inline-grid", "-moz-grid-group", "-moz-grid-line", "-moz-stack", "-moz-inline-stack",
-      "-moz-deck", "-moz-popup", "-moz-groupbox",
+      "table-column-group", "table-column", "table-cell", "table-caption",
       "flex", "inline-flex", "-webkit-box", "-webkit-inline-box",
-      "-webkit-flex", "-webkit-inline-flex",
-      "grid", "inline-grid",
-      "ruby", "ruby-base", "ruby-base-container", "ruby-text", "ruby-text-container",
+      "grid", "inline-grid", "inline list-item", "inline flow-root list-item",
+      "ruby", "ruby-base", "ruby-base-container", "ruby-text", "ruby-text-container", "block ruby",
       "contents", "flow-root" ];
   ok(testValues(values, expected), "property display's values.");
 
   // test property
   var prop = "float";
   var values = InspectorUtils.getCSSValuesForProperty(prop);
   var expected = [ "initial", "inherit", "unset", "revert", "none", "left", "right", "inline-start", "inline-end" ];
   ok(testValues(values, expected), "property float's values.");
--- a/layout/mathml/mathml.css
+++ b/layout/mathml/mathml.css
@@ -23,17 +23,17 @@ math {
   font-family: serif;
   line-height: normal;
   word-spacing: normal;
   letter-spacing: normal;
   text-rendering: optimizeLegibility;
   -moz-float-edge: margin-box;
   -moz-math-display: inline;
 }
-math[mode="display"], math[display="block"] {
+math[display="block"] {
   display: block;
   text-align: -moz-center;
   -moz-math-display: block;
 }
 math[display="inline"] {
   display: inline;
   -moz-math-display: inline;
 }
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -3731,17 +3731,17 @@ class nsDisplayListSet {
    * Move all display items in our lists to top of the corresponding lists in
    * the destination.
    */
   void MoveTo(const nsDisplayListSet& aDestination) const;
 
  private:
   // This class is only used on stack, so we don't have to worry about leaking
   // it.  Don't let us be heap-allocated!
-  void* operator new(size_t sz) CPP_THROW_NEW;
+  void* operator new(size_t sz) noexcept(true);
 
  protected:
   nsDisplayList* mBorderBackground;
   nsDisplayList* mBlockBorderBackgrounds;
   nsDisplayList* mFloats;
   nsDisplayList* mContent;
   nsDisplayList* mPositioned;
   nsDisplayList* mOutlines;
@@ -3777,17 +3777,17 @@ struct nsDisplayListCollection : public 
    * @param aContent the content element to use for content ordering
    */
   void SerializeWithCorrectZOrder(nsDisplayList* aOutResultList,
                                   nsIContent* aContent);
 
  private:
   // This class is only used on stack, so we don't have to worry about leaking
   // it.  Don't let us be heap-allocated!
-  void* operator new(size_t sz) CPP_THROW_NEW;
+  void* operator new(size_t sz) noexcept(true);
 
   nsDisplayList mLists[6];
 };
 
 /**
  * A display list that also retains the partial build
  * information (in the form of a DAG) used to create it.
  *
--- a/layout/reftests/css-grid/grid-item-blockifying-001.html
+++ b/layout/reftests/css-grid/grid-item-blockifying-001.html
@@ -340,17 +340,17 @@ var expected = [
   "flex flex",
   "contents",
   "flex block",
   "grid grid",
   "grid grid",
   "contents",
   "grid block",
   "list-item block",
-  "block block",
+  "block ruby block ruby",
   "block block",
   "block block",
   "block block",
   "block block",
   "contents",
   "contents",
   "contents",
   "contents",
@@ -358,17 +358,17 @@ var expected = [
 ];
 var expected2 = [ /* results for display:contents children */
   "block block",
   "block block",
   "block block",
   "table table",
   "flex flex",
   "grid grid",
-  "block block",
+  "block ruby block ruby",
   "block block",
   "block block",
   "block block",
   "block block",
 ];
 
 function is(elem, got, expected) {
   if (got != expected) {
--- a/layout/style/CSSFontFaceRule.h
+++ b/layout/style/CSSFontFaceRule.h
@@ -38,17 +38,17 @@ class CSSFontFaceRuleDecl final : public
   ~CSSFontFaceRuleDecl() = default;
 
   inline CSSFontFaceRule* ContainingRule();
   inline const CSSFontFaceRule* ContainingRule() const;
 
   RefPtr<RawServoFontFaceRule> mRawRule;
 
  private:
-  void* operator new(size_t size) CPP_THROW_NEW = delete;
+  void* operator new(size_t size) noexcept(true) = delete;
 };
 
 class CSSFontFaceRule final : public css::Rule {
  public:
   CSSFontFaceRule(already_AddRefed<RawServoFontFaceRule> aRawRule,
                   StyleSheet* aSheet, css::Rule* aParentRule, uint32_t aLine,
                   uint32_t aColumn)
       : css::Rule(aSheet, aParentRule, aLine, aColumn),
--- a/layout/style/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -109,17 +109,24 @@ inline void StyleArcInner<T>::IncrementR
 template <typename T>
 inline bool StyleArcInner<T>::DecrementRef() {
   if (count.load(std::memory_order_relaxed) == kStaticRefcount) {
     return false;
   }
   if (count.fetch_sub(1, std::memory_order_release) != 1) {
     return false;
   }
+#ifdef MOZ_TSAN
+  // TSan doesn't understand std::atomic_thread_fence, so in order
+  // to avoid a false positive for every time a refcounted object
+  // is deleted, we replace the fence with an atomic operation.
   count.load(std::memory_order_acquire);
+#else
+  std::atomic_thread_fence(std::memory_order_acquire);
+#endif
   MOZ_LOG_DTOR(this, "ServoArc", 8);
   return true;
 }
 
 static constexpr const uint64_t kArcSliceCanary = 0xf3f3f3f3f3f3f3f3;
 
 #define ASSERT_CANARY \
   MOZ_DIAGNOSTIC_ASSERT(_0.ptr->data.header.header == kArcSliceCanary, "Uh?");
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -665,17 +665,17 @@ void ServoStyleSet::AddDocStyleSheet(Sty
 
 bool ServoStyleSet::GeneratedContentPseudoExists(
     const ComputedStyle& aParentStyle, const ComputedStyle& aPseudoStyle) {
   auto type = aPseudoStyle.GetPseudoType();
   MOZ_ASSERT(type != PseudoStyleType::NotPseudo);
 
   if (type == PseudoStyleType::marker) {
     // ::marker only exist for list items (for now).
-    if (aParentStyle.StyleDisplay()->mDisplay != StyleDisplay::ListItem) {
+    if (!aParentStyle.StyleDisplay()->IsListItem()) {
       return false;
     }
     // display:none is equivalent to not having the pseudo-element at all.
     if (aPseudoStyle.StyleDisplay()->mDisplay == StyleDisplay::None) {
       return false;
     }
   }
 
--- a/layout/style/nsCSSAnonBoxList.h
+++ b/layout/style/nsCSSAnonBoxList.h
@@ -128,16 +128,17 @@ CSS_ANON_BOX(viewportScroll, ":-moz-view
 // Inside a flex container, a contiguous run of text gets wrapped in
 // an anonymous block, which is then treated as a flex item.
 CSS_WRAPPER_ANON_BOX(anonymousFlexItem, ":-moz-anonymous-flex-item")
 
 // Inside a grid container, a contiguous run of text gets wrapped in
 // an anonymous block, which is then treated as a grid item.
 CSS_WRAPPER_ANON_BOX(anonymousGridItem, ":-moz-anonymous-grid-item")
 
+CSS_ANON_BOX(blockRubyContent, ":-moz-block-ruby-content")
 CSS_WRAPPER_ANON_BOX(ruby, ":-moz-ruby")
 CSS_WRAPPER_ANON_BOX(rubyBase, ":-moz-ruby-base")
 CSS_WRAPPER_ANON_BOX(rubyBaseContainer, ":-moz-ruby-base-container")
 CSS_WRAPPER_ANON_BOX(rubyText, ":-moz-ruby-text")
 CSS_WRAPPER_ANON_BOX(rubyTextContainer, ":-moz-ruby-text-container")
 
 #ifdef MOZ_XUL
 CSS_ANON_BOX(mozTreeColumn, ":-moz-tree-column")
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -212,17 +212,17 @@ const KTableEntry nsCSSProps::kCursorKTa
     {eCSSKeyword_grabbing, StyleCursorKind::Grabbing},
     {eCSSKeyword_zoom_in, StyleCursorKind::ZoomIn},
     {eCSSKeyword_zoom_out, StyleCursorKind::ZoomOut},
     // -moz- prefixed vendor specific
     {eCSSKeyword__moz_grab, StyleCursorKind::Grab},
     {eCSSKeyword__moz_grabbing, StyleCursorKind::Grabbing},
     {eCSSKeyword__moz_zoom_in, StyleCursorKind::ZoomIn},
     {eCSSKeyword__moz_zoom_out, StyleCursorKind::ZoomOut},
-    {eCSSKeyword_UNKNOWN, -1}};
+    {eCSSKeyword_UNKNOWN, nsCSSKTableEntry::SENTINEL_VALUE}};
 
 KTableEntry nsCSSProps::kDisplayKTable[] = {
     {eCSSKeyword_none, StyleDisplay::None},
     {eCSSKeyword_inline, StyleDisplay::Inline},
     {eCSSKeyword_block, StyleDisplay::Block},
     {eCSSKeyword_inline_block, StyleDisplay::InlineBlock},
     {eCSSKeyword_list_item, StyleDisplay::ListItem},
     {eCSSKeyword_table, StyleDisplay::Table},
@@ -260,43 +260,43 @@ KTableEntry nsCSSProps::kDisplayKTable[]
     {eCSSKeyword_grid, StyleDisplay::Grid},
     {eCSSKeyword_inline_grid, StyleDisplay::InlineGrid},
     {eCSSKeyword__webkit_box, StyleDisplay::WebkitBox},
     {eCSSKeyword__webkit_inline_box, StyleDisplay::WebkitInlineBox},
     {eCSSKeyword__webkit_flex, StyleDisplay::Flex},
     {eCSSKeyword__webkit_inline_flex, StyleDisplay::InlineFlex},
     {eCSSKeyword_contents, StyleDisplay::Contents},
     {eCSSKeyword_flow_root, StyleDisplay::FlowRoot},
-    {eCSSKeyword_UNKNOWN, -1}};
+    {eCSSKeyword_UNKNOWN, nsCSSKTableEntry::SENTINEL_VALUE}};
 
 const KTableEntry nsCSSProps::kFontSmoothingKTable[] = {
     {eCSSKeyword_auto, NS_FONT_SMOOTHING_AUTO},
     {eCSSKeyword_grayscale, NS_FONT_SMOOTHING_GRAYSCALE},
-    {eCSSKeyword_UNKNOWN, -1}};
+    {eCSSKeyword_UNKNOWN, nsCSSKTableEntry::SENTINEL_VALUE}};
 
 const KTableEntry nsCSSProps::kTextAlignKTable[] = {
     {eCSSKeyword_left, NS_STYLE_TEXT_ALIGN_LEFT},
     {eCSSKeyword_right, NS_STYLE_TEXT_ALIGN_RIGHT},
     {eCSSKeyword_center, NS_STYLE_TEXT_ALIGN_CENTER},
     {eCSSKeyword_justify, NS_STYLE_TEXT_ALIGN_JUSTIFY},
     {eCSSKeyword__moz_center, NS_STYLE_TEXT_ALIGN_MOZ_CENTER},
     {eCSSKeyword__moz_right, NS_STYLE_TEXT_ALIGN_MOZ_RIGHT},
     {eCSSKeyword__moz_left, NS_STYLE_TEXT_ALIGN_MOZ_LEFT},
     {eCSSKeyword_start, NS_STYLE_TEXT_ALIGN_START},
     {eCSSKeyword_end, NS_STYLE_TEXT_ALIGN_END},
-    {eCSSKeyword_UNKNOWN, -1}};
+    {eCSSKeyword_UNKNOWN, nsCSSKTableEntry::SENTINEL_VALUE}};
 
 const KTableEntry nsCSSProps::kTextDecorationStyleKTable[] = {
     {eCSSKeyword__moz_none, NS_STYLE_TEXT_DECORATION_STYLE_NONE},
     {eCSSKeyword_solid, NS_STYLE_TEXT_DECORATION_STYLE_SOLID},
     {eCSSKeyword_double, NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE},
     {eCSSKeyword_dotted, NS_STYLE_TEXT_DECORATION_STYLE_DOTTED},
     {eCSSKeyword_dashed, NS_STYLE_TEXT_DECORATION_STYLE_DASHED},
     {eCSSKeyword_wavy, NS_STYLE_TEXT_DECORATION_STYLE_WAVY},
-    {eCSSKeyword_UNKNOWN, -1}};
+    {eCSSKeyword_UNKNOWN, nsCSSKTableEntry::SENTINEL_VALUE}};
 
 int32_t nsCSSProps::FindIndexOfKeyword(nsCSSKeyword aKeyword,
                                        const KTableEntry aTable[]) {
   if (eCSSKeyword_UNKNOWN == aKeyword) {
     // NOTE: we can have keyword tables where eCSSKeyword_UNKNOWN is used
     // not only for the sentinel, but also in the middle of the table to
     // knock out values that have been disabled by prefs, e.g. kDisplayKTable.
     // So we deal with eCSSKeyword_UNKNOWN up front to avoid returning a valid
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -38,36 +38,38 @@ class ComputedStyle;
 extern "C" {
 nsCSSPropertyID Servo_ResolveLogicalProperty(nsCSSPropertyID,
                                              const mozilla::ComputedStyle*);
 nsCSSPropertyID Servo_Property_LookupEnabledForAllContent(const nsACString*);
 const uint8_t* Servo_Property_GetName(nsCSSPropertyID, uint32_t* aLength);
 }
 
 struct nsCSSKTableEntry {
-  // nsCSSKTableEntry objects can be initialized either with an int16_t value
-  // or a value of an enumeration type that can fit within an int16_t.
+  // nsCSSKTableEntry objects can be initialized either with an uint16_t value
+  // or a value of an enumeration type that can fit within an uint16_t.
+  static constexpr uint16_t SENTINEL_VALUE = uint16_t(-1);
 
-  constexpr nsCSSKTableEntry(nsCSSKeyword aKeyword, int16_t aValue)
+  constexpr nsCSSKTableEntry(nsCSSKeyword aKeyword, uint16_t aValue)
       : mKeyword(aKeyword), mValue(aValue) {}
 
   template <typename T,
             typename = typename std::enable_if<std::is_enum<T>::value>::type>
   constexpr nsCSSKTableEntry(nsCSSKeyword aKeyword, T aValue)
-      : mKeyword(aKeyword), mValue(static_cast<int16_t>(aValue)) {
-    static_assert(mozilla::EnumTypeFitsWithin<T, int16_t>::value,
+      : mKeyword(aKeyword), mValue(static_cast<uint16_t>(aValue)) {
+    static_assert(mozilla::EnumTypeFitsWithin<T, uint16_t>::value,
                   "aValue must be an enum that fits within mValue");
+    MOZ_ASSERT(static_cast<uint16_t>(aValue) != SENTINEL_VALUE);
   }
 
   bool IsSentinel() const {
-    return mKeyword == eCSSKeyword_UNKNOWN && mValue == -1;
+    return mKeyword == eCSSKeyword_UNKNOWN && mValue == SENTINEL_VALUE;
   }
 
   nsCSSKeyword mKeyword;
-  int16_t mValue;
+  uint16_t mValue;
 };
 
 class nsCSSProps {
  public:
   typedef mozilla::CSSEnabledState EnabledState;
   typedef mozilla::CSSPropFlags Flags;
   typedef nsCSSKTableEntry KTableEntry;
 
@@ -134,30 +136,30 @@ class nsCSSProps {
   // Return the first keyword in |aTable| that has the corresponding value
   // |aValue|. Return |eCSSKeyword_UNKNOWN| if not found.
   static nsCSSKeyword ValueToKeywordEnum(int32_t aValue,
                                          const KTableEntry aTable[]);
   template <typename T,
             typename = typename std::enable_if<std::is_enum<T>::value>::type>
   static nsCSSKeyword ValueToKeywordEnum(T aValue, const KTableEntry aTable[]) {
     static_assert(
-        mozilla::EnumTypeFitsWithin<T, int16_t>::value,
+        mozilla::EnumTypeFitsWithin<T, uint16_t>::value,
         "aValue must be an enum that fits within KTableEntry::mValue");
-    return ValueToKeywordEnum(static_cast<int16_t>(aValue), aTable);
+    return ValueToKeywordEnum(static_cast<uint16_t>(aValue), aTable);
   }
   // Ditto but as a string, return "" when not found.
   static const nsCString& ValueToKeyword(int32_t aValue,
                                          const KTableEntry aTable[]);
   template <typename T,
             typename = typename std::enable_if<std::is_enum<T>::value>::type>
   static const nsCString& ValueToKeyword(T aValue, const KTableEntry aTable[]) {
     static_assert(
-        mozilla::EnumTypeFitsWithin<T, int16_t>::value,
+        mozilla::EnumTypeFitsWithin<T, uint16_t>::value,
         "aValue must be an enum that fits within KTableEntry::mValue");
-    return ValueToKeyword(static_cast<int16_t>(aValue), aTable);
+    return ValueToKeyword(static_cast<uint16_t>(aValue), aTable);
   }
 
  private:
   static const Flags kFlagsTable[eCSSProperty_COUNT];
 
  public:
   static bool PropHasFlags(nsCSSPropertyID aProperty, Flags aFlags) {
     MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT,
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -793,18 +793,20 @@ static nsIFrame* StyleFrame(nsIFrame* aO
   NS_ASSERTION(inner, "table wrapper must have an inner");
   NS_ASSERTION(!inner->GetNextSibling(),
                "table wrapper frames should have just one child, the inner "
                "table");
   return inner;
 }
 
 static bool IsNonReplacedInline(nsIFrame* aFrame) {
-  return aFrame->StyleDisplay()->mDisplay == StyleDisplay::Inline &&
-         !aFrame->IsFrameOfType(nsIFrame::eReplaced);
+  // FIXME: this should be IsInlineInsideStyle() since width/height
+  // doesn't apply to ruby boxes.
+  return aFrame->StyleDisplay()->DisplayInside() == StyleDisplayInside::Inline &&
+    !aFrame->IsFrameOfType(nsIFrame::eReplaced);
 }
 
 static Side SideForPaddingOrMarginOrInsetProperty(nsCSSPropertyID aPropID) {
   switch (aPropID) {
     case eCSSProperty_top:
     case eCSSProperty_margin_top:
     case eCSSProperty_padding_top:
       return eSideTop;
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -15,16 +15,81 @@
 
 #include "gfxFontConstants.h"
 #include "mozilla/ServoStyleConsts.h"
 
 // XXX fold this into ComputedStyle and group by nsStyleXXX struct
 
 namespace mozilla {
 
+static constexpr uint16_t STYLE_DISPLAY_LIST_ITEM_BIT = 0x8000;
+static constexpr uint8_t STYLE_DISPLAY_OUTSIDE_BITS = 7;
+static constexpr uint8_t STYLE_DISPLAY_INSIDE_BITS = 8;
+
+// The `display` longhand.
+uint16_t constexpr StyleDisplayFrom(StyleDisplayOutside aOuter,
+                                    StyleDisplayInside aInner) {
+  return uint16_t(uint16_t(aOuter) << STYLE_DISPLAY_INSIDE_BITS) | uint16_t(aInner);
+}
+
+enum class StyleDisplay : uint16_t {
+  // These MUST be in sync with the Rust enum values in servo/components/style/values/specified/box.rs
+  /// https://drafts.csswg.org/css-display/#the-display-properties
+  None = StyleDisplayFrom(StyleDisplayOutside::None, StyleDisplayInside::None),
+  Contents = StyleDisplayFrom(StyleDisplayOutside::None, StyleDisplayInside::Contents),
+  Inline = StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::Inline),
+  InlineBlock = StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::FlowRoot),
+  Block = StyleDisplayFrom(StyleDisplayOutside::Block, StyleDisplayInside::Block),
+  FlowRoot = StyleDisplayFrom(StyleDisplayOutside::Block, StyleDisplayInside::FlowRoot),
+  Flex = StyleDisplayFrom(StyleDisplayOutside::Block, StyleDisplayInside::Flex),
+  InlineFlex = StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::Flex),
+  Grid = StyleDisplayFrom(StyleDisplayOutside::Block, StyleDisplayInside::Grid),
+  InlineGrid = StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::Grid),
+  Table = StyleDisplayFrom(StyleDisplayOutside::Block, StyleDisplayInside::Table),
+  InlineTable = StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::Table),
+  TableCaption = StyleDisplayFrom(StyleDisplayOutside::TableCaption, StyleDisplayInside::Block),
+  Ruby = StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::Ruby),
+  WebkitBox = StyleDisplayFrom(StyleDisplayOutside::Block, StyleDisplayInside::WebkitBox),
+  WebkitInlineBox = StyleDisplayFrom(StyleDisplayOutside::Inline, StyleDisplayInside::WebkitBox),
+  ListItem = Block | STYLE_DISPLAY_LIST_ITEM_BIT,
+
+  /// Internal table boxes.
+  TableRowGroup = StyleDisplayFrom(StyleDisplayOutside::InternalTable, StyleDisplayInside::TableRowGroup),
+  TableHeaderGroup = StyleDisplayFrom(StyleDisplayOutside::InternalTable, StyleDisplayInside::TableHeaderGroup),
+  TableFooterGroup = StyleDisplayFrom(StyleDisplayOutside::InternalTable, StyleDisplayInside::TableFooterGroup),
+  TableColumn = StyleDisplayFrom(StyleDisplayOutside::InternalTable, StyleDisplayInside::TableColumn),
+  TableColumnGroup = StyleDisplayFrom(StyleDisplayOutside::InternalTable, StyleDisplayInside::TableColumnGroup),
+  TableRow = StyleDisplayFrom(StyleDisplayOutside::InternalTable, StyleDisplayInside::TableRow),
+  TableCell = StyleDisplayFrom(StyleDisplayOutside::InternalTable, StyleDisplayInside::TableCell),
+
+  /// Internal ruby boxes.
+  RubyBase = StyleDisplayFrom(StyleDisplayOutside::InternalRuby, StyleDisplayInside::RubyBase),
+  RubyBaseContainer = StyleDisplayFrom(StyleDisplayOutside::InternalRuby, StyleDisplayInside::RubyBaseContainer),
+  RubyText = StyleDisplayFrom(StyleDisplayOutside::InternalRuby, StyleDisplayInside::RubyText),
+  RubyTextContainer = StyleDisplayFrom(StyleDisplayOutside::InternalRuby, StyleDisplayInside::RubyTextContainer),
+
+  /// XUL boxes.
+  MozBox = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozBox),
+  MozInlineBox = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozInlineBox),
+  MozGrid = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozGrid),
+  MozInlineGrid = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozInlineGrid),
+  MozGridGroup = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozGridGroup),
+  MozGridLine = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozGridLine),
+  MozStack = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozStack),
+  MozInlineStack = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozInlineStack),
+  MozDeck = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozDeck),
+  MozGroupbox = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozGroupbox),
+  MozPopup = StyleDisplayFrom(StyleDisplayOutside::XUL, StyleDisplayInside::MozPopup),
+};
+// The order of the StyleDisplay values isn't meaningful.
+bool operator<(const StyleDisplay&, const StyleDisplay&) = delete;
+bool operator<=(const StyleDisplay&, const StyleDisplay&) = delete;
+bool operator>(const StyleDisplay&, const StyleDisplay&) = delete;
+bool operator>=(const StyleDisplay&, const StyleDisplay&) = delete;
+
 // Basic shapes
 enum class StyleBasicShapeType : uint8_t {
   Polygon,
   Circle,
   Ellipse,
   Inset,
 };
 
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -580,17 +580,17 @@ nsChangeHint nsStyleList::CalcDifference
   }
   nsChangeHint hint = nsChangeHint(0);
   // Only elements whose display value is list-item can be affected by
   // list-style-position and list-style-type. If the old display struct
   // doesn't exist, assume it isn't affected by display value at all,
   // and thus these properties should not affect it either. This also
   // relies on that when the display value changes from something else
   // to list-item, that change itself would cause ReconstructFrame.
-  if (aOldDisplay.mDisplay == StyleDisplay::ListItem) {
+  if (aOldDisplay.IsListItem()) {
     if (mListStylePosition != aNewData.mListStylePosition) {
       return nsChangeHint_ReconstructFrame;
     }
     if (mCounterStyle != aNewData.mCounterStyle) {
       return NS_STYLE_HINT_REFLOW;
     }
   } else if (mListStylePosition != aNewData.mListStylePosition ||
              mCounterStyle != aNewData.mCounterStyle) {
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1593,83 +1593,94 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   mozilla::NonNegativeLengthPercentage mShapeMargin;
 
   mozilla::StyleShapeSource mShapeOutside;
 
   bool HasAppearance() const {
     return mAppearance != mozilla::StyleAppearance::None;
   }
 
+  static mozilla::StyleDisplayOutside DisplayOutside(mozilla::StyleDisplay aDisplay) {
+    return mozilla::StyleDisplayOutside((uint16_t(aDisplay) >> mozilla::STYLE_DISPLAY_INSIDE_BITS) &
+                                        uint16_t(((1 << mozilla::STYLE_DISPLAY_OUTSIDE_BITS) - 1)));
+  }
+  mozilla::StyleDisplayOutside DisplayOutside() const {
+    return DisplayOutside(mDisplay);
+  }
+
+  static mozilla::StyleDisplayInside DisplayInside(mozilla::StyleDisplay aDisplay) {
+    return mozilla::StyleDisplayInside(uint16_t(aDisplay) &
+                                       uint16_t(((1 << mozilla::STYLE_DISPLAY_INSIDE_BITS) - 1)));
+  }
+  mozilla::StyleDisplayInside DisplayInside() const {
+    return DisplayInside(mDisplay);
+  }
+
+  static bool IsListItem(mozilla::StyleDisplay aDisplay) {
+    return !!(uint16_t(aDisplay) & mozilla::STYLE_DISPLAY_LIST_ITEM_BIT);
+  }
+  bool IsListItem() const { return IsListItem(mDisplay); }
+
   bool IsBlockInsideStyle() const {
     return mozilla::StyleDisplay::Block == mDisplay ||
            mozilla::StyleDisplay::ListItem == mDisplay ||
            mozilla::StyleDisplay::InlineBlock == mDisplay ||
            mozilla::StyleDisplay::TableCaption == mDisplay ||
            mozilla::StyleDisplay::FlowRoot == mDisplay;
     // Should TABLE_CELL be included here?  They have
     // block frames nested inside of them.
     // (But please audit all callers before changing.)
   }
 
   bool IsInlineInsideStyle() const {
-    return mozilla::StyleDisplay::Inline == mDisplay ||
-           mozilla::StyleDisplay::Ruby == mDisplay ||
-           mozilla::StyleDisplay::RubyBase == mDisplay ||
-           mozilla::StyleDisplay::RubyBaseContainer == mDisplay ||
-           mozilla::StyleDisplay::RubyText == mDisplay ||
-           mozilla::StyleDisplay::RubyTextContainer == mDisplay;
+    auto inside = DisplayInside();
+    return inside == mozilla::StyleDisplayInside::Inline ||
+           inside == mozilla::StyleDisplayInside::Ruby ||
+           inside == mozilla::StyleDisplayInside::RubyBase ||
+           inside == mozilla::StyleDisplayInside::RubyBaseContainer ||
+           inside == mozilla::StyleDisplayInside::RubyText ||
+           inside == mozilla::StyleDisplayInside::RubyTextContainer;
   }
 
   bool IsBlockOutsideStyle() const {
-    return mozilla::StyleDisplay::Block == mDisplay ||
-           mozilla::StyleDisplay::Flex == mDisplay ||
-           mozilla::StyleDisplay::WebkitBox == mDisplay ||
-           mozilla::StyleDisplay::Grid == mDisplay ||
-           mozilla::StyleDisplay::ListItem == mDisplay ||
-           mozilla::StyleDisplay::Table == mDisplay ||
-           mozilla::StyleDisplay::FlowRoot == mDisplay;
+    return DisplayOutside() == mozilla::StyleDisplayOutside::Block;
   }
 
   static bool IsDisplayTypeInlineOutside(mozilla::StyleDisplay aDisplay) {
-    return mozilla::StyleDisplay::Inline == aDisplay ||
-           mozilla::StyleDisplay::InlineBlock == aDisplay ||
-           mozilla::StyleDisplay::InlineTable == aDisplay ||
-           mozilla::StyleDisplay::MozInlineBox == aDisplay ||
-           mozilla::StyleDisplay::InlineFlex == aDisplay ||
-           mozilla::StyleDisplay::WebkitInlineBox == aDisplay ||
-           mozilla::StyleDisplay::InlineGrid == aDisplay ||
+    auto outside = DisplayOutside(aDisplay);
+    if (outside == mozilla::StyleDisplayOutside::Inline) {
+      return true;
+    }
+    // just an optimization for the common case:
+    if (outside == mozilla::StyleDisplayOutside::Block) {
+      return false;
+    }
+    return mozilla::StyleDisplay::MozInlineBox == aDisplay ||
            mozilla::StyleDisplay::MozInlineGrid == aDisplay ||
            mozilla::StyleDisplay::MozInlineStack == aDisplay ||
-           mozilla::StyleDisplay::Ruby == aDisplay ||
            mozilla::StyleDisplay::RubyBase == aDisplay ||
            mozilla::StyleDisplay::RubyBaseContainer == aDisplay ||
            mozilla::StyleDisplay::RubyText == aDisplay ||
            mozilla::StyleDisplay::RubyTextContainer == aDisplay;
   }
 
   bool IsInlineOutsideStyle() const {
     return IsDisplayTypeInlineOutside(mDisplay);
   }
 
   bool IsOriginalDisplayInlineOutsideStyle() const {
     return IsDisplayTypeInlineOutside(mOriginalDisplay);
   }
 
   bool IsInnerTableStyle() const {
-    return mozilla::StyleDisplay::TableCell == mDisplay ||
-           IsInternalTableStyleExceptCell();
+    return DisplayOutside() == mozilla::StyleDisplayOutside::InternalTable;
   }
 
   bool IsInternalTableStyleExceptCell() const {
-    return mozilla::StyleDisplay::TableRow == mDisplay ||
-           mozilla::StyleDisplay::TableRowGroup == mDisplay ||
-           mozilla::StyleDisplay::TableHeaderGroup == mDisplay ||
-           mozilla::StyleDisplay::TableFooterGroup == mDisplay ||
-           mozilla::StyleDisplay::TableColumn == mDisplay ||
-           mozilla::StyleDisplay::TableColumnGroup == mDisplay;
+    return IsInnerTableStyle() && mozilla::StyleDisplay::TableCell != mDisplay;
   }
 
   bool IsFloatingStyle() const { return mozilla::StyleFloat::None != mFloat; }
 
   bool IsAbsolutelyPositionedStyle() const {
     return NS_STYLE_POSITION_ABSOLUTE == mPosition ||
            NS_STYLE_POSITION_FIXED == mPosition;
   }
@@ -1682,17 +1693,17 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
     return NS_STYLE_POSITION_STICKY == mPosition;
   }
   bool IsPositionForcingStackingContext() const {
     return NS_STYLE_POSITION_STICKY == mPosition ||
            NS_STYLE_POSITION_FIXED == mPosition;
   }
 
   static bool IsRubyDisplayType(mozilla::StyleDisplay aDisplay) {
-    return mozilla::StyleDisplay::Ruby == aDisplay ||
+    return DisplayInside(aDisplay) == mozilla::StyleDisplayInside::Ruby ||
            IsInternalRubyDisplayType(aDisplay);
   }
 
   static bool IsInternalRubyDisplayType(mozilla::StyleDisplay aDisplay) {
     return mozilla::StyleDisplay::RubyBase == aDisplay ||
            mozilla::StyleDisplay::RubyBaseContainer == aDisplay ||
            mozilla::StyleDisplay::RubyText == aDisplay ||
            mozilla::StyleDisplay::RubyTextContainer == aDisplay;
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -5461,27 +5461,30 @@ var gCSSProperties = {
     initial_values: ["inline"],
     /* XXX none will really mess with other properties */
     prerequisites: { float: "none", position: "static", contain: "none" },
     other_values: [
       "block",
       "flex",
       "inline-flex",
       "list-item",
+      "inline list-item",
+      "inline flow-root list-item",
       "inline-block",
       "table",
       "inline-table",
       "table-row-group",
       "table-header-group",
       "table-footer-group",
       "table-row",
       "table-column-group",
       "table-column",
       "table-cell",
       "table-caption",
+      "block ruby",
       "ruby",
       "ruby-base",
       "ruby-base-container",
       "ruby-text",
       "ruby-text-container",
       "contents",
       "none",
     ],
--- a/layout/style/test/test_position_float_display.html
+++ b/layout/style/test/test_position_float_display.html
@@ -38,33 +38,36 @@ var mapping = {
   "table-column": "block",
   "table-column-group": "block",
   "table-header-group": "block",
   "table-footer-group": "block",
   "table-row": "block",
   "table-cell": "block",
   "table-caption": "block",
   "inline-block": "block",
-  "ruby": "block",
+  "block ruby": "block ruby",
+  "ruby": "block ruby",
   "ruby-base": "block",
   "ruby-base-container": "block",
   "ruby-text": "block",
   "ruby-text-container": "block",
   "flex": "flex",
   "grid": "grid",
   "none": "none",
   "table": "table",
   "inline-grid": "grid",
   "inline-flex": "flex",
   "inline-table": "table",
   "block": "block",
   "contents": "contents",
   "flow-root": "flow-root",
   // Note: this is sometimes block
-  "list-item": "list-item"
+  "list-item": "list-item",
+  "inline list-item": "list-item",
+  "inline flow-root list-item": "list-item",
 };
 
 function test_display_value(val)
 {
   var floatLeftElem = document.getElementById("float-left");
   floatLeftElem.style.display = val;
   var floatLeftConversion = window.getComputedStyle(floatLeftElem).display;
   floatLeftElem.style.display = "";
--- a/layout/style/test/test_root_node_display.html
+++ b/layout/style/test/test_root_node_display.html
@@ -37,17 +37,24 @@ function test_display_value(val)
 
   // Special case: "display:list-item" does not get modified by 'float',
   // but the spec allows us to convert it to 'block' on the root node
   // (and we do convert it, so that we don't have to support documents whose
   // root node is a list-item).
   if (val == "list-item") {
     is(floatConversion, val, "'float' shouldn't affect 'display:list-item'");
     is(rootConversion, "block",
-       "We traditionally convert 'display:list-item' on the root node to " +
+       "We traditionally convert '" + val + "' on the root node to " +
+       "'display:block' (though if that changes, it's not technically a bug, " +
+       "as long as we support it properly).");
+} else if (val == "inline list-item" ||
+           val == "inline flow-root list-item") {
+    is(floatConversion, "list-item", "'float' should blockify '" + val + "'");
+    is(rootConversion, "block",
+       "We traditionally convert '" + val + "' on the root node to " +
        "'display:block' (though if that changes, it's not technically a bug, " +
        "as long as we support it properly).");
   } else if (val == "contents") {
     is(floatConversion, val, "'float' shouldn't affect 'display:contents'");
     is(rootConversion, "block",
        "'display:contents' on the root node computes to block-level per" +
        "http://dev.w3.org/csswg/css-display/#transformations");
   } else {
--- a/layout/tables/SpanningCellSorter.h
+++ b/layout/tables/SpanningCellSorter.h
@@ -79,12 +79,12 @@ class MOZ_STACK_CLASS SpanningCellSorter
   uint32_t mEnumerationIndex;  // into mArray or mSortedHashTable
   HashTableEntry** mSortedHashTable;
 
   /*
    * operator new is forbidden since we use the pres shell's stack
    * memory, which much be pushed and popped at points matching a
    * push/pop on the C++ stack.
    */
-  void* operator new(size_t sz) CPP_THROW_NEW { return nullptr; }
+  void* operator new(size_t sz) noexcept(true) { return nullptr; }
 };
 
 #endif
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -100,17 +100,17 @@ class nsDisplayTableBackgroundSet {
 
   const nsPoint& TableToReferenceFrame() { return mToReferenceFrame; }
 
   const nsRect& GetDirtyRect() { return mDirtyRect; }
 
  private:
   // This class is only used on stack, so we don't have to worry about leaking
   // it.  Don't let us be heap-allocated!
-  void* operator new(size_t sz) CPP_THROW_NEW;
+  void* operator new(size_t sz) noexcept(true);
 
  protected:
   nsDisplayListBuilder* mBuilder;
   nsDisplayTableBackgroundSet* mPrevTableBackgroundSet;
 
   nsDisplayList mColGroupBackgrounds;
   nsDisplayList mColBackgrounds;
 
--- a/layout/xul/nsSprocketLayout.cpp
+++ b/layout/xul/nsSprocketLayout.cpp
@@ -1499,20 +1499,20 @@ nsBoxSize::nsBoxSize() {
   left = 0;
   right = 0;
   flex = 0;
   next = nullptr;
   bogus = false;
 }
 
 void* nsBoxSize::operator new(size_t sz,
-                              nsBoxLayoutState& aState) CPP_THROW_NEW {
+                              nsBoxLayoutState& aState) noexcept(true) {
   return mozilla::AutoStackArena::Allocate(sz);
 }
 
 void nsBoxSize::operator delete(void* aPtr, size_t sz) {}
 
 void* nsComputedBoxSize::operator new(size_t sz,
-                                      nsBoxLayoutState& aState) CPP_THROW_NEW {
+                                      nsBoxLayoutState& aState) noexcept(true) {
   return mozilla::AutoStackArena::Allocate(sz);
 }
 
 void nsComputedBoxSize::operator delete(void* aPtr, size_t sz) {}
--- a/layout/xul/nsSprocketLayout.h
+++ b/layout/xul/nsSprocketLayout.h
@@ -22,30 +22,30 @@ class nsBoxSize {
   nscoord flex;
   nscoord left;
   nscoord right;
   bool collapsed;
   bool bogus;
 
   nsBoxSize* next;
 
-  void* operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW;
+  void* operator new(size_t sz, nsBoxLayoutState& aState) noexcept(true);
   void operator delete(void* aPtr, size_t sz);
 };
 
 class nsComputedBoxSize {
  public:
   nsComputedBoxSize();
 
   nscoord size;
   bool valid;
   bool resized;
   nsComputedBoxSize* next;
 
-  void* operator new(size_t sz, nsBoxLayoutState& aState) CPP_THROW_NEW;
+  void* operator new(size_t sz, nsBoxLayoutState& aState) noexcept(true);
   void operator delete(void* aPtr, size_t sz);
 };
 
 #define GET_WIDTH(size, isHorizontal) (isHorizontal ? size.width : size.height)
 #define GET_HEIGHT(size, isHorizontal) (isHorizontal ? size.height : size.width)
 #define GET_X(size, isHorizontal) (isHorizontal ? size.x : size.y)
 #define GET_Y(size, isHorizontal) (isHorizontal ? size.y : size.x)
 #define GET_COORD(aX, aY, isHorizontal) (isHorizontal ? aX : aY)
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -1954,16 +1954,21 @@
   mirror: always
 
 # This pref enables the featurePolicy header support.
 - name: dom.security.featurePolicy.header.enabled
   type: bool
   value: @IS_NIGHTLY_BUILD@
   mirror: always
 
+- name: dom.security.respect_document_nosniff
+  type: RelaxedAtomicBool
+  value: true
+  mirror: always
+
 # Expose the 'policy' attribute in document and HTMLIFrameElement
 - name: dom.security.featurePolicy.webidl.enabled
   type: bool
   value: @IS_NIGHTLY_BUILD@
   mirror: always
 
 # Is support for selection event APIs enabled?
 - name: dom.select_events.enabled
--- a/mozglue/misc/AutoProfilerLabel.cpp
+++ b/mozglue/misc/AutoProfilerLabel.cpp
@@ -25,16 +25,18 @@ class MOZ_RAII AutoProfilerLabelData {
   ProfilerLabelEnter& EnterRef() { return sEnter; }
 
   const ProfilerLabelExit& ExitCRef() const { return sExit; }
   ProfilerLabelExit& ExitRef() { return sExit; }
 
   const uint32_t& GenerationCRef() const { return sGeneration; }
   uint32_t& GenerationRef() { return sGeneration; }
 
+  static bool RacyIsProfilerPresent() { return !!sGeneration; }
+
  private:
   // Thin shell around mozglue PlatformMutex, for local internal use.
   // Does not preserve behavior in JS record/replay.
   class Mutex : private mozilla::detail::MutexImpl {
    public:
     Mutex()
         : mozilla::detail::MutexImpl(
               mozilla::recordreplay::Behavior::DontPreserve) {}
@@ -64,31 +66,48 @@ void RegisterProfilerLabelEnterExit(Prof
   AutoProfilerLabelData data;
   MOZ_ASSERT(!aEnter != !data.EnterRef(),
              "Must go from null to non-null, or from non-null to null");
   data.EnterRef() = aEnter;
   data.ExitRef() = aExit;
   ++data.GenerationRef();
 }
 
+bool IsProfilerPresent() {
+  return AutoProfilerLabelData::RacyIsProfilerPresent();
+}
+
+ProfilerLabel ProfilerLabelBegin(const char* aLabelName,
+                                 const char* aDynamicString, void* aSp) {
+  const AutoProfilerLabelData data;
+  void* entryContext = (data.EnterCRef())
+                           ? data.EnterCRef()(aLabelName, aDynamicString, aSp)
+                           : nullptr;
+  uint32_t generation = data.GenerationCRef();
+
+  return MakeTuple(entryContext, generation);
+}
+
+void ProfilerLabelEnd(const ProfilerLabel& aLabel) {
+  if (!IsValidProfilerLabel(aLabel)) {
+    return;
+  }
+
+  const AutoProfilerLabelData data;
+  if (data.ExitCRef() && (Get<1>(aLabel) == data.GenerationCRef())) {
+    data.ExitCRef()(Get<0>(aLabel));
+  }
+}
+
 AutoProfilerLabel::AutoProfilerLabel(
     const char* aLabel,
     const char* aDynamicString MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) {
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
-  const AutoProfilerLabelData data;
-  mEntryContext = (data.EnterCRef())
-                      ? data.EnterCRef()(aLabel, aDynamicString, this)
-                      : nullptr;
-  mGeneration = data.GenerationCRef();
+  Tie(mEntryContext, mGeneration) =
+      ProfilerLabelBegin(aLabel, aDynamicString, this);
 }
 
 AutoProfilerLabel::~AutoProfilerLabel() {
-  if (!mEntryContext) {
-    return;
-  }
-  const AutoProfilerLabelData data;
-  if (data.ExitCRef() && (mGeneration == data.GenerationCRef())) {
-    data.ExitCRef()(mEntryContext);
-  }
+  ProfilerLabelEnd(MakeTuple(mEntryContext, mGeneration));
 }
 
 }  // namespace mozilla
--- a/mozglue/misc/AutoProfilerLabel.h
+++ b/mozglue/misc/AutoProfilerLabel.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_AutoProfilerLabel_h
 #define mozilla_AutoProfilerLabel_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
+#include "mozilla/Tuple.h"
 #include "mozilla/Types.h"
 
 // The Gecko Profiler defines AutoProfilerLabel, an RAII class for
 // pushing/popping frames to/from the ProfilingStack.
 //
 // This file defines a class of the same name that does much the same thing,
 // but which can be used in (and only in) mozglue. A different class is
 // necessary because mozglue cannot directly access sProfilingStack.
@@ -49,13 +50,24 @@ class MOZ_RAII AutoProfilerLabel {
  private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   void* mEntryContext;
   // Number of RegisterProfilerLabelEnterExit calls, to avoid giving an entry
   // context from one generation to the next.
   uint32_t mGeneration;
 };
 
+using ProfilerLabel = Tuple<void*, uint32_t>;
+
+bool IsProfilerPresent();
+ProfilerLabel ProfilerLabelBegin(const char* aLabelName,
+                                 const char* aDynamicString, void* aSp);
+void ProfilerLabelEnd(const ProfilerLabel& aLabel);
+
+inline bool IsValidProfilerLabel(const ProfilerLabel& aLabel) {
+  return !!Get<0>(aLabel);
+}
+
 #endif
 
 }  // namespace mozilla
 
 #endif  // mozilla_AutoProfilerLabel_h
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -47,17 +47,17 @@ namespace net {
 
 typedef nsTArray<nsCOMPtr<nsIRedirectHistoryEntry>> RedirectHistoryArray;
 
 /**
  * Class that provides an nsILoadInfo implementation.
  */
 class LoadInfo final : public nsILoadInfo {
  public:
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_ISUPPORTS
   NS_DECL_NSILOADINFO
 
   // aLoadingPrincipal MUST NOT BE NULL.
   LoadInfo(nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
            nsINode* aLoadingContext, nsSecurityFlags aSecurityFlags,
            nsContentPolicyType aContentPolicyType,
            const Maybe<mozilla::dom::ClientInfo>& aLoadingClientInfo =
                Maybe<mozilla::dom::ClientInfo>(),
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2689,25 +2689,16 @@ nsresult NS_GenerateHostPort(const nsCSt
     hostLine.AppendInt(port);
   }
   return NS_OK;
 }
 
 void NS_SniffContent(const char* aSnifferType, nsIRequest* aRequest,
                      const uint8_t* aData, uint32_t aLength,
                      nsACString& aSniffedType) {
-  // In case XCTO nosniff was present, we could just skip sniffing here
-  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
-  if (channel) {
-    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
-    if (loadInfo->GetSkipContentSniffing()) {
-      aSniffedType.Truncate();
-      return;
-    }
-  }
   typedef nsCategoryCache<nsIContentSniffer> ContentSnifferCache;
   extern ContentSnifferCache* gNetSniffers;
   extern ContentSnifferCache* gDataSniffers;
   ContentSnifferCache* cache = nullptr;
   if (!strcmp(aSnifferType, NS_CONTENT_SNIFFER_CATEGORY)) {
     if (!gNetSniffers) {
       gNetSniffers = new ContentSnifferCache(NS_CONTENT_SNIFFER_CATEGORY);
     }
@@ -2729,16 +2720,45 @@ void NS_SniffContent(const char* aSniffe
     nsresult rv = sniffers[i]->GetMIMETypeFromContent(aRequest, aData, aLength,
                                                       aSniffedType);
     if (NS_SUCCEEDED(rv) && !aSniffedType.IsEmpty()) {
       return;
     }
   }
 
   aSniffedType.Truncate();
+
+  // If the Sniffers did not hit and NoSniff is set
+  // Check if we have any MIME Type at all or report an
+  // Error to the Console
+  nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest);
+  if (channel) {
+    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
+
+    if (loadInfo->GetSkipContentSniffing()) {
+      nsAutoCString type;
+      channel->GetContentType(type);
+
+      if (type.Equals(nsCString("application/x-unknown-content-type"))) {
+        nsCOMPtr<nsIURI> requestUri;
+        channel->GetURI(getter_AddRefs(requestUri));
+        nsAutoCString spec;
+        requestUri->GetSpec(spec);
+        if (spec.Length() > 50) {
+          spec.Truncate(50);
+          spec.AppendLiteral("...");
+        }
+        channel->LogMimeTypeMismatch(
+            nsCString("XTCOWithMIMEValueMissing"), false,
+            NS_ConvertUTF8toUTF16(spec),
+            // Type is not used in the Error Message but required
+            NS_ConvertUTF8toUTF16(type));
+      }
+    }
+  }
 }
 
 bool NS_IsSrcdocChannel(nsIChannel* aChannel) {
   bool isSrcdoc;
   nsCOMPtr<nsIInputStreamChannel> isr = do_QueryInterface(aChannel);
   if (isr) {
     isr->GetIsSrcdocChannel(&isSrcdoc);
     return isSrcdoc;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -1436,22 +1436,24 @@ nsresult ProcessXCTO(nsHttpChannel* aCha
     if (nsContentUtils::IsJavascriptMIMEType(
             NS_ConvertUTF8toUTF16(contentType))) {
       return NS_OK;
     }
     ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType,
                            Report::Error);
     return NS_ERROR_CORRUPTED_CONTENT;
   }
+
   auto policyType = aLoadInfo->GetExternalContentPolicyType();
-  if (policyType == nsIContentPolicy::TYPE_DOCUMENT ||
-      policyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
+  if ((policyType == nsIContentPolicy::TYPE_DOCUMENT ||
+       policyType == nsIContentPolicy::TYPE_SUBDOCUMENT) &&
+      gHttpHandler->IsDocumentNosniffEnabled()) {
     // If the header XCTO nosniff is set for any browsing context, then
     // we set the skipContentSniffing flag on the Loadinfo. Within
-    // NS_SniffContent we then bail early and do not do any sniffing.
+    // GetMIMETypeFromContent we then bail early and do not do any sniffing.
     aLoadInfo->SetSkipContentSniffing(true);
     return NS_OK;
   }
 
   return NS_OK;
 }
 
 // Ensure that a load of type script has correct MIME type
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -1825,19 +1825,20 @@ void nsHttpConnection::CloseTransaction(
     // not closed transaction what will likely lead to a use of a closed ssl
     // socket and may cause a crash because of an unexpected use.
     //
     // There can possibly be two states: the actual transaction is still hanging
     // of off the filter, or has not even been assigned on it yet.  In the
     // latter case we simply must close the transaction given to us via the
     // argument.
     if (!mTLSFilter->Transaction()) {
-      LOG(("  closing transaction directly"));
-      MOZ_ASSERT(trans);
-      trans->Close(reason);
+      if (trans) {
+        LOG(("  closing transaction directly"));
+        trans->Close(reason);
+      }
     } else {
       LOG(("  closing transactin hanging of off mTLSFilter"));
       mTLSFilter->Close(reason);
     }
   }
 
   if (mTransaction) {
     LOG(("  closing associated mTransaction"));
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -104,17 +104,17 @@
 #endif
 
 #define HTTP_PREF_PREFIX "network.http."
 #define INTL_ACCEPT_LANGUAGES "intl.accept_languages"
 #define BROWSER_PREF_PREFIX "browser.cache."
 #define H2MANDATORY_SUITE "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256"
 #define SAFE_HINT_HEADER_VALUE "safeHint.enabled"
 #define SECURITY_PREFIX "security."
-
+#define DOM_SECURITY_PREFIX "dom.security"
 #define TCP_FAST_OPEN_ENABLE "network.tcp.tcp_fastopen_enable"
 #define TCP_FAST_OPEN_FAILURE_LIMIT \
   "network.tcp.tcp_fastopen_consecutive_failure_limit"
 #define TCP_FAST_OPEN_STALLS_LIMIT "network.tcp.tcp_fastopen_http_stalls_limit"
 #define TCP_FAST_OPEN_STALLS_IDLE \
   "network.tcp.tcp_fastopen_http_check_for_stalls_only_if_idle_for"
 #define TCP_FAST_OPEN_STALLS_TIMEOUT \
   "network.tcp.tcp_fastopen_http_stalls_timeout"
@@ -264,16 +264,17 @@ nsHttpHandler::nsHttpHandler()
       mConnectTimeout(90000),
       mTLSHandshakeTimeout(30000),
       mParallelSpeculativeConnectLimit(6),
       mRequestTokenBucketEnabled(true),
       mRequestTokenBucketMinParallelism(6),
       mRequestTokenBucketHz(100),
       mRequestTokenBucketBurst(32),
       mCriticalRequestPrioritization(true),
+      mRespectDocumentNoSniff(true),
       mTCPKeepaliveShortLivedEnabled(false),
       mTCPKeepaliveShortLivedTimeS(60),
       mTCPKeepaliveShortLivedIdleTimeS(10),
       mTCPKeepaliveLongLivedEnabled(false),
       mTCPKeepaliveLongLivedIdleTimeS(600),
       mEnforceH1Framing(FRAMECHECK_BARELY),
       mDefaultHpackBuffer(4096),
       mBug1563538(true),
@@ -426,16 +427,17 @@ static const char* gCallbackPrefs[] = {
     UA_PREF_PREFIX,
     INTL_ACCEPT_LANGUAGES,
     BROWSER_PREF("disk_cache_ssl"),
     H2MANDATORY_SUITE,
     HTTP_PREF("tcp_keepalive.short_lived_connections"),
     HTTP_PREF("tcp_keepalive.long_lived_connections"),
     SAFE_HINT_HEADER_VALUE,
     SECURITY_PREFIX,
+    DOM_SECURITY_PREFIX,
     TCP_FAST_OPEN_ENABLE,
     TCP_FAST_OPEN_FAILURE_LIMIT,
     TCP_FAST_OPEN_STALLS_LIMIT,
     TCP_FAST_OPEN_STALLS_IDLE,
     TCP_FAST_OPEN_STALLS_TIMEOUT,
     nullptr,
 };
 
@@ -1554,16 +1556,24 @@ void nsHttpHandler::PrefsChanged(const c
   // Whether or not to block requests for non head js/css items (e.g. media)
   // while those elements load.
   if (PREF_CHANGED(HTTP_PREF("rendering-critical-requests-prioritization"))) {
     rv = Preferences::GetBool(
         HTTP_PREF("rendering-critical-requests-prioritization"), &cVar);
     if (NS_SUCCEEDED(rv)) mCriticalRequestPrioritization = cVar;
   }
 
+  // Whether to respect X-Content-Type nosniff on Page loads
+  if (PREF_CHANGED("dom.security.respect_document_nosniff")) {
+    rv = Preferences::GetBool("dom.security.respect_document_nosniff", &cVar);
+    if (NS_SUCCEEDED(rv)) {
+      mRespectDocumentNoSniff = cVar;
+    }
+  }
+
   // on transition of network.http.diagnostics to true print
   // a bunch of information to the console
   if (pref && PREF_CHANGED(HTTP_PREF("diagnostics"))) {
     rv = Preferences::GetBool(HTTP_PREF("diagnostics"), &cVar);
     if (NS_SUCCEEDED(rv) && cVar) {
       if (mConnMgr) mConnMgr->PrintDiagnostics();
     }
   }
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -152,16 +152,18 @@ class nsHttpHandler final : public nsIHt
   uint32_t ConnectTimeout() { return mConnectTimeout; }
   uint32_t TLSHandshakeTimeout() { return mTLSHandshakeTimeout; }
   uint32_t ParallelSpeculativeConnectLimit() {
     return mParallelSpeculativeConnectLimit;
   }
   bool CriticalRequestPrioritization() {
     return mCriticalRequestPrioritization;
   }
+
+  bool IsDocumentNosniffEnabled() { return mRespectDocumentNoSniff; }
   bool UseH2Deps() { return mUseH2Deps; }
   bool IsH2WebsocketsEnabled() { return mEnableH2Websockets; }
 
   uint32_t MaxConnectionsPerOrigin() {
     return mMaxPersistentConnectionsPerServer;
   }
   bool UseRequestTokenBucket() { return mRequestTokenBucketEnabled; }
   uint16_t RequestTokenBucketMinParallelism() {
@@ -648,16 +650,19 @@ class nsHttpHandler final : public nsIHt
   uint16_t mRequestTokenBucketMinParallelism;
   uint32_t mRequestTokenBucketHz;     // EventTokenBucket HZ
   uint32_t mRequestTokenBucketBurst;  // EventTokenBucket Burst
 
   // Whether or not to block requests for non head js/css items (e.g. media)
   // while those elements load.
   bool mCriticalRequestPrioritization;
 
+  // Whether to respect X-Content-Type nosniff on Page loads
+  bool mRespectDocumentNoSniff;
+
   // TCP Keepalive configuration values.
 
   // True if TCP keepalive is enabled for short-lived conns.
   bool mTCPKeepaliveShortLivedEnabled;
   // Time (secs) indicating how long a conn is considered short-lived.
   int32_t mTCPKeepaliveShortLivedTimeS;
   // Time (secs) before first keepalive probe; between successful probes.
   int32_t mTCPKeepaliveShortLivedIdleTimeS;
--- a/netwerk/streamconv/converters/nsUnknownDecoder.cpp
+++ b/netwerk/streamconv/converters/nsUnknownDecoder.cpp
@@ -322,19 +322,21 @@ nsUnknownDecoder::OnStopRequest(nsIReque
 // ----
 NS_IMETHODIMP
 nsUnknownDecoder::GetMIMETypeFromContent(nsIRequest* aRequest,
                                          const uint8_t* aData, uint32_t aLength,
                                          nsACString& type) {
   // This is only used by sniffer, therefore we do not need to lock anything
   // here.
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
-  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
-  if (loadInfo->GetSkipContentSniffing()) {
-    return NS_OK;
+  if (channel) {
+    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
+    if (loadInfo->GetSkipContentSniffing()) {
+      return NS_ERROR_NOT_AVAILABLE;
+    }
   }
 
   mBuffer = const_cast<char*>(reinterpret_cast<const char*>(aData));
   mBufferLen = aLength;
   DetermineContentType(aRequest);
   mBuffer = nullptr;
   mBufferLen = 0;
   type.Assign(mContentType);
--- a/netwerk/system/mac/nsNetworkLinkService.h
+++ b/netwerk/system/mac/nsNetworkLinkService.h
@@ -9,17 +9,17 @@
 #include "nsIObserver.h"
 #include "mozilla/Mutex.h"
 
 #include <SystemConfiguration/SCNetworkReachability.h>
 #include <SystemConfiguration/SystemConfiguration.h>
 
 class nsNetworkLinkService : public nsINetworkLinkService, public nsIObserver {
  public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSINETWORKLINKSERVICE
   NS_DECL_NSIOBSERVER
 
   nsNetworkLinkService();
 
   nsresult Init();
   nsresult Shutdown();
 
@@ -40,14 +40,15 @@ class nsNetworkLinkService : public nsIN
 
   void UpdateReachability();
   void SendEvent(bool aNetworkChanged);
   static void ReachabilityChanged(SCNetworkReachabilityRef target,
                                   SCNetworkConnectionFlags flags, void* info);
   static void IPConfigChanged(SCDynamicStoreRef store, CFArrayRef changedKeys,
                               void* info);
   void calculateNetworkId(void);
+  void calculateNetworkIdInternal(void);
 
   mozilla::Mutex mMutex;
   nsCString mNetworkId;
 };
 
 #endif /* NSNETWORKLINKSERVICEMAC_H_ */
--- a/netwerk/system/mac/nsNetworkLinkService.mm
+++ b/netwerk/system/mac/nsNetworkLinkService.mm
@@ -15,23 +15,24 @@
 #include <arpa/inet.h>
 #include <ifaddrs.h>
 
 #include "nsCOMPtr.h"
 #include "nsIObserverService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsCRT.h"
+#include "nsNetCID.h"
+#include "nsThreadUtils.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SHA1.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Telemetry.h"
 #include "nsNetworkLinkService.h"
-#include "MainThreadUtils.h"
 #include "../../base/IPv6Utils.h"
 
 #import <Cocoa/Cocoa.h>
 #import <netinet/in.h>
 
 #define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
 
 using namespace mozilla;
@@ -318,16 +319,30 @@ static bool ipv6NetworkId(SHA1Sum* sha1)
   for (int i = 0; i < prefixCount; i++) {
     sha1->update(&prefixStore[i], sizeof(prefixStore[i]));
     sha1->update(&netmaskStore[i], sizeof(netmaskStore[i]));
   }
   return true;
 }
 
 void nsNetworkLinkService::calculateNetworkId(void) {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  if (!target) {
+    return;
+  }
+
+  MOZ_ALWAYS_SUCCEEDS(
+      target->Dispatch(NewRunnableMethod("nsNetworkLinkService::calculateNetworkIdInternal", this,
+                                         &nsNetworkLinkService::calculateNetworkIdInternal),
+                       NS_DISPATCH_NORMAL));
+}
+
+void nsNetworkLinkService::calculateNetworkIdInternal(void) {
   MOZ_ASSERT(!NS_IsMainThread(), "Should not be called on the main thread");
   SHA1Sum sha1;
   bool found4 = ipv4NetworkId(&sha1);
   bool found6 = ipv6NetworkId(&sha1);
 
   if (found4 || found6) {
     // This 'addition' could potentially be a fixed number from the
     // profile or something.
--- a/old-configure.in
+++ b/old-configure.in
@@ -2566,17 +2566,16 @@ if test -n "$GNU_CC" -a -n "$GNU_CXX"; t
     CXXFLAGS="$CXXFLAGS -pipe"
     AC_MSG_RESULT([yes])
 else
     AC_MSG_RESULT([no])
 fi
 
 fi # ! SKIP_COMPILER_CHECKS
 
-AC_DEFINE(CPP_THROW_NEW, [throw()])
 AC_LANG_C
 
 if test "$COMPILE_ENVIRONMENT"; then
 MOZ_EXPAND_LIBS
 fi # COMPILE_ENVIRONMENT
 
 dnl ========================================================
 dnl =
@@ -3271,17 +3270,16 @@ if test -n "$A11Y_LOG"; then
     AC_DEFINE(A11Y_LOG)
 fi
 
 dnl Spit out some output
 dnl ========================================================
 
 dnl The following defines are used by xpcom
 _NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES
-CPP_THROW_NEW
 HAVE_GETPAGESIZE
 HAVE_STATVFS64
 HAVE_STATVFS
 HAVE_STATFS64
 HAVE_STATFS
 HAVE_SYS_STATVFS_H
 HAVE_SYS_STATFS_H
 HAVE_SYS_VFS_H
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -1916,20 +1916,16 @@ VARIABLES = {
     'ANDROID_INSTRUMENTATION_MANIFESTS': (ManifestparserManifestList, list,
                                           """List of manifest files defining Android instrumentation tests.
         """),
 
     'FIREFOX_UI_FUNCTIONAL_MANIFESTS': (ManifestparserManifestList, list,
                                         """List of manifest files defining firefox-ui-functional tests.
         """),
 
-    'FIREFOX_UI_UPDATE_MANIFESTS': (ManifestparserManifestList, list,
-                                    """List of manifest files defining firefox-ui-update tests.
-        """),
-
     'PUPPETEER_FIREFOX_MANIFESTS': (ManifestparserManifestList, list,
                                     """List of manifest files defining puppeteer unit tests for Firefox.
         """),
 
     'MARIONETTE_LAYOUT_MANIFESTS': (ManifestparserManifestList, list,
                                     """List of manifest files defining marionette-layout tests.
         """),
 
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -3,16 +3,18 @@
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! A struct to encapsulate all the style fixups and flags propagations
 //! a computed style needs in order for it to adhere to the CSS spec.
 
 use crate::dom::TElement;
 use crate::properties::computed_value_flags::ComputedValueFlags;
 use crate::properties::longhands::display::computed_value::T as Display;
+#[cfg(feature = "gecko")]
+use crate::values::specified::box_::DisplayInside;
 use crate::properties::longhands::float::computed_value::T as Float;
 use crate::properties::longhands::overflow_x::computed_value::T as Overflow;
 use crate::properties::longhands::position::computed_value::T as Position;
 use crate::properties::{self, ComputedValues, StyleBuilder};
 use app_units::Au;
 
 /// A struct that implements all the adjustment methods.
 ///
@@ -170,17 +172,19 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
         }
 
         element.map_or(false, |e| e.skip_item_display_fixup())
     }
 
     /// Apply the blockification rules based on the table in CSS 2.2 section 9.7.
     /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo>
     /// A ::marker pseudo-element with 'list-style-position:outside' needs to
-    /// have its 'display' blockified.
+    /// have its 'display' blockified, unless the ::marker is for an inline
+    /// list-item (for which 'list-style-position:outside' behaves as 'inside').
+    /// https://drafts.csswg.org/css-lists-3/#list-style-position-property
     fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>)
     where
         E: TElement,
     {
         use crate::computed_values::list_style_position::T as ListStylePosition;
 
         let mut blockify = false;
         macro_rules! blockify_if {
@@ -189,31 +193,29 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
                     blockify = $if_what;
                 }
             };
         }
 
         let is_root = self.style.pseudo.is_none() && element.map_or(false, |e| e.is_root());
         blockify_if!(is_root);
         if !self.skip_item_display_fixup(element) {
-            blockify_if!(layout_parent_style
-                .get_box()
-                .clone_display()
-                .is_item_container());
+            let parent_display = layout_parent_style.get_box().clone_display();
+            blockify_if!(parent_display.is_item_container());
         }
 
         let is_item_or_root = blockify;
 
         blockify_if!(self.style.floated());
         blockify_if!(self.style.out_of_flow_positioned());
+        #[cfg(feature = "gecko")]
         blockify_if!(
             self.style.pseudo.map_or(false, |p| p.is_marker()) &&
-                self.style.get_parent_list().clone_list_style_position() ==
-                    ListStylePosition::Outside
-        );
+             self.style.get_parent_list().clone_list_style_position() == ListStylePosition::Outside &&
+             layout_parent_style.get_box().clone_display().inside() != DisplayInside::Inline);
 
         if !blockify {
             return;
         }
 
         let display = self.style.get_box().clone_display();
         let blockified_display = display.equivalent_block_display(is_root);
         if display != blockified_display {
--- a/servo/components/style/values/specified/box.rs
+++ b/servo/components/style/values/specified/box.rs
@@ -11,16 +11,17 @@ use crate::properties::{PropertyId, Shor
 use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount;
 use crate::values::generics::box_::Perspective as GenericPerspective;
 use crate::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword};
 use crate::values::specified::length::{LengthPercentage, NonNegativeLength};
 use crate::values::specified::{AllowQuirks, Number};
 use crate::values::{CustomIdent, KeyframesName};
 use crate::Atom;
 use cssparser::Parser;
+use num_traits::FromPrimitive;
 use selectors::parser::SelectorParseErrorKind;
 use std::fmt::{self, Write};
 use style_traits::{CssWriter, KeywordsCollectFn, ParseError};
 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
 
 #[cfg(feature = "gecko")]
 fn moz_display_values_enabled(context: &ParserContext) -> bool {
     context.in_ua_or_chrome_sheet() ||
@@ -28,149 +29,320 @@ fn moz_display_values_enabled(context: &
 }
 
 #[cfg(feature = "gecko")]
 fn moz_box_display_values_enabled(context: &ParserContext) -> bool {
     context.in_ua_or_chrome_sheet() ||
         static_prefs::pref!("layout.css.xul-box-display-values.content.enabled")
 }
 
-#[cfg(any(feature = "gecko", feature = "servo-layout-2013"))]
+#[cfg(feature = "servo-layout-2013")]
 fn parse_unimplemented_in_servo_2020(_context: &ParserContext) -> bool {
     true
 }
 
 #[cfg(feature = "servo-layout-2020")]
 fn parse_unimplemented_in_servo_2020(_context: &ParserContext) -> bool {
     servo_config::prefs::pref_map()
         .get("layout.2020.unimplemented")
         .as_bool()
         .unwrap_or(false)
 }
 
 /// Defines an element’s display type, which consists of
 /// the two basic qualities of how an element generates boxes
 /// <https://drafts.csswg.org/css-display/#propdef-display>
-///
-///
-/// NOTE(emilio): Order is important in Gecko!
-///
-/// If you change it, make sure to take a look at the
-/// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and
-/// ensure it's still correct!
+#[allow(missing_docs)]
+#[derive(
+    MallocSizeOf,
+    ToShmem,
+    Clone,
+    Copy,
+    Debug,
+    Eq,
+    FromPrimitive,
+    Hash,
+    PartialEq,
+    ToCss,
+)]
+#[cfg(feature = "gecko")]
+#[repr(u8)]
+pub enum DisplayOutside {
+    None = 0,
+    Inline,
+    Block,
+    TableCaption,
+    InternalTable,
+    InternalRuby,
+    XUL,
+}
+
+#[allow(missing_docs)]
+#[derive(
+    MallocSizeOf,
+    ToShmem,
+    Clone,
+    Copy,
+    Debug,
+    Eq,
+    FromPrimitive,
+    Hash,
+    PartialEq,
+    ToCss,
+)]
+#[cfg(feature = "gecko")]
+#[repr(u8)]
+pub enum DisplayInside {
+    None = 0,
+    Contents,
+    Block,
+    FlowRoot,
+    Inline,
+    Flex,
+    Grid,
+    Table,
+    TableRowGroup,
+    TableColumn,
+    TableColumnGroup,
+    TableHeaderGroup,
+    TableFooterGroup,
+    TableRow,
+    TableCell,
+    Ruby,
+    RubyBase,
+    RubyBaseContainer,
+    RubyText,
+    RubyTextContainer,
+    WebkitBox,
+    MozBox,
+    MozInlineBox,
+    MozGrid,
+    MozInlineGrid,
+    MozGridGroup,
+    MozGridLine,
+    MozStack,
+    MozInlineStack,
+    MozDeck,
+    MozGroupbox,
+    MozPopup,
+    Flow, // only used for parsing, not computed value
+}
+
 #[allow(missing_docs)]
 #[derive(
     Clone,
     Copy,
     Debug,
     Eq,
     FromPrimitive,
     Hash,
     MallocSizeOf,
+    PartialEq,
+    ToComputedValue,
+    ToResolvedValue,
+    ToShmem,
+)]
+#[cfg(feature = "gecko")]
+#[repr(transparent)]
+pub struct Display(u16);
+