Merge autoland to mozilla-central. a=merge
authorDaniel Varga <dvarga@mozilla.com>
Wed, 14 Aug 2019 00:50:57 +0300
changeset 488122 eff8c62bdeb71f78dbf0e2324b31d094355cf3dc
parent 488121 f0697743acd0901520396029ee3c2aa9509a8c56 (current diff)
parent 487701 5d84ea4c115a32a6a7b4d8e9a024cbda22681839 (diff)
child 488123 d3deef805f92d4a899d7b6de76b347328f33e54b
push id92640
push usercbrindusan@mozilla.com
push dateThu, 15 Aug 2019 09:51:33 +0000
treeherderautoland@caf7bd7998b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone70.0a1
first release with
nightly linux32
eff8c62bdeb7 / 70.0a1 / 20190813215212 / files
nightly linux64
eff8c62bdeb7 / 70.0a1 / 20190813215212 / files
nightly mac
eff8c62bdeb7 / 70.0a1 / 20190813215212 / files
nightly win32
eff8c62bdeb7 / 70.0a1 / 20190813215212 / files
nightly win64
eff8c62bdeb7 / 70.0a1 / 20190813215212 / 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
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -19,17 +19,17 @@ replace-with = "vendored-sources"
 
 [source."https://github.com/rust-lang-nursery/packed_simd"]
 git = "https://github.com/hsivonen/packed_simd"
 branch = "rust_1_32"
 replace-with = "vendored-sources"
 
 [source."https://github.com/CraneStation/Cranelift"]
 git = "https://github.com/CraneStation/Cranelift"
-rev = "c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
+rev = "b2c69b1d032626c67f685080dbf6971d3b9ffa43"
 replace-with = "vendored-sources"
 
 [source."https://github.com/ChunMinChang/coreaudio-sys"]
 git = "https://github.com/ChunMinChang/coreaudio-sys"
 branch = "gecko-build"
 replace-with = "vendored-sources"
 
 [source."https://github.com/alexcrichton/mio-named-pipes"]
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -193,18 +193,18 @@ dependencies = [
  "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "baldrdash"
 version = "0.1.0"
 dependencies = [
  "bindgen 0.51.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "cranelift-codegen 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
- "cranelift-wasm 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
+ "cranelift-codegen 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
+ "cranelift-wasm 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
  "env_logger 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "base64"
 version = "0.9.3"
@@ -623,67 +623,67 @@ name = "cose-c"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.37.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f#c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
-dependencies = [
- "cranelift-entity 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
+version = "0.38.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43#b2c69b1d032626c67f685080dbf6971d3b9ffa43"
+dependencies = [
+ "cranelift-entity 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.37.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f#c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
-dependencies = [
- "cranelift-bforest 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
- "cranelift-codegen-meta 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
- "cranelift-entity 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
+version = "0.38.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43#b2c69b1d032626c67f685080dbf6971d3b9ffa43"
+dependencies = [
+ "cranelift-bforest 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
+ "cranelift-codegen-meta 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
+ "cranelift-entity 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.37.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f#c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
-dependencies = [
- "cranelift-entity 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
+version = "0.38.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43#b2c69b1d032626c67f685080dbf6971d3b9ffa43"
+dependencies = [
+ "cranelift-entity 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.37.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f#c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
+version = "0.38.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43#b2c69b1d032626c67f685080dbf6971d3b9ffa43"
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.37.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f#c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
-dependencies = [
- "cranelift-codegen 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
+version = "0.38.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43#b2c69b1d032626c67f685080dbf6971d3b9ffa43"
+dependencies = [
+ "cranelift-codegen 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "target-lexicon 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "cranelift-wasm"
-version = "0.37.0"
-source = "git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f#c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
-dependencies = [
- "cranelift-codegen 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
- "cranelift-entity 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
- "cranelift-frontend 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)",
+version = "0.38.0"
+source = "git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43#b2c69b1d032626c67f685080dbf6971d3b9ffa43"
+dependencies = [
+ "cranelift-codegen 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
+ "cranelift-entity 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
+ "cranelift-frontend 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)",
  "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "failure_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "wasmparser 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crc"
@@ -3954,22 +3954,22 @@ dependencies = [
 "checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
 "checksum cookie 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1465f8134efa296b4c19db34d909637cb2bf0f7aaf21299e23e18fa29ac557cf"
 "checksum core-foundation 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4e2640d6d0bf22e82bed1b73c6aef8d5dd31e5abe6666c57e6d45e2649f4f887"
 "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
 "checksum core-graphics 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "62ceafe1622ffc9a332199096841d0ff9912ec8cf8f9cde01e254a7d5217cd10"
 "checksum core-text 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f46450d6f2397261af420b4ccce23807add2e45fa206410a03d66fb7f050ae"
 "checksum cose 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72fa26cb151d3ae4b70f63d67d0fed57ce04220feafafbae7f503bef7aae590d"
 "checksum cose-c 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "49726015ab0ca765144fcca61e4a7a543a16b795a777fa53f554da2fffff9a94"
-"checksum cranelift-bforest 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)" = "<none>"
-"checksum cranelift-codegen 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)" = "<none>"
-"checksum cranelift-codegen-meta 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)" = "<none>"
-"checksum cranelift-entity 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)" = "<none>"
-"checksum cranelift-frontend 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)" = "<none>"
-"checksum cranelift-wasm 0.37.0 (git+https://github.com/CraneStation/Cranelift?rev=c927680fd42a56efcc9a5ee59e814d9eef6c5b4f)" = "<none>"
+"checksum cranelift-bforest 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)" = "<none>"
+"checksum cranelift-codegen 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)" = "<none>"
+"checksum cranelift-codegen-meta 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)" = "<none>"
+"checksum cranelift-entity 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)" = "<none>"
+"checksum cranelift-frontend 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)" = "<none>"
+"checksum cranelift-wasm 0.38.0 (git+https://github.com/CraneStation/Cranelift?rev=b2c69b1d032626c67f685080dbf6971d3b9ffa43)" = "<none>"
 "checksum crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd5d02c0aac6bd68393ed69e00bbc2457f3e89075c6349db7189618dc4ddc1d7"
 "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
 "checksum crossbeam-channel 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8d4f5844607ce8da3fff431e7dba56cda8bfcc570aa50bee36adba8a32b8cad7"
 "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13"
 "checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4"
 "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
 "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c"
 "checksum cssparser 0.25.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a921abc45ea75c2c817d951caeda31b94539d09a6b5e8d58a857b3b35c9c3894"
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -58,16 +58,16 @@ codegen-units = 1
 
 [patch.crates-io]
 libudev-sys = { path = "dom/webauthn/libudev-sys" }
 winapi = { git = "https://github.com/froydnj/winapi-rs", branch = "aarch64" }
 packed_simd = { git = "https://github.com/hsivonen/packed_simd", branch = "rust_1_32" }
 
 [patch.crates-io.cranelift-codegen]
 git = "https://github.com/CraneStation/Cranelift"
-rev = "c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
+rev = "b2c69b1d032626c67f685080dbf6971d3b9ffa43"
 
 [patch.crates-io.cranelift-wasm]
 git = "https://github.com/CraneStation/Cranelift"
-rev = "c927680fd42a56efcc9a5ee59e814d9eef6c5b4f"
+rev = "b2c69b1d032626c67f685080dbf6971d3b9ffa43"
 
 [patch.crates-io.coreaudio-sys]
 path = "third_party/rust/coreaudio-sys"
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -324,19 +324,16 @@ pref("browser.urlbar.switchTabs.adoptInt
 // Whether addresses and search results typed into the address bar
 // should be opened in new tabs by default.
 pref("browser.urlbar.openintab", false);
 
 // This is disabled until Bug 1340663 figures out the remaining requirements.
 pref("browser.urlbar.usepreloadedtopurls.enabled", false);
 pref("browser.urlbar.usepreloadedtopurls.expire_days", 14);
 
-// Enable the new Address Bar code.
-pref("browser.urlbar.quantumbar", true);
-
 pref("browser.urlbar.openViewOnFocus", false);
 
 pref("browser.altClickSave", false);
 
 // Enable logging downloads operations to the Console.
 pref("browser.download.loglevel", "Error");
 
 // Number of milliseconds to wait for the http headers (and thus
--- a/browser/base/content/browser-siteProtections.js
+++ b/browser/base/content/browser-siteProtections.js
@@ -1872,16 +1872,22 @@ var gProtectionsHandler = {
       this._protectionsPopupTPSwitch.setAttribute(
         "aria-label",
         gNavigatorBundle.getFormattedString("protections.enableAriaLabel", [
           host,
         ])
       );
     }
 
+    // Show the blocked tracker counter if it is not updating. We can sure the
+    // data in the tracker counter is up-to-date here, so we can show it.
+    if (!this._updatingFooter) {
+      this._protectionsPopupTrackersCounterBox.toggleAttribute("showing", true);
+    }
+
     // Update the tooltip of the blocked tracker counter.
     this.maybeUpdateEarliestRecordedDateTooltip();
   },
 
   disableForCurrentPage() {
     ContentBlockingAllowList.add(gBrowser.selectedBrowser);
     PanelMultiView.hidePopup(this._protectionsPopup);
     BrowserReload();
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -868,17 +868,20 @@ var gSync = {
     for (let target of targets) {
       let type, lastModified;
       if (target.clientRecord) {
         type = Weave.Service.clientsEngine.getClientType(
           target.clientRecord.id
         );
         lastModified = new Date(target.clientRecord.serverLastModified * 1000);
       } else {
-        type = target.type === "desktop" ? "desktop" : "phone"; // Normalizing the FxA types just in case.
+        const validFxADeviceTypes = ["desktop", "phone", "tablet", "tv", "vr"];
+        type = validFxADeviceTypes.includes(target.type)
+          ? target.type
+          : "desktop";
         lastModified = null;
       }
       addTargetDevice(target.id, target.name, type, lastModified);
     }
 
     // "Send to All Devices" menu item
     if (targets.length > 1) {
       const separator = createDeviceNodeFn();
--- a/browser/components/controlcenter/content/protectionsPanel.inc.xul
+++ b/browser/components/controlcenter/content/protectionsPanel.inc.xul
@@ -13,17 +13,16 @@
        orient="vertical">
 
   <panelmultiview id="protections-popup-multiView"
                   mainViewId="protections-popup-mainView">
     <panelview id="protections-popup-mainView"
                role="document"
                descriptionheightworkaround="true">
       <hbox id="protections-popup-mainView-panel-header"
-            flex="1"
             onclick="gProtectionsHandler.onHeaderClicked(event);">
         <label id="protections-popup-main-header-label" flex="1"
                role="heading" aria-level="1">
           <html:span id="protections-popup-mainView-panel-header-span"/>
         </label>
         <toolbarbutton id="protections-popup-info-button" class="panel-info-button"
                        aria-label="&protections.etpMoreInfo.label;">
           <image/>
--- a/browser/components/places/content/places-menupopup.js
+++ b/browser/components/places/content/places-menupopup.js
@@ -129,17 +129,17 @@
         `;
         default:
           return s;
       }
     }
 
     get markup() {
       return `
-      <html:link rel="stylesheet" href="chrome://global/content/widgets.css" />
+      <html:link rel="stylesheet" href="chrome://global/skin/global.css" />
       <html:style>${this.commonStyles}${this.styles}</html:style>
       <hbox flex="1" part="innerbox">
         <vbox class="menupopup-drop-indicator-bar" hidden="true">
           <image class="menupopup-drop-indicator" mousethrough="always"></image>
         </vbox>
         <arrowscrollbox class="popup-internal-box" flex="1" orient="vertical"
                         smoothscroll="false">
           <html:slot></html:slot>
@@ -692,17 +692,17 @@
         ".panel-arrowcontainer": "side,panelopen",
         ".panel-arrow": "side",
         ".panel-arrowcontent": "side,align,dir,orient,pack",
       };
     }
 
     get markup() {
       return `
-      <html:link rel="stylesheet" href="chrome://global/content/widgets.css" />
+      <html:link rel="stylesheet" href="chrome://global/skin/global.css" />
       <html:style>${this.commonStyles}</html:style>
       <vbox class="panel-arrowcontainer" flex="1">
         <box class="panel-arrowbox">
           <image class="panel-arrow"></image>
         </box>
         <box class="panel-arrowcontent" part="arrowcontent" flex="1">
           <vbox class="menupopup-drop-indicator-bar" hidden="true">
             <image class="menupopup-drop-indicator" mousethrough="always"></image>
--- a/browser/components/sessionstore/test/browser_cookies_privacy.js
+++ b/browser/components/sessionstore/test/browser_cookies_privacy.js
@@ -1,15 +1,15 @@
 "use strict";
 
 // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision.
 const MAX_EXPIRY = Math.pow(2, 62);
 
 function addCookie(scheme, secure = false) {
-  let cookie = createTestCookie("http", secure);
+  let cookie = createTestCookie(scheme, secure);
   Services.cookies.add(
     cookie.host,
     cookie.path,
     cookie.name,
     cookie.value,
     cookie.secure,
     /* isHttpOnly = */ false,
     /* isSession = */ true,
--- a/browser/components/urlbar/UrlbarPrefs.jsm
+++ b/browser/components/urlbar/UrlbarPrefs.jsm
@@ -103,19 +103,16 @@ const PREF_URLBAR_DEFAULTS = new Map([
 
   // Whether addresses and search results typed into the address bar
   // should be opened in new tabs by default.
   ["openintab", false],
 
   // Whether to open the urlbar view when the input field is focused by the user.
   ["openViewOnFocus", false],
 
-  // Whether the quantum bar is enabled.
-  ["quantumbar", false],
-
   // Whether speculative connections should be enabled.
   ["speculativeConnect.enabled", true],
 
   // Results will include the user's bookmarks when this is true.
   ["suggest.bookmark", true],
 
   // Results will include the user's history when this is true.
   ["suggest.history", true],
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -80,17 +80,16 @@ class UrlbarView {
 
     // Disable one off search buttons from appearing if
     // the contextual tip is the only item in the urlbar view.
     if (this.visibleItemCount == 0) {
       this._enableOrDisableOneOffSearches(false);
     }
 
     this._openPanel();
-    this.input.focus();
   }
 
   /**
    * Hides the contextual tip.
    */
   hideContextualTip() {
     if (this.contextualTip) {
       this.contextualTip.hide();
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -6,21 +6,27 @@
 
 /* Hide all conditional elements by default. */
 :-moz-any([when-connection],[when-customroot],[when-mixedcontent],[when-ciphers],[when-loginforms]) {
   display: none;
 }
 
 #identity-popup,
 #protections-popup {
-  font-size: 1.1em;
-  --popup-width: 33em;
+  font-size: 1.18rem;
+  --popup-width: 36.36rem;
   /* Set default fill for icons in the identity popup.
      Individual icons can override this. */
   fill: currentColor;
+  --horizontal-padding: 2.1rem;
+  --vertical-section-padding: 0.9em;
+}
+
+#protections-popup[toast] {
+  --popup-width: 32rem;
 }
 
 /* This is used by screenshots tests to hide intermittently different
  * identity popup shadows (see bug 1425253). */
 #identity-popup.no-shadow {
   -moz-window-shadow: none;
 }
 
@@ -193,23 +199,24 @@
 #protections-popup-mainView-panel-header > label,
 #protections-popup-mainView-panel-header > description,
 #protections-popup-trackersView > .panel-header,
 #protections-popup-sendReportView > .panel-header,
 .protections-popup-category-label,
 .protections-popup-category-state-label,
 #protections-popup-content > description,
 .protections-popup-footer-button-label,
-#protections-popup-trackers-blocked-counter-description {
+#protections-popup-trackers-blocked-counter-description,
+#protections-popup-sendReportView-heading > description {
   margin: 0;
 }
 
 #identity-popup-mainView-panel-header,
 #protections-popup-mainView-panel-header {
-  padding: 4px 1em;
+  padding: var(--vertical-section-padding) var(--horizontal-padding);
   min-height: 40px;
   -moz-box-pack: center;
   -moz-box-align: center;
 }
 
 #protections-popup > .panel-arrowcontainer > .panel-arrowbox > .panel-arrow {
   transition-property: fill;
   transition-timing-function: var(--animation-easing-function);
@@ -237,38 +244,52 @@
   background: radial-gradient(circle at top right, #C689FF, #00B3F4);
 }
 
 :root[lwt-popup-brighttext] #protections-popup-mainView-panel-header:-moz-locale-dir(rtl) {
   background: radial-gradient(circle at top left, #C689FF, #00B3F4);
 }
 
 #identity-popup-mainView-panel-header-span,
+#protections-popup-mainView-panel-header-span {
+  font-weight: 600;
+}
+
+#identity-popup-mainView-panel-header-span,
 #protections-popup-mainView-panel-header-span,
 #protections-popup-toast-panel-tp-on-desc,
 #protections-popup-toast-panel-tp-off-desc {
   display: inline-block;
-  font-weight: 600;
   text-align: center;
   overflow-wrap: break-word;
   /* This is needed for the overflow-wrap to work correctly.
-   * 33em is the panel width, panel-header has 1em padding on each side. */
-  max-width: calc(var(--popup-width) - 2em);
-}
-
-#protections-popup-mainView-panel-header-span,
-#protections-popup-toast-panel-tp-on-desc,
-#protections-popup-toast-panel-tp-off-desc {
-  max-width: calc(var(--popup-width) - 2em);
+   * The panel-header has 1em padding on each side. */
+  max-width: calc(var(--popup-width) - 2 * var(--horizontal-padding));
 }
 
 #protections-popup[toast] #protections-popup-info-button {
   display: none;
 }
 
+#protections-popup-info-button {
+  margin: 0;
+  padding: 0;
+  border-radius: 0;
+  outline: 3px solid transparent;
+  -moz-outline-radius: var(--toolbarbutton-border-radius);
+}
+
+#protections-popup-info-button:hover {
+  outline-color: var(--toolbarbutton-hover-background);
+}
+
+#protections-popup-info-button:not(:hover)[checked] {
+  outline-color: var(--toolbarbutton-active-background);
+}
+
 #identity-popup-permissions-content > description,
 #protections-popup-content > description {
   color: var(--panel-disabled-color);
 }
 
 /* This element needs the pre-wrap because we add newlines to it in the code. */
 #identity-popup-content-supplemental {
   white-space: pre-wrap;
@@ -387,17 +408,17 @@ description#identity-popup-content-verif
 #protections-popup-sendReportView-footer > button {
   flex: 1;
 }
 
 #identity-popup-breakageReportView-heading,
 #identity-popup-breakageReportView-body,
 #protections-popup-sendReportView-heading,
 #protections-popup-sendReportView-body {
-  padding: 16px;
+  padding: var(--vertical-section-padding) var(--horizontal-padding);
 }
 
 .identity-popup-breakageReportView-collection-section,
 .protections-popup-sendReportView-collection-section {
   margin-bottom: 16px;
 }
 
 #identity-popup-breakageReportView-body,
@@ -607,24 +628,23 @@ description#identity-popup-content-verif
   margin: 4.85em 7.25em;
   font-size: 1.1em;
   text-align: center;
   color: #737373;
 }
 
 #protections-popup-content {
   padding: 0;
-  margin: 0 0 10px 0;
+  margin: 0 0 var(--vertical-section-padding) 0;
 }
 
 #protections-popup-blocking-section-header,
 #protections-popup-not-blocking-section-header {
-  font-weight: 500;
   margin: 0;
-  padding: 10px calc(10px + 1em);
+  padding: var(--vertical-section-padding) var(--horizontal-padding);
   color: #737373;
   display: none;
 }
 
 :root[lwt-popup-brighttext] #protections-popup-no-trackers-found-description,
 :root[lwt-popup-brighttext] #protections-popup-blocking-section-header,
 :root[lwt-popup-brighttext] #protections-popup-not-blocking-section-header {
   color: #f9f9fa;
@@ -647,17 +667,17 @@ description#identity-popup-content-verif
   padding: 0;
   margin: 0;
 }
 
 #protections-popup-blocking-section-header {
   grid-row: 1;
 }
 
-.protections-popup-category.blocked:not([hidden]) ~ #protections-popup-blocking-section-header,
+#protections-popup:not([hasException]) .protections-popup-category.blocked:not([hidden]) ~ #protections-popup-blocking-section-header,
 .protections-popup-category:not(.blocked):not([hidden]) ~ #protections-popup-not-blocking-section-header {
   display: flex;
 }
 
 #protections-popup:not([hasException]) #protections-popup-category-tracking-protection.blocked {
   grid-row: 2;
 }
 
@@ -793,17 +813,17 @@ description#identity-popup-content-verif
 }
 
 .protections-popup-category,
 .identity-popup-permission-item {
   min-height: 24px;
 }
 
 .protections-popup-category {
-  padding: 0 calc(10px + 1em);
+  padding: 0 var(--horizontal-padding);
 }
 
 .identity-popup-permission-item {
   padding-inline-end: 8px;
   margin-top: 0.25em;
 }
 
 #identity-popup-permission-reload-hint,
@@ -909,55 +929,68 @@ description#identity-popup-content-verif
   display: none;
 }
 
 #protections-popup:not([hasException])[toast] #protections-popup-toast-panel-tp-on-desc,
 #protections-popup[hasException][toast] #protections-popup-toast-panel-tp-off-desc {
   display: unset;
 }
 
+#protections-popup-siteNotWorkingView-header,
 #protections-popup-tp-switch-section {
-  padding: 4px;
+  padding: var(--vertical-section-padding) var(--horizontal-padding);
+}
+
+#protections-popup-siteNotWorkingView-body > label,
+.protections-popup-tp-switch-label-box > label {
+  margin: 0;
+}
+
+#protections-popup-tp-switch-breakage-link {
+  margin-top: calc(0.5 * var(--vertical-section-padding)) !important;
 }
 
 #protections-popup[hasException] #protections-popup-tp-switch-section {
   background: repeating-linear-gradient(
     -56deg,
     var(--arrowpanel-dimmed),
     var(--arrowpanel-dimmed) 10px,
     transparent 10px,
     transparent 20px
   );
 }
 
 #protections-popup-siteNotWorkingView-header {
   border-bottom: 1px solid var(--panel-separator-color);
 }
 
+#protections-popup-siteNotWorkingView-body {
+    padding: var(--vertical-section-padding) var(--horizontal-padding);
+}
+
 #protections-popup-siteNotWorkingView-body,
 .protections-popup-tp-switch-label-box,
 .protections-popup-tp-switch-box {
-  padding: 4px 1em;
   min-height: 40px;
   -moz-box-pack: center;
   /* This is needed in order to make 'position' working for the badge alongside
      the TP switch toggle.*/
   position: relative;
 }
 
 /* This is needed in order to show a correct height if the 'Site not working?'
    link is not displaying. */
 #protections-popup-tp-switch-section[short] > .protections-popup-tp-switch-label-box,
 #protections-popup-tp-switch-section[short] > .protections-popup-tp-switch-box {
   min-height: 30px;
 }
 
 .protections-popup-tp-switch-on-header,
 .protections-popup-tp-switch-off-header {
-  font-weight: 500;
+  font-weight: 600;
 }
 
 .protections-popup-tp-switch {
   -moz-appearance: none;
   box-sizing: border-box;
   min-width: 26px;
   border-radius: 10px;
   background-color: var(--arrowpanel-dimmed-even-further);
@@ -1000,17 +1033,17 @@ description#identity-popup-content-verif
 #protections-popup-siteNotWorkingView-body-issue-list {
   padding-inline-start: 1em;
 }
 
 /* Protection popup footer categories */
 
 .protections-popup-footer-button {
   min-height: 24px;
-  padding-inline-start: 2em;
+  padding-inline-start: var(--horizontal-padding);
   margin: 0;
 }
 
 .protections-popup-footer-icon {
   -moz-context-properties: fill;
   width: 16px;
   height: 16px;
 }
@@ -1019,21 +1052,21 @@ description#identity-popup-content-verif
   list-style-image: url(chrome://browser/skin/settings.svg);
 }
 
 .protections-popup-show-report-icon {
   list-style-image: url(chrome://browser/skin/controlcenter/dashboard.svg);
 }
 
 #protections-popup-footer {
-  padding: 6px 0;
+  padding: var(--vertical-section-padding) 0;
 }
 
 #protections-popup-trackers-blocked-counter-box {
-  margin-inline-end: calc(4px + 1em);
+  margin-inline-end: var(--horizontal-padding);
   visibility: hidden;
   opacity: 0;
   transition: opacity 200ms linear;
 }
 
 #protections-popup-trackers-blocked-counter-box[showing] {
   visibility: visible;
   opacity: 1;
@@ -1051,24 +1084,24 @@ description#identity-popup-content-verif
   background: #00B3F4;
   border: 2px rgba(0,144,237,0.5) solid;
   content: " ";
   border-radius: 6px;
   position: absolute;
   margin: auto;
   top: 0;
   bottom: 12px;
-  left: calc(1em + 21px);
+  left: 21px;
   height: 6px;
   width: 6px;
 }
 
 #protections-popup-tp-switch:not([enabled])[showdotindicator]:-moz-locale-dir(rtl)::after {
   left: unset;
-  right: calc(1em + 20px);
+  right: 21px;
 }
 
 .protections-popup-description {
   border-bottom: 1px solid var(--panel-separator-color);
 }
 
 .protections-popup-description > description {
   margin: 10px 24px;
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/icons/device-tv.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M14.5 2h-13A1.5 1.5 0 0 0 0 3.5v8A1.5 1.5 0 0 0 1.5 13h3a0.5 0.5 0 0 0 0 1h7a0.5 0.5 0 0 0 0-1h3a1.5 1.5 0 0 0 1.5-1.5v-8A1.5 1.5 0 0 0 14.5 2zM14 11H2V4h12z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/icons/device-vr.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M13.5 3h-11A2.5 2.5 0 0 0 0 5.5v5A2.5 2.5 0 0 0 2.5 13h3a2 2 0 0 0 1.56-0.75l0.1-0.15 0.38-0.7a0.5 0.5 0 0 1 0.88 0l0.38 0.7 0.1 0.15A2 2 0 0 0 10.46 13h3A2.5 2.5 0 0 0 16 10.5v-5A2.5 2.5 0 0 0 13.5 3zm0.5 7.5a0.5 0.5 0 0 1-0.5 0.5h-3l-0.3-0.56a2.51 2.51 0 0 0-4.4 0L5.5 11h-3A0.5 0.5 0 0 1 2 10.5v-5A0.5 0.5 0 0 1 2.5 5h11A0.5 0.5 0 0 1 14 5.5z"/>
+  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M12.5 6h-9a0.5 0.5 0 0 0 0 1h9a0.5 0.5 0 0 0 0-1z"/>
+</svg>
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -160,16 +160,18 @@
   skin/classic/browser/chevron.svg                    (../shared/icons/chevron.svg)
   skin/classic/browser/chevron-animation.svg          (../shared/icons/chevron-animation.svg)
   skin/classic/browser/check-animation.svg            (../shared/icons/check-animation.svg)
   skin/classic/browser/customize.svg                  (../shared/icons/customize.svg)
   skin/classic/browser/developer.svg                  (../shared/icons/developer.svg)
   skin/classic/browser/device-phone.svg               (../shared/icons/device-phone.svg)
   skin/classic/browser/device-tablet.svg              (../shared/icons/device-tablet.svg)
   skin/classic/browser/device-desktop.svg             (../shared/icons/device-desktop.svg)
+  skin/classic/browser/device-tv.svg                  (../shared/icons/device-tv.svg)
+  skin/classic/browser/device-vr.svg                  (../shared/icons/device-vr.svg)
   skin/classic/browser/edit-copy.svg                  (../shared/icons/edit-copy.svg)
   skin/classic/browser/edit-cut.svg                   (../shared/icons/edit-cut.svg)
   skin/classic/browser/edit-paste.svg                 (../shared/icons/edit-paste.svg)
   skin/classic/browser/folder.svg                     (../shared/icons/folder.svg)
   skin/classic/browser/forget.svg                     (../shared/icons/forget.svg)
   skin/classic/browser/forward.svg                    (../shared/icons/forward.svg)
   skin/classic/browser/fullscreen.svg                 (../shared/icons/fullscreen.svg)
   skin/classic/browser/fullscreen-exit.svg            (../shared/icons/fullscreen-exit.svg)
--- a/browser/themes/shared/syncedtabs/sidebar.inc.css
+++ b/browser/themes/shared/syncedtabs/sidebar.inc.css
@@ -118,16 +118,24 @@ body {
 .item.client[clientType=tablet] > .item-title-container > .item-icon-container {
   background-image: url("chrome://browser/skin/device-tablet.svg");
 }
 
 .item.client[clientType=desktop] > .item-title-container > .item-icon-container {
   background-image: url("chrome://browser/skin/device-desktop.svg");
 }
 
+.item.client[clientType=tv] > .item-title-container > .item-icon-container {
+  background-image: url("chrome://browser/skin/device-tv.svg");
+}
+
+.item.client[clientType=vr] > .item-title-container > .item-icon-container {
+  background-image: url("chrome://browser/skin/device-vr.svg");
+}
+
 .item.tab > .item-title-container > .item-icon-container {
   background-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
 .item-icon-container {
   min-width: 16px;
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -215,16 +215,24 @@
 .pageAction-sendToDevice-device[clientType=tablet] {
   list-style-image: url("chrome://browser/skin/device-tablet.svg");
 }
 
 .pageAction-sendToDevice-device[clientType=desktop] {
   list-style-image: url("chrome://browser/skin/device-desktop.svg");
 }
 
+.pageAction-sendToDevice-device[clientType=tv] {
+  list-style-image: url("chrome://browser/skin/device-tv.svg");
+}
+
+.pageAction-sendToDevice-device[clientType=vr] {
+  list-style-image: url("chrome://browser/skin/device-vr.svg");
+}
+
 .pageAction-sendToDevice-device.signintosync,
 #pageAction-panel-sendToDevice-fxa,
 #pageAction-urlbar-sendToDevice-fxa {
   list-style-image: url("chrome://browser/skin/sync.svg");
 }
 
 #pageAction-panel-addSearchEngine > .toolbarbutton-badge-stack > .toolbarbutton-icon {
   width: 16px;
--- a/devtools/client/application/application.css
+++ b/devtools/client/application/application.css
@@ -6,16 +6,17 @@
 * Global styles
 */
 @import "resource://devtools/client/application/src/base.css";
 
 /*
 * Components
 */
 @import "resource://devtools/client/application/src/components/App.css";
+@import "resource://devtools/client/application/src/components/manifest/ManifestPage.css";
 @import "resource://devtools/client/application/src/components/service-workers/Worker.css";
 @import "resource://devtools/client/application/src/components/service-workers/WorkerList.css";
 @import "resource://devtools/client/application/src/components/service-workers/WorkerListEmpty.css";
 @import "resource://devtools/client/application/src/components/service-workers/WorkersPage.css";
 @import "resource://devtools/client/application/src/components/ui/UIButton.css";
 
 html,
 body,
--- a/devtools/client/application/src/components/layout/PageContainer.js
+++ b/devtools/client/application/src/components/layout/PageContainer.js
@@ -8,32 +8,39 @@ const {
   createFactory,
   PureComponent,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const { PAGE_TYPES } = require("../../constants");
 
+const ManifestPage = createFactory(require("../manifest/ManifestPage"));
 const WorkersPage = createFactory(require("../service-workers/WorkersPage"));
 
 class PageContainer extends PureComponent {
   static get propTypes() {
     return {
       page: PropTypes.oneOf(Object.values(PAGE_TYPES)),
     };
   }
 
   render() {
     let component = null;
 
     switch (this.props.page) {
+      case PAGE_TYPES.MANIFEST:
+        component = ManifestPage({});
+        break;
       case PAGE_TYPES.SERVICE_WORKERS:
         component = WorkersPage({});
         break;
+      default:
+        console.error("Unknown path. Can not direct to a page.");
+        return null;
     }
 
     return component;
   }
 }
 
 function mapStateToProps(state) {
   return {
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/manifest/ManifestPage.css
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+.manifest-page {
+	height: 100vh;
+  padding: 0 2rem;
+  display: grid;
+  -moz-user-select: none;
+
+  /* slipt up in components in https://bugzilla.mozilla.org/show_bug.cgi?id=1566011 */
+  align-items: center;
+  justify-content: center;
+  font-size: var(--title-10-font-size);
+  color: var(--theme-toolbar-color);
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/manifest/ManifestPage.js
@@ -0,0 +1,30 @@
+/* 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 {
+  createFactory,
+  PureComponent,
+} = require("devtools/client/shared/vendor/react");
+const {
+  section,
+} = require("devtools/client/shared/vendor/react-dom-factories");
+
+const FluentReact = require("devtools/client/shared/vendor/fluent-react");
+const Localized = createFactory(FluentReact.Localized);
+
+class ManifestPage extends PureComponent {
+  render() {
+    return Localized(
+      {
+        id: "manifest-empty-intro",
+      },
+      section({ className: `manifest-page` })
+    );
+  }
+}
+
+// Exports
+module.exports = ManifestPage;
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/src/components/manifest/moz.build
@@ -0,0 +1,8 @@
+# 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/.
+
+DevToolsModules(
+    'ManifestPage.css',
+    'ManifestPage.js'
+)
--- a/devtools/client/application/src/components/moz.build
+++ b/devtools/client/application/src/components/moz.build
@@ -1,14 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'layout',
+    'manifest',
     'service-workers',
     'ui',
 ]
 
 DevToolsModules(
     'App.css',
     'App.js',
 )
--- a/devtools/client/application/src/constants.js
+++ b/devtools/client/application/src/constants.js
@@ -10,15 +10,16 @@ const actionTypes = {
   // ui substate
   UPDATE_SELECTED_PAGE: "UPDATE_SELECTED_PAGE",
   // workers substate
   UPDATE_CAN_DEBUG_WORKERS: "UPDATE_CAN_DEBUG_WORKERS",
   UPDATE_WORKERS: "UPDATE_WORKERS",
 };
 
 const PAGE_TYPES = {
+  MANIFEST: "manifest",
   SERVICE_WORKERS: "service-workers",
 };
 
-const DEFAULT_PAGE = PAGE_TYPES.SERVICE_WORKERS;
+const DEFAULT_PAGE = PAGE_TYPES.MANIFEST;
 
 // flatten constants
 module.exports = Object.assign({}, { DEFAULT_PAGE, PAGE_TYPES }, actionTypes);
--- a/devtools/client/application/test/browser/browser_application_panel_debug-service-worker.js
+++ b/devtools/client/application/test/browser/browser_application_panel_debug-service-worker.js
@@ -18,16 +18,19 @@ Services.scriptloader.loadSubScript(
 const TAB_URL = URL_ROOT + "resources/service-workers/debug.html";
 
 add_task(async function() {
   await enableApplicationPanel();
 
   const { panel, tab, target } = await openNewTabAndApplicationPanel(TAB_URL);
   const doc = panel.panelWin.document;
 
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   info("Wait until the service worker appears in the application panel");
   await waitUntil(() => getWorkerContainers(doc).length === 1);
 
   const container = getWorkerContainers(doc)[0];
   info("Wait until the debug button is displayed and enabled");
   await waitUntil(() => {
     const button = container.querySelector(".js-debug-button");
     return button && !button.disabled;
--- a/devtools/client/application/test/browser/browser_application_panel_list-domain-workers.js
+++ b/devtools/client/application/test/browser/browser_application_panel_list-domain-workers.js
@@ -16,16 +16,19 @@ const EMPTY_URL = (URL_ROOT + "resources
 );
 
 add_task(async function() {
   await enableApplicationPanel();
 
   const { panel, target } = await openNewTabAndApplicationPanel(SIMPLE_URL);
   const doc = panel.panelWin.document;
 
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   info("Wait until the service worker appears in the application panel");
   await waitUntil(() => getWorkerContainers(doc).length === 1);
 
   let scopeEl = getWorkerContainers(doc)[0].querySelector(".js-sw-scope");
   ok(
     scopeEl.textContent.startsWith("example.com"),
     "First service worker registration is displayed for the correct domain"
   );
--- a/devtools/client/application/test/browser/browser_application_panel_list-several-workers.js
+++ b/devtools/client/application/test/browser/browser_application_panel_list-several-workers.js
@@ -12,16 +12,19 @@ const SIMPLE_URL = URL_ROOT + "resources
 const OTHER_SCOPE_URL = URL_ROOT + "resources/service-workers/scope-page.html";
 
 add_task(async function() {
   await enableApplicationPanel();
 
   const { panel, target } = await openNewTabAndApplicationPanel(SIMPLE_URL);
   const doc = panel.panelWin.document;
 
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   info("Wait until the service worker appears in the application panel");
   await waitUntil(() => getWorkerContainers(doc).length === 1);
 
   info("Wait until the unregister button is displayed for the service worker");
   await waitUntil(() =>
     getWorkerContainers(doc)[0].querySelector(".js-unregister-button")
   );
 
--- a/devtools/client/application/test/browser/browser_application_panel_list-single-worker.js
+++ b/devtools/client/application/test/browser/browser_application_panel_list-single-worker.js
@@ -7,16 +7,19 @@ const TAB_URL =
   URL_ROOT + "resources/service-workers/dynamic-registration.html";
 
 add_task(async function() {
   await enableApplicationPanel();
 
   const { panel, tab } = await openNewTabAndApplicationPanel(TAB_URL);
   const doc = panel.panelWin.document;
 
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   const isWorkerListEmpty = !!doc.querySelector(".worker-list-empty");
   ok(isWorkerListEmpty, "No Service Worker displayed");
 
   info("Register a service worker in the page.");
   await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
     content.wrappedJSObject.registerServiceWorker();
   });
 
--- a/devtools/client/application/test/browser/browser_application_panel_list-unicode.js
+++ b/devtools/client/application/test/browser/browser_application_panel_list-unicode.js
@@ -13,16 +13,19 @@ const TAB_URL = (
  */
 
 add_task(async function() {
   await enableApplicationPanel();
 
   const { panel, target } = await openNewTabAndApplicationPanel(TAB_URL);
   const doc = panel.panelWin.document;
 
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   info("Wait until the service worker appears in the application panel");
   await waitUntil(() => getWorkerContainers(doc).length === 1);
 
   const workerContainer = getWorkerContainers(doc)[0];
 
   const scopeEl = workerContainer.querySelector(".js-sw-scope");
   ok(
     scopeEl.textContent.startsWith(
--- a/devtools/client/application/test/browser/browser_application_panel_list-workers-empty.js
+++ b/devtools/client/application/test/browser/browser_application_panel_list-workers-empty.js
@@ -11,15 +11,18 @@
 const EMPTY_URL = URL_ROOT + "resources/service-workers/empty.html";
 
 add_task(async function() {
   await enableApplicationPanel();
 
   const { panel, tab } = await openNewTabAndApplicationPanel(EMPTY_URL);
   const doc = panel.panelWin.document;
 
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   await waitUntil(() => doc.querySelector(".js-worker-list-empty") !== null);
   ok(true, "No service workers are shown for an empty page");
 
   // close the tab
   info("Closing the tab.");
   await BrowserTestUtils.removeTab(tab);
 });
--- a/devtools/client/application/test/browser/browser_application_panel_open-links.js
+++ b/devtools/client/application/test/browser/browser_application_panel_open-links.js
@@ -10,18 +10,20 @@ const { Toolbox } = require("devtools/cl
  */
 
 const TAB_URL = URL_ROOT + "resources/service-workers/empty.html";
 
 add_task(async function() {
   await enableApplicationPanel();
 
   const { panel, toolbox } = await openNewTabAndApplicationPanel(TAB_URL);
+  const doc = panel.panelWin.document;
 
-  const doc = panel.panelWin.document;
+  // select service worker view
+  selectPage(panel, "service-workers");
 
   // detach devtools in a separate window
   await toolbox.switchHost(Toolbox.HostType.WINDOW);
 
   // click on the link and wait for the new tab to open
   const onTabLoaded = BrowserTestUtils.waitForNewTab(
     gBrowser,
     "about:debugging#workers"
--- a/devtools/client/application/test/browser/browser_application_panel_start-service-worker.js
+++ b/devtools/client/application/test/browser/browser_application_panel_start-service-worker.js
@@ -16,16 +16,19 @@ add_task(async function() {
   // The default value is 30000 milliseconds.
   info("Set a low service worker idle timeout");
   await pushPref("dom.serviceWorkers.idle_timeout", 1000);
   await pushPref("dom.serviceWorkers.idle_extended_timeout", 1000);
 
   const { panel, tab, target } = await openNewTabAndApplicationPanel(TAB_URL);
   const doc = panel.panelWin.document;
 
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   await waitForWorkerRegistration(tab);
 
   info("Wait until the service worker appears in the application panel");
   await waitUntil(() => getWorkerContainers(doc).length === 1);
 
   info("Wait until the start button is displayed and enabled");
   const container = getWorkerContainers(doc)[0];
   await waitUntil(() => {
@@ -54,16 +57,20 @@ add_task(async function() {
   await enableApplicationPanel();
 
   // disable sw debugging by increasing the # of processes and thus multi-e10s kicking in
   info("Disable service worker debugging");
   await pushPref("dom.ipc.processCount", 8);
 
   const { panel, tab, target } = await openNewTabAndApplicationPanel(TAB_URL);
   const doc = panel.panelWin.document;
+
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   await waitForWorkerRegistration(tab);
 
   info("Wait until the service worker appears in the application panel");
   await waitUntil(() => getWorkerContainers(doc).length === 1);
 
   info("Wait until the start button is displayed");
   const container = getWorkerContainers(doc)[0];
   await waitUntil(() => container.querySelector(".js-start-button"));
--- a/devtools/client/application/test/browser/browser_application_panel_unregister-worker.js
+++ b/devtools/client/application/test/browser/browser_application_panel_unregister-worker.js
@@ -6,16 +6,19 @@
 const TAB_URL = URL_ROOT + "resources/service-workers/simple.html";
 
 add_task(async function() {
   await enableApplicationPanel();
 
   const { panel, tab, target } = await openNewTabAndApplicationPanel(TAB_URL);
   const doc = panel.panelWin.document;
 
+  // select service worker view
+  selectPage(panel, "service-workers");
+
   info("Wait until the service worker appears in the application panel");
   await waitUntil(() => getWorkerContainers(doc).length === 1);
 
   const workerContainer = getWorkerContainers(doc)[0];
 
   info("Wait until the unregister button is displayed for the service worker");
   await waitUntil(() => workerContainer.querySelector(".js-unregister-button"));
   info("Click the unregister button");
--- a/devtools/client/application/test/browser/head.js
+++ b/devtools/client/application/test/browser/head.js
@@ -76,8 +76,17 @@ async function waitForWorkerRegistration
   info("Wait until the registration appears on the window");
   const swBrowser = swTab.linkedBrowser;
   await asyncWaitUntil(async () =>
     ContentTask.spawn(swBrowser, {}, function() {
       return content.wrappedJSObject.getRegistration();
     })
   );
 }
+
+// TODO: update this function once the sidebar links are implemented (See bug
+// https: //bugzilla.mozilla.org/show_bug.cgi?id=1565213), and switch to to
+// click those links instead, since it's more representative of what users do
+function selectPage(panel, page) {
+  info(`Selecting application page: ${page}`);
+  const actions = panel.panelWin.Application.actions;
+  actions.updateSelectedPage(page);
+}
--- a/devtools/client/application/test/components/layout/__snapshots__/components_application_panel-PageContainer.test.js.snap
+++ b/devtools/client/application/test/components/layout/__snapshots__/components_application_panel-PageContainer.test.js.snap
@@ -1,7 +1,9 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`PageContainer renders nothing when an invalid page is selected 1`] = `""`;
 
 exports[`PageContainer renders nothing when no page is selected 1`] = `""`;
 
+exports[`PageContainer renders the ManifestPage component when manifest page is selected 1`] = `<ManifestPage />`;
+
 exports[`PageContainer renders the WorkersPage component when workers page is selected 1`] = `<Connect(WorkersPage) />`;
--- a/devtools/client/application/test/components/layout/components_application_panel-PageContainer.test.js
+++ b/devtools/client/application/test/components/layout/components_application_panel-PageContainer.test.js
@@ -2,16 +2,18 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Import libs
 const { shallow } = require("enzyme");
 const { createFactory } = require("react");
 
+console.error = jest.fn();
+
 // Import setupStore with imported & combined reducers
 const {
   setupStore,
 } = require("devtools/client/application/test/components/helpers/helpers");
 
 const PageContainer = createFactory(
   require("devtools/client/application/src/components/layout/PageContainer")
 );
@@ -28,26 +30,38 @@ describe("PageContainer", () => {
       preloadedState: {
         ui: {
           selectedPage,
         },
       },
     });
   }
 
+  beforeEach(() => {
+    console.error.mockClear();
+  });
+
+  it("renders the ManifestPage component when manifest page is selected", () => {
+    const store = buildStoreWithSelectedPage(PAGE_TYPES.MANIFEST);
+    const wrapper = shallow(PageContainer({ store })).dive();
+    expect(wrapper).toMatchSnapshot();
+  });
+
   it("renders the WorkersPage component when workers page is selected", () => {
     const store = buildStoreWithSelectedPage(PAGE_TYPES.SERVICE_WORKERS);
     const wrapper = shallow(PageContainer({ store })).dive();
     expect(wrapper).toMatchSnapshot();
   });
 
   it("renders nothing when no page is selected", () => {
     const store = buildStoreWithSelectedPage(null);
     const wrapper = shallow(PageContainer({ store })).dive();
+    expect(console.error).toHaveBeenCalledTimes(1);
     expect(wrapper).toMatchSnapshot();
   });
 
   it("renders nothing when an invalid page is selected", () => {
     const store = buildStoreWithSelectedPage("foo");
     const wrapper = shallow(PageContainer({ store })).dive();
+    expect(console.error).toHaveBeenCalledTimes(1);
     expect(wrapper).toMatchSnapshot();
   });
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/test/components/manifest/__snapshots__/components_application_panel-ManifestPage.test.js.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ManifestPage renders the expected snapshot 1`] = `
+<Localized
+  id="manifest-empty-intro"
+>
+  <section
+    className="manifest-page"
+  />
+</Localized>
+`;
new file mode 100644
--- /dev/null
+++ b/devtools/client/application/test/components/manifest/components_application_panel-ManifestPage.test.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Import libs
+const { shallow } = require("enzyme");
+const { createFactory } = require("react");
+
+const ManifestPage = createFactory(
+  require("devtools/client/application/src/components/manifest/ManifestPage")
+);
+
+/**
+ * Test for ManifestPage.js component
+ */
+
+describe("ManifestPage", () => {
+  it("renders the expected snapshot", () => {
+    const wrapper = shallow(ManifestPage({}));
+    expect(wrapper).toMatchSnapshot();
+  });
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/src/components/SecondaryPanes/DOMMutationBreakpoints.css
@@ -0,0 +1,70 @@
+/* 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/>. */
+
+ .dom-mutation-empty {
+  padding: 6px 0;
+  text-align: center;
+  font-style: italic;
+  color: var(--theme-body-color);
+ }
+
+.dom-mutation-list * {
+  -moz-user-select: none;
+  user-select: none;
+}
+
+.dom-mutation-list {
+  padding: 4px 0;
+  list-style-type: none;
+}
+
+.dom-mutation-list li {
+  position: relative;
+
+  display: flex;
+  align-items: start;
+  overflow: hidden;
+  padding-top: 2px;
+  padding-bottom: 2px;
+  padding-inline-start: 20px;
+  padding-inline-end: 12px;
+}
+
+.dom-mutation-list input {
+  margin: 2px 3px;
+
+  padding-inline-start: 2px;
+  margin-top: 0px;
+  margin-bottom: 0px;
+  margin-inline-start: 0;
+  margin-inline-end: 2px;
+  vertical-align: text-bottom;
+}
+
+.dom-mutation-info {
+  flex-grow: 1;
+  text-overflow: ellipsis;
+  overflow: hidden;
+  margin-inline-end: 20px;
+}
+
+.dom-mutation-list .close-btn {
+  position: absolute;
+  /* hide button outside of row until hovered or focused */
+  top: -100px;
+}
+
+/* Reveal the remove button on hover/focus */
+.dom-mutation-list li:hover .close-btn,
+.dom-mutation-list li .close-btn:focus {
+  top: calc(50% - 8px);
+}
+
+[dir="ltr"] .dom-mutation-list .close-btn {
+  right: 12px;
+}
+
+[dir="rtl"] .dom-mutation-list .close-btn {
+  left: 12px;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/src/components/SecondaryPanes/DOMMutationBreakpoints.js
@@ -0,0 +1,141 @@
+/* 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/>. */
+
+import React, { Component } from "react";
+
+import Reps from "devtools-reps";
+const {
+  REPS: { Rep },
+  MODE,
+} = Reps;
+import { translateNodeFrontToGrip } from "inspector-shared-utils";
+
+import {
+  deleteDOMMutationBreakpoint,
+  toggleDOMMutationBreakpointState,
+} from "framework-actions";
+
+import actions from "../../actions";
+import { connect } from "../../utils/connect";
+
+import { CloseButton } from "../shared/Button";
+
+import "./DOMMutationBreakpoints.css";
+import type { DOMMutationBreakpoint } from "../../types";
+
+type Props = {
+  breakpoints: DOMMutationBreakpoint[],
+  openElementInInspector: typeof actions.openElementInInspectorCommand,
+  highlightDomElement: typeof actions.highlightDomElement,
+  unHighlightDomElement: typeof actions.unHighlightDomElement,
+  deleteBreakpoint: typeof deleteDOMMutationBreakpoint,
+  toggleBreakpoint: typeof toggleDOMMutationBreakpointState,
+};
+
+const localizationTerms = {
+  subtree: L10N.getStr("domMutationTypes.subtree"),
+  attribute: L10N.getStr("domMutationTypes.attribute"),
+  removal: L10N.getStr("domMutationTypes.removal"),
+};
+
+class DOMMutationBreakpointsContents extends Component<Props> {
+  renderItem(breakpoint: DOMMutationBreakpoint) {
+    const {
+      openElementInInspector,
+      highlightDomElement,
+      unHighlightDomElement,
+      toggleBreakpoint,
+      deleteBreakpoint,
+    } = this.props;
+    return (
+      <li>
+        <input
+          type="checkbox"
+          checked={breakpoint.enabled}
+          onChange={() => toggleBreakpoint(breakpoint.id, !breakpoint.enabled)}
+        />
+        <div className="dom-mutation-info">
+          <div className="dom-mutation-label">
+            {Rep({
+              object: translateNodeFrontToGrip(breakpoint.nodeFront),
+              mode: MODE.LONG,
+              onDOMNodeClick: grip => openElementInInspector(grip),
+              onInspectIconClick: grip => openElementInInspector(grip),
+              onDOMNodeMouseOver: grip => highlightDomElement(grip),
+              onDOMNodeMouseOut: grip => unHighlightDomElement(grip),
+            })}
+          </div>
+          <div className="dom-mutation-type">
+            {localizationTerms[breakpoint.mutationType] ||
+              breakpoint.mutationType}
+          </div>
+        </div>
+        <CloseButton
+          handleClick={() =>
+            deleteBreakpoint(breakpoint.nodeFront, breakpoint.mutationType)
+          }
+        />
+      </li>
+    );
+  }
+
+  renderEmpty() {
+    return (
+      <div className="dom-mutation-empty">
+        {L10N.getStr("noDomMutationBreakpointsText")}
+      </div>
+    );
+  }
+
+  render() {
+    const { breakpoints } = this.props;
+
+    if (breakpoints.length === 0) {
+      return this.renderEmpty();
+    }
+
+    return (
+      <ul className="dom-mutation-list">
+        {breakpoints.map(breakpoint => this.renderItem(breakpoint))}
+      </ul>
+    );
+  }
+}
+
+const mapStateToProps = state => ({
+  breakpoints: state.domMutationBreakpoints.breakpoints,
+});
+
+const DOMMutationBreakpointsPanel = connect(
+  mapStateToProps,
+  {
+    deleteBreakpoint: deleteDOMMutationBreakpoint,
+    toggleBreakpoint: toggleDOMMutationBreakpointState,
+  },
+  undefined,
+  { storeKey: "toolbox-store" }
+)(DOMMutationBreakpointsContents);
+
+class DomMutationBreakpoints extends Component<Props> {
+  render() {
+    return (
+      <DOMMutationBreakpointsPanel
+        openElementInInspector={this.props.openElementInInspector}
+        highlightDomElement={this.props.highlightDomElement}
+        unHighlightDomElement={this.props.unHighlightDomElement}
+      />
+    );
+  }
+}
+
+export default connect(
+  undefined,
+  {
+    // the debugger-specific action bound to the debugger store
+    // since there is no `storeKey`
+    openElementInInspector: actions.openElementInInspectorCommand,
+    highlightDomElement: actions.highlightDomElement,
+    unHighlightDomElement: actions.unHighlightDomElement,
+  }
+)(DomMutationBreakpoints);
--- a/devtools/client/debugger/src/components/SecondaryPanes/index.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/index.js
@@ -35,16 +35,17 @@ import Expressions from "./Expressions";
 import SplitBox from "devtools-splitter";
 import Frames from "./Frames";
 import Workers from "./Workers";
 import Accordion from "../shared/Accordion";
 import CommandBar from "./CommandBar";
 import UtilsBar from "./UtilsBar";
 import XHRBreakpoints from "./XHRBreakpoints";
 import EventListeners from "./EventListeners";
+import DOMMutationBreakpoints from "./DOMMutationBreakpoints";
 import WhyPaused from "./WhyPaused";
 
 import Scopes from "./Scopes";
 
 import "./SecondaryPanes.css";
 
 import type {
   Expression,
@@ -368,16 +369,29 @@ class SecondaryPanes extends Component<P
       component: <EventListeners />,
       opened: prefs.eventListenersVisible,
       onToggle: opened => {
         prefs.eventListenersVisible = opened;
       },
     };
   }
 
+  getDOMMutationsItem(): AccordionPaneItem {
+    return {
+      header: L10N.getStr("domMutationHeader"),
+      className: "dom-mutations-pane",
+      buttons: [],
+      component: <DOMMutationBreakpoints />,
+      opened: prefs.domMutationBreakpointsVisible,
+      onToggle: opened => {
+        prefs.domMutationBreakpointsVisible = opened;
+      },
+    };
+  }
+
   getStartItems(): AccordionPaneItem[] {
     const items: AccordionPaneItem[] = [];
     const { horizontal, hasFrames } = this.props;
 
     if (horizontal) {
       if (features.workers && this.props.workers.length > 0) {
         items.push(this.getWorkersItem());
       }
@@ -397,16 +411,20 @@ class SecondaryPanes extends Component<P
     if (features.xhrBreakpoints) {
       items.push(this.getXHRItem());
     }
 
     if (features.eventListenersBreakpoints) {
       items.push(this.getEventListenersItem());
     }
 
+    if (features.domMutationBreakpoints) {
+      items.push(this.getDOMMutationsItem());
+    }
+
     return items;
   }
 
   getEndItems(): AccordionPaneItem[] {
     if (this.props.horizontal) {
       return [];
     }
 
--- a/devtools/client/debugger/src/components/SecondaryPanes/moz.build
+++ b/devtools/client/debugger/src/components/SecondaryPanes/moz.build
@@ -5,29 +5,31 @@
 
 DIRS += [
     'Breakpoints',
     'Frames',
 ]
 
 CompiledModules(
     'CommandBar.js',
+    'DOMMutationBreakpoints.js',
     'EventListeners.js',
     'Expressions.js',
     'index.js',
     'Scopes.js',
     'UtilsBar.js',
     'WhyPaused.js',
     'Worker.js',
     'Workers.js',
     'XHRBreakpoints.js',
 )
 
 DevToolsModules(
     'CommandBar.css',
+    'DOMMutationBreakpoints.css',
     'EventListeners.css',
     'Expressions.css',
     'Scopes.css',
     'SecondaryPanes.css',
     'WhyPaused.css',
     'Workers.css',
     'XHRBreakpoints.css',
 )
--- a/devtools/client/debugger/src/debugger.css
+++ b/devtools/client/debugger/src/debugger.css
@@ -34,16 +34,17 @@
 @import url("./components/PrimaryPanes/Outline.css");
 @import url("./components/PrimaryPanes/OutlineFilter.css");
 @import url("./components/PrimaryPanes/Sources.css");
 @import url("./components/ProjectSearch.css");
 @import url("./components/QuickOpenModal.css");
 @import url("./components/SecondaryPanes/Breakpoints/Breakpoints.css");
 @import url("./components/SecondaryPanes/CommandBar.css");
 @import url("./components/SecondaryPanes/EventListeners.css");
+@import url("./components/SecondaryPanes/DOMMutationBreakpoints.css");
 @import url("./components/SecondaryPanes/Expressions.css");
 @import url("./components/SecondaryPanes/Frames/Frames.css");
 @import url("./components/SecondaryPanes/Frames/Group.css");
 @import url("./components/SecondaryPanes/Scopes.css");
 @import url("./components/SecondaryPanes/SecondaryPanes.css");
 @import url("./components/SecondaryPanes/WhyPaused.css");
 @import url("./components/SecondaryPanes/Workers.css");
 @import url("./components/SecondaryPanes/XHRBreakpoints.css");
--- a/devtools/client/debugger/src/types.js
+++ b/devtools/client/debugger/src/types.js
@@ -484,16 +484,23 @@ export type Cancellable = {
 
 export type EventListenerBreakpoints = string[];
 
 export type SourceDocuments = { [string]: Object };
 
 export type BreakpointPosition = MappedLocation;
 export type BreakpointPositions = { [number]: BreakpointPosition[] };
 
+export type DOMMutationBreakpoint = {
+  id: number,
+  nodeFront: Object,
+  mutationType: "subtree" | "attribute" | "removal",
+  enabled: boolean,
+};
+
 export type { Context, ThreadContext } from "./utils/context";
 
 export type Previews = {
   [line: string]: Array<Preview>,
 };
 
 export type Preview = {
   name: string,
--- a/devtools/client/debugger/src/utils/prefs.js
+++ b/devtools/client/debugger/src/utils/prefs.js
@@ -25,16 +25,17 @@ if (isDevelopment()) {
   pref("devtools.debugger.call-stack-visible", true);
   pref("devtools.debugger.scopes-visible", true);
   pref("devtools.debugger.component-visible", true);
   pref("devtools.debugger.workers-visible", true);
   pref("devtools.debugger.expressions-visible", true);
   pref("devtools.debugger.xhr-breakpoints-visible", true);
   pref("devtools.debugger.breakpoints-visible", true);
   pref("devtools.debugger.event-listeners-visible", true);
+  pref("devtools.debugger.dom-mutation-breakpoints-visible", true);
   pref("devtools.debugger.start-panel-collapsed", false);
   pref("devtools.debugger.end-panel-collapsed", false);
   pref("devtools.debugger.start-panel-size", 300);
   pref("devtools.debugger.end-panel-size", 300);
   pref("devtools.debugger.tabsBlackBoxed", "[]");
   pref("devtools.debugger.ui.editor-wrapping", false);
   pref("devtools.debugger.ui.framework-grouping-on", true);
   pref("devtools.debugger.pending-selected-location", "{}");
@@ -60,16 +61,17 @@ if (isDevelopment()) {
   pref("devtools.debugger.features.component-pane", false);
   pref("devtools.debugger.features.autocomplete-expressions", false);
   pref("devtools.debugger.features.map-expression-bindings", true);
   pref("devtools.debugger.features.map-await-expression", true);
   pref("devtools.debugger.features.xhr-breakpoints", true);
   pref("devtools.debugger.features.original-blackbox", true);
   pref("devtools.debugger.features.windowless-workers", true);
   pref("devtools.debugger.features.event-listeners-breakpoints", true);
+  pref("devtools.debugger.features.dom-mutation-breakpoints", true);
   pref("devtools.debugger.features.log-points", true);
   pref("devtools.debugger.features.inline-preview", true);
   pref("devtools.debugger.log-actions", true);
   pref("devtools.debugger.features.overlay-step-buttons", false);
 }
 
 export const prefs = new PrefsHelper("devtools", {
   logging: ["Bool", "debugger.logging"],
@@ -84,16 +86,20 @@ export const prefs = new PrefsHelper("de
   callStackVisible: ["Bool", "debugger.call-stack-visible"],
   scopesVisible: ["Bool", "debugger.scopes-visible"],
   componentVisible: ["Bool", "debugger.component-visible"],
   workersVisible: ["Bool", "debugger.workers-visible"],
   breakpointsVisible: ["Bool", "debugger.breakpoints-visible"],
   expressionsVisible: ["Bool", "debugger.expressions-visible"],
   xhrBreakpointsVisible: ["Bool", "debugger.xhr-breakpoints-visible"],
   eventListenersVisible: ["Bool", "debugger.event-listeners-visible"],
+  domMutationBreakpointsVisible: [
+    "Bool",
+    "debugger.dom-mutation-breakpoints-visible",
+  ],
   startPanelCollapsed: ["Bool", "debugger.start-panel-collapsed"],
   endPanelCollapsed: ["Bool", "debugger.end-panel-collapsed"],
   startPanelSize: ["Int", "debugger.start-panel-size"],
   endPanelSize: ["Int", "debugger.end-panel-size"],
   frameworkGroupingOn: ["Bool", "debugger.ui.framework-grouping-on"],
   tabsBlackBoxed: ["Json", "debugger.tabsBlackBoxed", []],
   pendingSelectedLocation: ["Json", "debugger.pending-selected-location", {}],
   expressions: ["Json", "debugger.expressions", []],
@@ -122,16 +128,17 @@ export const features = new PrefsHelper(
   skipPausing: ["Bool", "skip-pausing"],
   autocompleteExpression: ["Bool", "autocomplete-expressions"],
   mapExpressionBindings: ["Bool", "map-expression-bindings"],
   mapAwaitExpression: ["Bool", "map-await-expression"],
   componentPane: ["Bool", "component-pane"],
   xhrBreakpoints: ["Bool", "xhr-breakpoints"],
   originalBlackbox: ["Bool", "original-blackbox"],
   eventListenersBreakpoints: ["Bool", "event-listeners-breakpoints"],
+  domMutationBreakpoints: ["Bool", "dom-mutation-breakpoints"],
   logPoints: ["Bool", "log-points"],
   showOverlayStepButtons: ["Bool", "debugger.features.overlay-step-buttons"],
   inlinePreview: ["Bool", "inline-preview"],
 });
 
 export const asyncStore = asyncStoreHelper("debugger", {
   pendingBreakpoints: ["pending-breakpoints", {}],
   tabs: ["tabs", []],
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -2,18 +2,22 @@
 tags = devtools
 subsuite = devtools
 skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
   examples/*
   head.js
   helpers.js
   helpers/context.js
+  !/devtools/client/inspector/test/head.js
+  !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
+  !/devtools/client/shared/test/test-actor-registry.js
+  !/devtools/client/shared/test/test-actor.js
 
 [browser_dbg-asm.js]
 [browser_dbg-audiocontext.js]
 [browser_dbg-async-stepping.js]
 [browser_dbg-sourcemapped-breakpoint-console.js]
 skip-if = (os == "win" && ccov) # Bug 1453549
 [browser_dbg-xhr-breakpoints.js]
 [browser_dbg-xhr-run-to-completion.js]
@@ -34,16 +38,17 @@ skip-if = os == "win" # Bug 1448523, Bug
 [browser_dbg-breakpoints-cond-source-maps.js]
 [browser_dbg-breakpoints-duplicate-functions.js]
 [browser_dbg-browser-content-toolbox.js]
 skip-if = !e10s || verify # This test is only valid in e10s
 [browser_dbg-breakpoints-reloading.js]
 [browser_dbg-breakpoint-skipping.js]
 [browser_dbg-breakpoint-skipping-console.js]
 [browser_dbg-call-stack.js]
+[browser_dbg-dom-mutation-breakpoints.js]
 [browser_dbg-scopes.js]
 [browser_dbg-chrome-create.js]
 skip-if = (verify && !debug && (os == 'linux'))
 [browser_dbg-chrome-debugging.js]
 [browser_dbg-console.js]
 [browser_dbg-console-async.js]
 [browser_dbg-console-eval.js]
 [browser_dbg-console-link.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+
+// Tests adding, disble/enable, and removal of dom mutation breakpoints
+
+
+/* import-globals-from ../../../inspector/test/shared-head.js */
+
+// Import helpers for the inspector
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
+  this
+);
+
+const DMB_TEST_URL = "http://example.com/browser/devtools/client/debugger/test/mochitest/examples/doc-dom-mutation.html";
+
+add_task(async function() {
+  // Enable features
+  await pushPref("devtools.debugger.features.dom-mutation-breakpoints", true);
+  await pushPref("devtools.markup.mutationBreakpoints.enabled", true);
+  await pushPref("devtools.debugger.dom-mutation-breakpoints-visible", true);
+
+  info("Switches over to the inspector pane");
+
+  const { inspector, toolbox } = await openInspectorForURL(DMB_TEST_URL);
+
+  info("Sellecting the body node");
+  await selectNode("body", inspector);
+
+  info("Adding a DOM mutation breakpoint to body");
+  const allMenuItems = openContextMenuAndGetAllItems(inspector);
+
+  const breakOnMenuItem = allMenuItems.find(item => item.id === "node-menu-mutation-breakpoint-attribute");
+  breakOnMenuItem.click();
+
+  info("Switches over to the debugger pane");
+  await toolbox.selectTool("jsdebugger");
+
+  const dbg = createDebuggerContext(toolbox);
+
+  info("Confirms that one DOM mutation breakpoint exists");
+  const mutationItem = await waitForElementWithSelector(dbg, ".dom-mutation-list li");
+  ok(mutationItem, "A DOM mutation breakpoint exists");
+
+  mutationItem.scrollIntoView();
+
+  info("Enabling and disabling the DOM mutation breakpoint works ");
+  const checkbox = mutationItem.querySelector("input");
+  checkbox.click();
+  await waitFor(() => !checkbox.checked);
+  checkbox.click();
+  await waitFor(() => checkbox.checked);
+
+  info("Changing attribute to trigger debugger pause");
+  ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+    content.document.querySelector("button").click();
+  });
+  await waitForPaused(dbg);
+  await resume(dbg);
+});
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/examples/doc-dom-mutation.html
@@ -0,0 +1,19 @@
+ <!-- This Source Code Form is subject to the terms of the Mozilla Public
+    - License, v. 2.0. If a copy of the MPL was not distributed with this
+    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Debugger test page</title>
+  </head>
+
+  <body>
+    <button title="Hello" onclick="changeAttribute()">Click me!</button>
+    <script>
+      function changeAttribute() {
+        document.body.setAttribute("title", "Goodbye");
+      }
+    </script>
+  </body>
+</html>
--- a/devtools/client/framework/actions/dom-mutation-breakpoints.js
+++ b/devtools/client/framework/actions/dom-mutation-breakpoints.js
@@ -95,17 +95,17 @@ function updateBreakpointsForMutations(m
     }
 
     if (removedNodeFronts.length > 0) {
       dispatch({
         type: "REMOVE_DOM_MUTATION_BREAKPOINTS_FOR_FRONTS",
         nodeFronts: removedNodeFronts,
       });
     }
-    if (changedNodeFronts.length > 0) {
+    if (changedNodeFronts.size > 0) {
       const enabledStates = [];
       for (const {
         id,
         nodeFront,
         mutationType,
         enabled,
       } of getDOMMutationBreakpoints(getState())) {
         if (changedNodeFronts.has(nodeFront)) {
--- a/devtools/client/framework/toolbox-process-window.js
+++ b/devtools/client/framework/toolbox-process-window.js
@@ -18,16 +18,21 @@ var { Toolbox } = require("devtools/clie
 var Services = require("Services");
 var { DebuggerClient } = require("devtools/shared/client/debugger-client");
 var { PrefsHelper } = require("devtools/client/shared/prefs");
 const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper(
   "devtools/client/locales/toolbox.properties"
 );
+loader.lazyImporter(
+  this,
+  "BrowserToolboxProcess",
+  "resource://devtools/client/framework/ToolboxProcess.jsm"
+);
 
 // Timeout to wait before we assume that a connect() timed out without an error.
 // In milliseconds. (With the Debugger pane open, this has been reported to last
 // more than 10 seconds!)
 const STATUS_REVEAL_TIME = 15000;
 
 /**
  * Shortcuts for accessing various debugger preferences.
@@ -124,16 +129,17 @@ function setPrefDefaults() {
   Services.prefs.setBoolPref("devtools.performance.enabled", false);
 }
 
 window.addEventListener(
   "load",
   async function() {
     gShortcuts = new KeyShortcuts({ window });
     gShortcuts.on("CmdOrCtrl+W", onCloseCommand);
+    gShortcuts.on("CmdOrCtrl+Alt+Shift+I", onDebugBrowserToolbox);
 
     const statusMessageContainer = document.getElementById(
       "status-message-title"
     );
     statusMessageContainer.textContent = L10N.getStr(
       "browserToolbox.statusMessage"
     );
 
@@ -157,16 +163,26 @@ window.addEventListener(
   },
   { once: true }
 );
 
 function onCloseCommand(event) {
   window.close();
 }
 
+/**
+ * Open a Browser toolbox debugging the current browser toolbox
+ *
+ * This helps debugging the browser toolbox code, especially the code
+ * running in the parent process. i.e. frontend code.
+ */
+function onDebugBrowserToolbox() {
+  BrowserToolboxProcess.init();
+}
+
 async function openToolbox(target) {
   const form = target.targetForm;
   appendStatusMessage(
     `Create toolbox target: ${JSON.stringify({ form }, null, 2)}`
   );
   const frame = document.getElementById("toolbox-iframe");
 
   // Remember the last panel that was used inside of this profile.
--- a/devtools/client/inspector/markup/markup-context-menu.js
+++ b/devtools/client/inspector/markup/markup-context-menu.js
@@ -544,16 +544,17 @@ class MarkupContextMenu {
         disabled: !isSelectionElement,
         label: INSPECTOR_L10N.getStr("inspectorSubtreeModification.label"),
         type: "checkbox",
       })
     );
 
     menu.append(
       new MenuItem({
+        id: "node-menu-mutation-breakpoint-attribute",
         checked: mutationBreakpoints.attribute,
         click: () => this.markup.toggleMutationBreakpoint("attribute"),
         disabled: !isSelectionElement,
         label: INSPECTOR_L10N.getStr("inspectorAttributeModification.label"),
         type: "checkbox",
       })
     );
 
@@ -809,16 +810,17 @@ class MarkupContextMenu {
         "devtools.markup.mutationBreakpoints.enabled"
       ) &&
       this.selection.nodeFront.mutationBreakpoints
     ) {
       menu.append(
         new MenuItem({
           label: INSPECTOR_L10N.getStr("inspectorBreakpointSubmenu.label"),
           submenu: this._getDOMBreakpointSubmenu(isSelectionElement),
+          id: "node-menu-mutation-breakpoint",
         })
       );
     }
 
     menu.append(
       new MenuItem({
         id: "node-menu-useinconsole",
         label: INSPECTOR_L10N.getStr("inspectorUseInConsole.label"),
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -2186,23 +2186,28 @@ MarkupView.prototype = {
     }
 
     return promise.all([...this._queuedChildUpdates.values()]);
   },
 
   /**
    * Return a list of the children to display for this container.
    */
-  _getVisibleChildren: function(container, centered) {
+  _getVisibleChildren: async function(container, centered) {
     let maxChildren = container.maxChildren || this.maxChildren;
     if (maxChildren == -1) {
       maxChildren = undefined;
     }
 
-    return this.walker.children(container.node, {
+    // We have to use node's walker and not a top level walker
+    // as for fission frames, we are going to have multiple walkers
+    const inspectorFront = await container.node.targetFront.getFront(
+      "inspector"
+    );
+    return inspectorFront.walker.children(container.node, {
       maxNodes: maxChildren,
       center: centered,
     });
   },
 
   /**
    * The parent of a given node as rendered in the markup view is not necessarily
    * node.parentNode(). For instance, shadow roots don't have a parentNode, but a host
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -178,34 +178,16 @@ async function focusNode(selector, inspe
 function clearCurrentNodeSelection(inspector) {
   info("Clearing the current selection");
   const updated = inspector.once("inspector-updated");
   inspector.selection.setNodeFront(null);
   return updated;
 }
 
 /**
- * Open the inspector in a tab with given URL.
- * @param {string} url  The URL to open.
- * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
- * @return A promise that is resolved once the tab and inspector have loaded
- *         with an object: { tab, toolbox, inspector }.
- */
-var openInspectorForURL = async function(url, hostType) {
-  const tab = await addTab(url);
-  const { inspector, toolbox, testActor } = await openInspector(hostType);
-  return { tab, inspector, toolbox, testActor };
-};
-
-async function getActiveInspector() {
-  const target = await TargetFactory.forTab(gBrowser.selectedTab);
-  return gDevTools.getToolbox(target).getPanel("inspector");
-}
-
-/**
  * Right click on a node in the test page and click on the inspect menu item.
  * @param {TestActor}
  * @param {String} selector The selector for the node to click on in the page.
  * @return {Promise} Resolves to the inspector when it has opened and is updated
  */
 var clickOnInspectMenuItem = async function(testActor, selector) {
   info("Showing the contextual menu on node " + selector);
   const contentAreaContextMenu = document.querySelector(
--- a/devtools/client/locales/en-US/application.ftl
+++ b/devtools/client/locales/en-US/application.ftl
@@ -74,8 +74,11 @@ serviceworker-empty-suggestions-console 
 
 # Suggestion to use the debugger to investigate why a service worker is not registered.
 # Clicking on the link will switch from the Application panel to the debugger.
 serviceworker-empty-suggestions-debugger = Step through your Service Worker registration and look for exceptions. <a>Open the Debugger</a>
 
 # Suggestion to go to about:debugging in order to see Service Workers for all domains.
 # Clicking on the link will open about:debugging in a new tab.
 serviceworker-empty-suggestions-aboutdebugging = Inspect Service Workers from other domains. <a>Open about:debugging</a>
+
+# Text displayed when no manifest was found for the current page.
+manifest-empty-intro = No manifest found to inspect.
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -126,16 +126,36 @@ mainThread=Main Thread
 # LOCALIZATION NOTE (noSourcesText): The text to display in the sources list
 # when there are no sources.
 noSourcesText=This page has no sources.
 
 # LOCALIZATION NOTE (eventListenersHeader1): The text to display in the events
 # header.
 eventListenersHeader1=Event Listener Breakpoints
 
+# LOCALIZATION NOTE (noDomMutationBreakpointsText): The text to display in the
+# DOM Mutation Breakpoints pane when there are no events.
+noDomMutationBreakpointsText=No breakpoints to display.
+
+# LOCALIZATION NOTE (domMutationHeader): The text to display in the
+# DOM Mutation Breakpoints header
+domMutationHeader=DOM Mutation Breakpoints
+
+# LOCALIZATION NOTE (domMutationTypes.attribute): The text to display in the
+# DOM Mutation Breakpoints panel for an attribute change
+domMutationTypes.attribute=Attribute Modification
+
+# LOCALIZATION NOTE (domMutationTypes.removal): The text to display in the
+# DOM Mutation Breakpoints panel for a DOM node removal
+domMutationTypes.removal=Node Removal
+
+# LOCALIZATION NOTE (domMutationTypes.subtree): The text to display in the
+# DOM Mutation Breakpoints panel for a DOM subtree change
+domMutationTypes.subtree=Subtree Modification
+
 # LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for
 # searching all the source files the debugger has seen.
 # Do not localize "CmdOrCtrl+P", or change the format of the string. These are
 # key identifiers, not messages displayed to the user.
 sources.search.key2=CmdOrCtrl+P
 
 # LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the
 # search for searching all the source files the debugger has seen.
--- a/devtools/client/netmonitor/src/utils/filter-text-utils.js
+++ b/devtools/client/netmonitor/src/utils/filter-text-utils.js
@@ -115,136 +115,115 @@ function processFlagFilter(type, value) 
         return null;
       }
       return quantity * multiplier;
     default:
       return value.toLowerCase();
   }
 }
 
-/* eslint-disable complexity */
 function isFlagFilterMatch(item, { type, value, negative }) {
   if (value == null) {
     return false;
   }
 
   // Ensures when filter token is exactly a flag ie. "remote-ip:", all values are shown
   if (value.length < 1) {
     return true;
   }
 
   let match = true;
   let { responseCookies = { cookies: [] } } = item;
   responseCookies = responseCookies.cookies || responseCookies;
-  switch (type) {
-    case "status-code":
-      match = item.status && item.status.toString() === value;
-      break;
-    case "method":
-      match = item.method.toLowerCase() === value;
-      break;
-    case "protocol":
+
+  const matchers = {
+    "status-code": () => item.status && item.status.toString() === value,
+    method: () => item.method.toLowerCase() === value,
+    protocol: () => {
       const protocol = item.httpVersion;
-      match =
-        typeof protocol === "string"
-          ? protocol.toLowerCase().includes(value)
-          : false;
-      break;
-    case "domain":
-      match = item.urlDetails.host.toLowerCase().includes(value);
-      break;
-    case "remote-ip":
+      return typeof protocol === "string"
+        ? protocol.toLowerCase().includes(value)
+        : false;
+    },
+    domain: () => item.urlDetails.host.toLowerCase().includes(value),
+    "remote-ip": () => {
       const data = getFormattedIPAndPort(item.remoteAddress, item.remotePort);
-      match = data ? data.toLowerCase().includes(value) : false;
-      break;
-    case "has-response-header":
+      return data ? data.toLowerCase().includes(value) : false;
+    },
+    "has-response-header": () => {
       if (typeof item.responseHeaders === "object") {
         const { headers } = item.responseHeaders;
-        match = headers.findIndex(h => h.name.toLowerCase() === value) > -1;
-      } else {
-        match = false;
+        return headers.findIndex(h => h.name.toLowerCase() === value) > -1;
       }
-      break;
-    case "cause":
+      return false;
+    },
+    cause: () => {
       const causeType = item.cause.type;
-      match =
-        typeof causeType === "string"
-          ? causeType.toLowerCase().includes(value)
-          : false;
-      break;
-    case "transferred":
+      return typeof causeType === "string"
+        ? causeType.toLowerCase().includes(value)
+        : false;
+    },
+    transferred: () => {
       if (item.fromCache) {
-        match = false;
-      } else {
-        match = isSizeMatch(value, item.transferredSize);
+        return false;
       }
-      break;
-    case "size":
-      match = isSizeMatch(value, item.contentSize);
-      break;
-    case "larger-than":
-      match = item.contentSize > value;
-      break;
-    case "transferred-larger-than":
+      return isSizeMatch(value, item.transferredSize);
+    },
+    size: () => isSizeMatch(value, item.contentSize),
+    "larger-than": () => item.contentSize > value,
+    "transferred-larger-than": () => {
       if (item.fromCache) {
-        match = false;
-      } else {
-        match = item.transferredSize > value;
+        return false;
       }
-      break;
-    case "mime-type":
-      match = item.mimeType.includes(value);
-      break;
-    case "is":
+      return item.transferredSize > value;
+    },
+    "mime-type": () => item.mimeType.includes(value),
+    is: () => {
       if (value === "from-cache" || value === "cached") {
-        match = item.fromCache || item.status === "304";
-      } else if (value === "running") {
-        match = !item.status;
+        return item.fromCache || item.status === "304";
+      }
+      if (value === "running") {
+        return !item.status;
       }
-      break;
-    case "scheme":
-      match = item.urlDetails.scheme === value;
-      break;
-    case "regexp":
+      return match;
+    },
+    scheme: () => item.urlDetails.scheme === value,
+    regexp: () => {
       try {
         const pattern = new RegExp(value);
-        match = pattern.test(item.url);
+        return pattern.test(item.url);
       } catch (e) {
-        match = false;
+        return false;
       }
-      break;
-    case "set-cookie-domain":
+    },
+    "set-cookie-domain": () => {
       if (responseCookies.length > 0) {
         const host = item.urlDetails.host;
         const i = responseCookies.findIndex(c => {
           const domain = c.hasOwnProperty("domain") ? c.domain : host;
           return domain.includes(value);
         });
-        match = i > -1;
-      } else {
-        match = false;
+        return i > -1;
       }
-      break;
-    case "set-cookie-name":
-      match =
-        responseCookies.findIndex(c => c.name.toLowerCase().includes(value)) >
-        -1;
-      break;
-    case "set-cookie-value":
-      match =
-        responseCookies.findIndex(c => c.value.toLowerCase().includes(value)) >
-        -1;
-      break;
+      return false;
+    },
+    "set-cookie-name": () =>
+      responseCookies.findIndex(c => c.name.toLowerCase().includes(value)) > -1,
+    "set-cookie-value": () =>
+      responseCookies.findIndex(c => c.value.toLowerCase().includes(value)) >
+      -1,
+  };
+
+  const matcher = matchers[type];
+  if (matcher) {
+    match = matcher();
   }
-  if (negative) {
-    return !match;
-  }
-  return match;
+
+  return negative ? !match : match;
 }
-/* eslint-enable complexity */
 
 function isSizeMatch(value, size) {
   return value >= size - size / 10 && value <= size + size / 10;
 }
 
 function isTextFilterMatch({ url }, text) {
   const lowerCaseUrl = getUnicodeUrl(url).toLowerCase();
   let lowerCaseText = text.toLowerCase();
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -30,16 +30,17 @@ pref("devtools.debugger.ui.variables-sea
 pref("devtools.debugger.ui.framework-grouping-on", true);
 pref("devtools.debugger.ui.editor-wrapping", false);
 pref("devtools.debugger.call-stack-visible", true);
 pref("devtools.debugger.scopes-visible", true);
 pref("devtools.debugger.component-visible", true);
 pref("devtools.debugger.workers-visible", true);
 pref("devtools.debugger.breakpoints-visible", true);
 pref("devtools.debugger.expressions-visible", true);
+pref("devtools.debugger.dom-mutation-breakpoints-visible", true);
 pref("devtools.debugger.xhr-breakpoints-visible", true);
 pref("devtools.debugger.event-listeners-visible", true);
 pref("devtools.debugger.start-panel-collapsed", false);
 pref("devtools.debugger.end-panel-collapsed", false);
 pref("devtools.debugger.start-panel-size", 300);
 pref("devtools.debugger.end-panel-size", 300);
 pref("devtools.debugger.tabs", "[]");
 pref("devtools.debugger.tabsBlackBoxed", "[]");
@@ -70,11 +71,12 @@ pref("devtools.debugger.features.compone
 pref("devtools.debugger.features.async-stepping", false);
 pref("devtools.debugger.features.skip-pausing", true);
 pref("devtools.debugger.features.autocomplete-expressions", false);
 pref("devtools.debugger.features.map-expression-bindings", true);
 pref("devtools.debugger.features.xhr-breakpoints", true);
 pref("devtools.debugger.features.original-blackbox", true);
 pref("devtools.debugger.features.windowless-workers", true);
 pref("devtools.debugger.features.event-listeners-breakpoints", true);
+pref("devtools.debugger.features.dom-mutation-breakpoints", false);
 pref("devtools.debugger.features.log-points", true);
 pref("devtools.debugger.features.overlay-step-buttons", false);
 pref("devtools.debugger.features.inline-preview", false);
--- a/devtools/client/preferences/devtools-client.js
+++ b/devtools/client/preferences/devtools-client.js
@@ -167,16 +167,18 @@ pref("devtools.netmonitor.enabled", true
 pref("devtools.netmonitor.features.search", false);
 
 // Enable the Application panel
 pref("devtools.application.enabled", false);
 
 // The default Network Monitor UI settings
 pref("devtools.netmonitor.panes-network-details-width", 550);
 pref("devtools.netmonitor.panes-network-details-height", 450);
+pref("devtools.netmonitor.panes-search-width", 550);
+pref("devtools.netmonitor.panes-search-height", 450);
 pref("devtools.netmonitor.filters", "[\"all\"]");
 pref("devtools.netmonitor.visibleColumns",
   "[\"status\",\"method\",\"domain\",\"file\",\"cause\",\"type\",\"transferred\",\"contentSize\",\"waterfall\"]"
 );
 pref("devtools.netmonitor.columnsData",
   '[{"name":"status","minWidth":30,"width":5}, {"name":"method","minWidth":30,"width":5}, {"name":"domain","minWidth":30,"width":10}, {"name":"file","minWidth":30,"width":25}, {"name":"url","minWidth":30,"width":25}, {"name":"cause","minWidth":30,"width":10},{"name":"type","minWidth":30,"width":5},{"name":"transferred","minWidth":30,"width":10},{"name":"contentSize","minWidth":30,"width":5},{"name":"waterfall","minWidth":150,"width":25}]');
 pref("devtools.netmonitor.ws.payload-preview-height", 128);
 pref("devtools.netmonitor.ws.visibleColumns",
--- a/devtools/client/responsive/test/browser/browser.ini
+++ b/devtools/client/responsive/test/browser/browser.ini
@@ -80,16 +80,17 @@ skip-if = os == "linux" || os == "mac" #
 skip-if = os == "mac" # Bug 1569508
 [browser_touch_device.js]
 [browser_touch_does_not_trigger_hover_states.js]
 [browser_touch_simulation.js]
 [browser_typeahead_find.js]
 fail-if = true # Bug 1547783
 [browser_user_agent_input.js]
 [browser_viewport_basics.js]
+[browser_viewport_resizing_after_reload.js]
 [browser_viewport_resizing_fixed_width.js]
 [browser_viewport_resizing_fixed_width_and_zoom.js]
 [browser_viewport_resizing_minimum_scale.js]
 [browser_viewport_resizing_scrollbar.js]
 skip-if = fission && debug # Crashes: @ xpc::WrapperFactory::PrepareForWrapping(JSContext*, JS::Handle<JSObject*>, JS::Handle<JSObject*>, JS::Handle<JSObject*>, JS::MutableHandle<JSObject*>)
 fail-if = true # Bug 1547101
 [browser_window_close.js]
 [browser_window_sizing.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive/test/browser/browser_viewport_resizing_after_reload.js
@@ -0,0 +1,101 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test viewport resizing, with and without meta viewport support.
+
+const TEST_URL =
+  "data:text/html;charset=utf-8," +
+  '<head><meta name="viewport" content="width=device-width"/></head>' +
+  '<body style="margin:0px;min-width:600px">' +
+  '<div style="width:100%;height:100px;background-color:black"></div>' +
+  '<div style="width:100%;height:1100px;background-color:lightblue"></div>' +
+  "</body>";
+
+addRDMTask(TEST_URL, async function({ ui, manager }) {
+  // Turn on the pref that allows meta viewport support.
+  await SpecialPowers.pushPrefEnv({
+    set: [["devtools.responsive.metaViewport.enabled", true]],
+  });
+
+  const store = ui.toolWindow.store;
+
+  // Wait until the viewport has been added.
+  await waitUntilState(store, state => state.viewports.length == 1);
+
+  info("--- Starting viewport test output ---");
+
+  // We're going to take a 300,600 viewport (before), reload it,
+  // then resize it to 600,300 (after) and then resize it back.
+  // At the before and after points, we'll measure zoom and the
+  // layout viewport width and height.
+  const expected = [
+    {
+      metaSupport: false,
+      before: [1.0, 300, 600],
+      after: [1.0, 600, 300],
+    },
+    {
+      metaSupport: true,
+      before: [0.5, 600, 1200],
+      after: [1.0, 600, 300],
+    },
+  ];
+
+  for (const e of expected) {
+    const b = e.before;
+    const a = e.after;
+
+    const message = "Meta Viewport " + (e.metaSupport ? "ON" : "OFF");
+
+    // Ensure meta viewport is set.
+    info(message + " setting meta viewport support.");
+    await setTouchAndMetaViewportSupport(ui, e.metaSupport);
+
+    // Get to the initial size and check values.
+    await setViewportSize(ui, manager, 300, 600);
+    await testViewportZoomWidthAndHeight(
+      message + " before resize",
+      ui,
+      b[0],
+      b[1],
+      b[2]
+    );
+
+    // Force a reload.
+    const reload = waitForViewportLoad(ui);
+    const browser = ui.getViewportBrowser();
+    browser.reload();
+    await reload;
+
+    // Check initial values again.
+    await testViewportZoomWidthAndHeight(
+      message + " after reload",
+      ui,
+      b[0],
+      b[1],
+      b[2]
+    );
+
+    // Move to the smaller size.
+    await setViewportSize(ui, manager, 600, 300);
+    await testViewportZoomWidthAndHeight(
+      message + " after resize",
+      ui,
+      a[0],
+      a[1],
+      a[2]
+    );
+
+    // Go back to the initial size and check again.
+    await setViewportSize(ui, manager, 300, 600);
+    await testViewportZoomWidthAndHeight(
+      message + " return to initial size",
+      ui,
+      b[0],
+      b[1],
+      b[2]
+    );
+  }
+});
--- a/devtools/client/shared/build/build-debugger.js
+++ b/devtools/client/shared/build/build-debugger.js
@@ -30,16 +30,18 @@ const mappings = Object.assign(
     "react-redux": "devtools/client/shared/vendor/react-redux",
     redux: "devtools/client/shared/vendor/redux",
     reselect: "devtools/client/shared/vendor/reselect",
     "prop-types": "devtools/client/shared/vendor/react-prop-types",
     "devtools-services": "Services",
     "wasmparser/dist/WasmParser": "devtools/client/shared/vendor/WasmParser",
     "wasmparser/dist/WasmDis": "devtools/client/shared/vendor/WasmDis",
     "whatwg-url": "devtools/client/shared/vendor/whatwg-url",
+    "framework-actions": "devtools/client/framework/actions/index",
+    "inspector-shared-utils": "devtools/client/inspector/shared/utils",
   },
   EXCLUDED_FILES
 );
 
 const mappingValues = Object.values(mappings);
 
 // Add two additional mappings that cannot be reused when creating the
 // webpack bundles.
--- a/devtools/client/shared/test/shared-head.js
+++ b/devtools/client/shared/test/shared-head.js
@@ -1,14 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 
+/* import-globals-from ../../inspector/test/shared-head.js */
+
 "use strict";
 
 // This shared-head.js file is used for multiple mochitest test directories in
 // devtools.
 // It contains various common helper functions.
 
 const { Constructor: CC } = Components;
 
@@ -228,16 +230,34 @@ var refreshTab = async function(tab = gB
   info("Refreshing tab.");
   const finished = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
   gBrowser.reloadTab(tab);
   await finished;
   info("Tab finished refreshing.");
 };
 
 /**
+ * Open the inspector in a tab with given URL.
+ * @param {string} url  The URL to open.
+ * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
+ * @return A promise that is resolved once the tab and inspector have loaded
+ *         with an object: { tab, toolbox, inspector }.
+ */
+var openInspectorForURL = async function(url, hostType) {
+  const tab = await addTab(url);
+  const { inspector, toolbox, testActor } = await openInspector(hostType);
+  return { tab, inspector, toolbox, testActor };
+};
+
+async function getActiveInspector() {
+  const target = await TargetFactory.forTab(gBrowser.selectedTab);
+  return gDevTools.getToolbox(target).getPanel("inspector");
+}
+
+/**
  * Simulate a key event from a <key> element.
  * @param {DOMNode} key
  */
 function synthesizeKeyFromKeyTag(key) {
   is(key && key.tagName, "key", "Successfully retrieved the <key> node");
 
   const modifiersAttr = key.getAttribute("modifiers");
 
--- a/devtools/client/themes/tooltips.css
+++ b/devtools/client/themes/tooltips.css
@@ -79,44 +79,41 @@ strong {
   color: var(--theme-body-color);
   padding: 2px;
 }
 
 /* Tooltip: Inactive CSS tooltip */
 
 .devtools-tooltip-inactive-css {
   color: var(--theme-body-color);
-  padding: 5px 13px;
+  padding: 7px 14px 9px;
+  font-size: 12px;
   margin: 0;
 }
 
 .devtools-tooltip-inactive-css,
 .devtools-tooltip-inactive-css strong {
   -moz-user-select: text;
   -moz-user-focus: normal;
 }
 
 .devtools-tooltip-inactive-css p {
   margin-block-start: 0;
-  margin-block-end: 0.63em;
+  margin-block-end: calc(1em - 4px);
 }
 
 .devtools-tooltip-inactive-css p:last-child {
   margin-block-end: 0;
 }
 
 .devtools-tooltip-inactive-css .link {
   color: var(--theme-highlight-blue);
   cursor: pointer;
 }
 
-.tooltip-bottom .devtools-tooltip-inactive-css {
-  padding: 2px 13px 7px;
-}
-
 /* Tooltip: Tiles */
 
 .devtools-tooltip-tiles {
   background-color: #eee;
   background-image: linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc),
     linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%, #ccc);
   background-size: 20px 20px;
   background-position: 0 0, 10px 10px;
--- a/devtools/client/themes/variables.css
+++ b/devtools/client/themes/variables.css
@@ -111,17 +111,16 @@
   --theme-bg-yellow: #FFF697;
 }
 
 /*
  * For doorhangers elsewhere in Firefox, Mac uses fixed colors rather than
  * system colors.
  */
 :root[platform="mac"].theme-light {
-  --theme-popup-background: hsla(0,0%,99%,.975);
   --theme-popup-color: hsl(0,0%,10%);
   --theme-popup-border-color: var(--grey-90-a20);
 }
 
 :root.theme-dark {
   --theme-body-background: #232327;
   --theme-sidebar-background: #18181a;
   --theme-contrast-background: #ffb35b;
--- a/devtools/server/actors/inspector/node.js
+++ b/devtools/server/actors/inspector/node.js
@@ -1,20 +1,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cu } = require("chrome");
 const Services = require("Services");
+const ChromeUtils = require("ChromeUtils");
 const InspectorUtils = require("InspectorUtils");
 const protocol = require("devtools/shared/protocol");
 const { PSEUDO_CLASSES } = require("devtools/shared/css/constants");
 const { nodeSpec, nodeListSpec } = require("devtools/shared/specs/node");
+const { DebuggerServer } = require("devtools/server/debugger-server");
 
 loader.lazyRequireGetter(
   this,
   "getCssPath",
   "devtools/shared/inspector/css-logic",
   true
 );
 loader.lazyRequireGetter(
@@ -138,16 +140,21 @@ loader.lazyRequireGetter(
   "devtools/server/actors/inspector/utils",
   true
 );
 
 const SUBGRID_ENABLED = Services.prefs.getBoolPref(
   "layout.css.grid-template-subgrid-value.enabled"
 );
 
+const BROWSER_TOOLBOX_FISSION_ENABLED = Services.prefs.getBoolPref(
+  "devtools.browsertoolbox.fission",
+  false
+);
+
 const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
 const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;
 
 /**
  * Server side of the node actor.
  */
 const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
   initialize: function(walker, node) {
@@ -258,16 +265,23 @@ const NodeActor = protocol.ActorClassWit
         this.rawNode.ownerDocument.contentType === "text/html",
       hasEventListeners: this._hasEventListeners,
     };
 
     if (this.isDocumentElement()) {
       form.isDocumentElement = true;
     }
 
+    // Flag the remote frame and declare at least one child (the #document element) so
+    // that they can be expanded.
+    if (this.isRemoteFrame) {
+      form.remoteFrame = true;
+      form.numChildren = 1;
+    }
+
     return form;
   },
 
   /**
    * Watch the given document node for mutations using the DOM observer
    * API.
    */
   watchDocument: function(doc, callback) {
@@ -290,16 +304,29 @@ const NodeActor = protocol.ActorClassWit
   /**
    * Watch for all "slotchange" events on the node.
    */
   watchSlotchange: function(callback) {
     this.slotchangeListener = callback;
     this.rawNode.addEventListener("slotchange", this.slotchangeListener);
   },
 
+  /**
+   * Check if the current node is representing a remote frame.
+   * EXPERIMENTAL: Only works if fission is enabled in the toolbox.
+   */
+  get isRemoteFrame() {
+    return (
+      this.numChildren == 0 &&
+      ChromeUtils.getClassName(this.rawNode) == "XULFrameElement" &&
+      this.rawNode.getAttribute("remote") == "true" &&
+      BROWSER_TOOLBOX_FISSION_ENABLED
+    );
+  },
+
   // Estimate the number of children that the walker will return without making
   // a call to children() if possible.
   get numChildren() {
     // For pseudo elements, childNodes.length returns 1, but the walker
     // will return 0.
     if (
       isMarkerPseudoElement(this.rawNode) ||
       isBeforePseudoElement(this.rawNode) ||
@@ -666,16 +693,31 @@ const NodeActor = protocol.ActorClassWit
    */
   getOwnerGlobalDimensions: function() {
     const win = this.rawNode.ownerGlobal;
     return {
       innerWidth: win.innerWidth,
       innerHeight: win.innerHeight,
     };
   },
+
+  /**
+   * Fetch the target actor's form for the current remote frame.
+   *
+   * (to be called only if form.remoteFrame is true)
+   */
+  connectToRemoteFrame() {
+    if (!this.isRemoteFrame) {
+      return {
+        error: "ErrorRemoteFrame",
+        message: "Tried to call `connectToRemoteFrame` on a local frame",
+      };
+    }
+    return DebuggerServer.connectToFrame(this.conn, this.rawNode);
+  },
 });
 
 /**
  * Server side of a node list as returned by querySelectorAll()
  */
 const NodeListActor = protocol.ActorClassWithSpec(nodeListSpec, {
   typeName: "domnodelist",
 
--- a/devtools/shared/fronts/inspector.js
+++ b/devtools/shared/fronts/inspector.js
@@ -447,16 +447,35 @@ class WalkerFront extends FrontClassWith
   async removeNode(node) {
     const previousSibling = await this.previousSibling(node);
     const nextSibling = await super.removeNode(node);
     return {
       previousSibling: previousSibling,
       nextSibling: nextSibling,
     };
   }
+
+  async children(node, options) {
+    if (!node.remoteFrame) {
+      return super.children(node, options);
+    }
+    // First get the target actor form of this remote frame element
+    const target = await node.connectToRemoteFrame();
+    // Then get an inspector front, and grab its walker front
+    const walker = (await target.getFront("inspector")).walker;
+    // Finally retrieve the NodeFront of the remote frame's document
+    const documentNode = await walker.getRootNode();
+
+    // And return the same kind of response `walker.children` returns
+    return {
+      nodes: [documentNode],
+      hasFirst: true,
+      hasLast: true,
+    };
+  }
 }
 
 exports.WalkerFront = WalkerFront;
 registerFront(WalkerFront);
 
 /**
  * Client side of the inspector actor, which is used to create
  * inspector-related actors, including the walker.
--- a/devtools/shared/fronts/node.js
+++ b/devtools/shared/fronts/node.js
@@ -15,16 +15,23 @@ const promise = require("promise");
 const { SimpleStringFront } = require("devtools/shared/fronts/string");
 
 loader.lazyRequireGetter(
   this,
   "nodeConstants",
   "devtools/shared/dom-node-constants"
 );
 
+loader.lazyRequireGetter(
+  this,
+  "BrowsingContextTargetFront",
+  "devtools/shared/fronts/targets/browsing-context",
+  true
+);
+
 const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
 
 /**
  * Client side of a node list as returned by querySelectorAll()
  */
 class NodeListFront extends FrontClassWithSpec(nodeListSpec) {
   marshallPool() {
     return this.parent();
@@ -268,16 +275,19 @@ class NodeFront extends FrontClassWithSp
   }
 
   get hasChildren() {
     return this._form.numChildren > 0;
   }
   get numChildren() {
     return this._form.numChildren;
   }
+  get remoteFrame() {
+    return this._form.remoteFrame;
+  }
   get hasEventListeners() {
     return this._form.hasEventListeners;
   }
 
   get isMarkerPseudoElement() {
     return this._form.isMarkerPseudoElement;
   }
   get isBeforePseudoElement() {
@@ -492,12 +502,26 @@ class NodeFront extends FrontClassWithSp
     const actor = DebuggerServer.searchAllConnectionsForActor(this.actorID);
     if (!actor) {
       // Can happen if we try to get the raw node for an already-expired
       // actor.
       return null;
     }
     return actor.rawNode;
   }
+
+  async connectToRemoteFrame() {
+    if (this._remoteFrameTarget) {
+      return this._remoteFrameTarget;
+    }
+    // First get the target actor form of this remote frame element
+    const form = await super.connectToRemoteFrame();
+    // Build the related Target object
+    this._remoteFrameTarget = new BrowsingContextTargetFront(this.conn);
+    this._remoteFrameTarget.actorID = form.actor;
+    this._remoteFrameTarget.form(form);
+    this._remoteFrameTarget.manage(this._remoteFrameTarget);
+    return this._remoteFrameTarget;
+  }
 }
 
 exports.NodeFront = NodeFront;
 registerFront(NodeFront);
--- a/devtools/shared/specs/node.js
+++ b/devtools/shared/specs/node.js
@@ -129,12 +129,22 @@ const nodeSpec = generateActorSpec({
       response: {
         value: RetVal("string"),
       },
     },
     getOwnerGlobalDimensions: {
       request: {},
       response: RetVal("windowDimensions"),
     },
+    connectToRemoteFrame: {
+      request: {},
+      // We are passing a target actor form here.
+      // As we are manually fetching the form JSON via DebuggerServer.connectToFrame,
+      // we are not instanciating a protocol.js front class and can't use proper type
+      // here and have automatic marshalling.
+      //
+      // Alex: Can we do something to address that??
+      response: RetVal("json"),
+    },
   },
 });
 
 exports.nodeSpec = nodeSpec;
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -13287,18 +13287,18 @@ void Document::CleanupFullscreenState() 
     }
   }
   mFullscreenStack.Clear();
   mFullscreenRoot = nullptr;
 
   // Restore the zoom level that was in place prior to entering fullscreen.
   if (PresShell* presShell = GetPresShell()) {
     if (presShell->GetMobileViewportManager()) {
-      presShell->SetResolutionAndScaleTo(mSavedResolution,
-                                         ResolutionChangeOrigin::MainThread);
+      presShell->SetResolutionAndScaleTo(
+          mSavedResolution, ResolutionChangeOrigin::MainThreadRestore);
     }
   }
 
   UpdateViewportScrollbarOverrideForFullscreen(this);
 }
 
 bool Document::FullscreenStackPush(Element* aElement) {
   NS_ASSERTION(aElement, "Must pass non-null to FullscreenStackPush()");
@@ -13686,17 +13686,17 @@ bool Document::ApplyFullscreen(UniquePtr
     // when in fullscreen mode.
     if (PresShell* presShell = child->GetPresShell()) {
       if (RefPtr<MobileViewportManager> manager =
               presShell->GetMobileViewportManager()) {
         // Save the previous resolution so it can be restored.
         child->mSavedResolution = presShell->GetResolution();
         presShell->SetResolutionAndScaleTo(
             manager->ComputeIntrinsicResolution(),
-            ResolutionChangeOrigin::MainThread);
+            ResolutionChangeOrigin::MainThreadRestore);
       }
     }
 
     NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
                  "Fullscreen root should be set!");
     if (child == fullScreenRootDoc) {
       break;
     }
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -555,17 +555,17 @@ nsDOMWindowUtils::GetScrollbarSizes(Elem
 NS_IMETHODIMP
 nsDOMWindowUtils::SetResolutionAndScaleTo(float aResolution) {
   PresShell* presShell = GetPresShell();
   if (!presShell) {
     return NS_ERROR_FAILURE;
   }
 
   presShell->SetResolutionAndScaleTo(aResolution,
-                                     ResolutionChangeOrigin::MainThread);
+                                     ResolutionChangeOrigin::MainThreadRestore);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SetRestoreResolution(float aResolution,
                                        uint32_t aDisplayWidth,
                                        uint32_t aDisplayHeight) {
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -137,75 +137,52 @@ bool nsMathMLElement::ParseAttribute(int
 
   return nsMathMLElementBase::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                              aMaybeScriptedPrincipal, aResult);
 }
 
 static Element::MappedAttributeEntry sMtableStyles[] = {{nsGkAtoms::width},
                                                         {nullptr}};
 
-static Element::MappedAttributeEntry sTokenStyles[] = {
-    {nsGkAtoms::mathsize_},    {nsGkAtoms::fontsize_},
-    {nsGkAtoms::color},        {nsGkAtoms::fontfamily_},
-    {nsGkAtoms::fontstyle_},   {nsGkAtoms::fontweight_},
-    {nsGkAtoms::mathvariant_}, {nullptr}};
+// https://mathml-refresh.github.io/mathml-core/#global-attributes
+static Element::MappedAttributeEntry sGlobalAttributes[] = {
+    {nsGkAtoms::dir},
+    {nsGkAtoms::mathbackground_},
+    {nsGkAtoms::mathcolor_},
+    {nsGkAtoms::mathsize_},
+    {nsGkAtoms::mathvariant_},
+    {nsGkAtoms::scriptlevel_},
+    // XXXfredw: Also map displaystyle to CSS math-style?
+    {nullptr}};
 
-static Element::MappedAttributeEntry sEnvironmentStyles[] = {
-    {nsGkAtoms::scriptlevel_},
+// XXXfredw: Add a runtime flag to disable these attributes.
+static Element::MappedAttributeEntry sMathML3Attributes[] = {
+    // XXXfredw(bug 1548471)
     {nsGkAtoms::scriptminsize_},
     {nsGkAtoms::scriptsizemultiplier_},
+    // XXXfredw(bug 1548524)
     {nsGkAtoms::background},
+    {nsGkAtoms::color},
+    {nsGkAtoms::fontfamily_},
+    {nsGkAtoms::fontsize_},
+    {nsGkAtoms::fontstyle_},
+    {nsGkAtoms::fontweight_},
     {nullptr}};
 
-static Element::MappedAttributeEntry sCommonPresStyles[] = {
-    {nsGkAtoms::mathcolor_}, {nsGkAtoms::mathbackground_}, {nullptr}};
-
-static Element::MappedAttributeEntry sDirStyles[] = {{nsGkAtoms::dir},
-                                                     {nullptr}};
-
 bool nsMathMLElement::IsAttributeMapped(const nsAtom* aAttribute) const {
   MOZ_ASSERT(IsMathMLElement());
 
-  static const MappedAttributeEntry* const mtableMap[] = {sMtableStyles,
-                                                          sCommonPresStyles};
-  static const MappedAttributeEntry* const tokenMap[] = {
-      sTokenStyles, sCommonPresStyles, sDirStyles};
-  static const MappedAttributeEntry* const mstyleMap[] = {
-      sTokenStyles, sEnvironmentStyles, sCommonPresStyles, sDirStyles};
-  static const MappedAttributeEntry* const commonPresMap[] = {
-      sCommonPresStyles};
-  static const MappedAttributeEntry* const mrowMap[] = {sCommonPresStyles,
-                                                        sDirStyles};
-
-  // We don't support mglyph (yet).
-  if (IsAnyOfMathMLElements(nsGkAtoms::ms_, nsGkAtoms::mi_, nsGkAtoms::mn_,
-                            nsGkAtoms::mo_, nsGkAtoms::mtext_,
-                            nsGkAtoms::mspace_))
-    return FindAttributeDependence(aAttribute, tokenMap);
-  if (IsAnyOfMathMLElements(nsGkAtoms::mstyle_, nsGkAtoms::math))
-    return FindAttributeDependence(aAttribute, mstyleMap);
-
+  static const MappedAttributeEntry* const mtableMap[] = {
+      sMtableStyles, sGlobalAttributes, sMathML3Attributes};
   if (mNodeInfo->Equals(nsGkAtoms::mtable_))
     return FindAttributeDependence(aAttribute, mtableMap);
 
-  if (mNodeInfo->Equals(nsGkAtoms::mrow_))
-    return FindAttributeDependence(aAttribute, mrowMap);
-
-  if (IsAnyOfMathMLElements(
-          nsGkAtoms::maction_, nsGkAtoms::maligngroup_, nsGkAtoms::malignmark_,
-          nsGkAtoms::menclose_, nsGkAtoms::merror_, nsGkAtoms::mfenced_,
-          nsGkAtoms::mfrac_, nsGkAtoms::mover_, nsGkAtoms::mpadded_,
-          nsGkAtoms::mphantom_, nsGkAtoms::mprescripts_, nsGkAtoms::mroot_,
-          nsGkAtoms::msqrt_, nsGkAtoms::msub_, nsGkAtoms::msubsup_,
-          nsGkAtoms::msup_, nsGkAtoms::mtd_, nsGkAtoms::mtr_,
-          nsGkAtoms::munder_, nsGkAtoms::munderover_, nsGkAtoms::none)) {
-    return FindAttributeDependence(aAttribute, commonPresMap);
-  }
-
-  return false;
+  static const MappedAttributeEntry* const mathmlMap[] = {sGlobalAttributes,
+                                                          sMathML3Attributes};
+  return FindAttributeDependence(aAttribute, mathmlMap);
 }
 
 nsMapRuleToAttributesFunc nsMathMLElement::GetAttributeMappingFunction() const {
   // It doesn't really matter what our tag is here, because only attributes
   // that satisfy IsAttributeMapped will be stored in the mapped attributes
   // list and available to the mapping function
   return &MapMathMLAttributesInto;
 }
@@ -876,24 +853,16 @@ bool nsMathMLElement::IsFocusableInterna
   if (aTabIndex) {
     *aTabIndex = -1;
   }
 
   return false;
 }
 
 bool nsMathMLElement::IsLink(nsIURI** aURI) const {
-  // http://www.w3.org/TR/2010/REC-MathML3-20101021/chapter6.html#interf.link
-  // The REC says that the following elements should not be linking elements:
-  if (IsAnyOfMathMLElements(nsGkAtoms::mprescripts_, nsGkAtoms::none,
-                            nsGkAtoms::malignmark_, nsGkAtoms::maligngroup_)) {
-    *aURI = nullptr;
-    return false;
-  }
-
   bool hasHref = false;
   const nsAttrValue* href = mAttrs.GetAttr(nsGkAtoms::href, kNameSpaceID_None);
   if (href) {
     // MathML href
     // The REC says: "When user agents encounter MathML elements with both href
     // and xlink:href attributes, the href attribute should take precedence."
     hasHref = true;
   } else {
--- a/dom/notification/test/mochitest/mochitest.ini
+++ b/dom/notification/test/mochitest/mochitest.ini
@@ -4,15 +4,16 @@ support-files =
   blank.html
   create_notification.html
   MockServices.js
   NotificationTest.js
 skip-if = toolkit == 'android' && !is_fennec # Bug 1531097
 
 [test_notification_basics.html]
 [test_notification_crossorigin_iframe.html]
+skip-if = fission
 # This test needs to be run on HTTP (not HTTPS).
 [test_notification_insecure_context.html]
 [test_notification_storage.html]
 [test_bug931307.html]
 skip-if = (os == 'android') # Bug 1258975 on android.
 [test_notification_tag.html]
 fail-if = fission
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -38,44 +38,16 @@
 
 using namespace mozilla::Telemetry;
 
 NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager,
                   nsIChannelEventSink)
 
 static mozilla::LazyLogModule sCSMLog("CSMLog");
 
-// This allowlist contains files that are permanently allowed to use eval()-like
-// functions. It is supposed to be restricted to files that are exclusively used
-// in testing contexts.
-static nsLiteralCString evalAllowlist[] = {
-    // Test-only third-party library
-    NS_LITERAL_CSTRING("resource://testing-common/sinon-7.2.7.js"),
-    // Test-only third-party library
-    NS_LITERAL_CSTRING("resource://testing-common/ajv-4.1.1.js"),
-    // Test-only utility
-    NS_LITERAL_CSTRING("resource://testing-common/content-task.js"),
-
-    // The Browser Toolbox/Console
-    NS_LITERAL_CSTRING("debugger"),
-
-    // The following files are NOT supposed to stay on this whitelist.
-    // Bug numbers indicate planned removal of each file.
-
-    // Bug 1498560
-    NS_LITERAL_CSTRING("chrome://global/content/bindings/autocomplete.xml"),
-};
-
-// We also permit two specific idioms in eval()-like contexts. We'd like to
-// elminate these too; but there are in-the-wild Mozilla privileged extensions
-// that use them.
-static NS_NAMED_LITERAL_STRING(sAllowedEval1, "this");
-static NS_NAMED_LITERAL_STRING(sAllowedEval2,
-                               "function anonymous(\n) {\nreturn this\n}");
-
 static Atomic<bool, mozilla::Relaxed> sTelemetryEventEnabled(false);
 
 /* static */
 bool nsContentSecurityManager::AllowTopLevelNavigationToDataURI(
     nsIChannel* aChannel) {
   // Let's block all toplevel document navigations to a data: URI.
   // In all cases where the toplevel document is navigated to a
   // data: URI the triggeringPrincipal is a contentPrincipal, or
@@ -383,16 +355,44 @@ FilenameType nsContentSecurityManager::F
   }
 
   return FilenameType(kOther, Nothing());
 }
 
 /* static */
 void nsContentSecurityManager::AssertEvalNotRestricted(
     JSContext* cx, nsIPrincipal* aSubjectPrincipal, const nsAString& aScript) {
+  // This allowlist contains files that are permanently allowed to use
+  // eval()-like functions. It is supposed to be restricted to files that are
+  // exclusively used in testing contexts.
+  static nsLiteralCString evalAllowlist[] = {
+      // Test-only third-party library
+      NS_LITERAL_CSTRING("resource://testing-common/sinon-7.2.7.js"),
+      // Test-only third-party library
+      NS_LITERAL_CSTRING("resource://testing-common/ajv-4.1.1.js"),
+      // Test-only utility
+      NS_LITERAL_CSTRING("resource://testing-common/content-task.js"),
+
+      // The Browser Toolbox/Console
+      NS_LITERAL_CSTRING("debugger"),
+
+      // The following files are NOT supposed to stay on this whitelist.
+      // Bug numbers indicate planned removal of each file.
+
+      // Bug 1498560
+      NS_LITERAL_CSTRING("chrome://global/content/bindings/autocomplete.xml"),
+  };
+
+  // We also permit two specific idioms in eval()-like contexts. We'd like to
+  // elminate these too; but there are in-the-wild Mozilla privileged extensions
+  // that use them.
+  static NS_NAMED_LITERAL_STRING(sAllowedEval1, "this");
+  static NS_NAMED_LITERAL_STRING(sAllowedEval2,
+                                 "function anonymous(\n) {\nreturn this\n}");
+
   bool systemPrincipal = aSubjectPrincipal->IsSystemPrincipal();
   if (systemPrincipal &&
       StaticPrefs::security_allow_eval_with_system_principal()) {
     MOZ_LOG(
         sCSMLog, LogLevel::Debug,
         ("Allowing eval() %s because allowing pref is "
          "enabled",
          (systemPrincipal ? "with System Principal" : "in parent process")));
--- a/dom/security/test/csp/browser.ini
+++ b/dom/security/test/csp/browser.ini
@@ -3,14 +3,17 @@ support-files =
   !/dom/security/test/csp/file_testserver.sjs
   !/dom/security/test/csp/file_web_manifest.html
   !/dom/security/test/csp/file_web_manifest.json
   !/dom/security/test/csp/file_web_manifest.json^headers^
   !/dom/security/test/csp/file_web_manifest_https.html
   !/dom/security/test/csp/file_web_manifest_https.json
   !/dom/security/test/csp/file_web_manifest_mixed_content.html
   !/dom/security/test/csp/file_web_manifest_remote.html
+  file_test_browser_bookmarklets.html
+  file_test_browser_bookmarklets.html^headers^
 [browser_test_web_manifest.js]
 [browser_test_web_manifest_mixed_content.js]
+[browser_test_bookmarklets.js]
 [browser_test_uir_optional_clicks.js]
 support-files =
   file_csp_meta_uir.html
 [browser_manifest-src-override-default-src.js]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/browser_test_bookmarklets.js
@@ -0,0 +1,82 @@
+"use strict";
+
+let BASE_URL = getRootDirectory(gTestPath).replace(
+  "chrome://mochitests/content/",
+  "http://example.com/"
+);
+const DUMMY_URL = BASE_URL + "file_test_browser_bookmarklets.html";
+
+function makeBookmarkFor(url, keyword) {
+  return Promise.all([
+    PlacesUtils.bookmarks.insert({
+      parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+      title: "bookmarklet",
+      url,
+    }),
+    PlacesUtils.keywords.insert({ url, keyword }),
+  ]);
+}
+/* Test Description:
+ * 1 - Load a Page with CSP script-src: none
+ * 2 - Create a bookmarklet with javascript:window.open('about:blank')
+ * 3 - Select and enter the bookmarklet
+ *  A new tab with about:blank should be opened
+ */
+add_task(async function openKeywordBookmarkWithWindowOpen() {
+  // This is the current default, but let's not assume that...
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.link.open_newwindow", 3],
+      ["dom.disable_open_during_load", true],
+    ],
+  });
+
+  let moztab;
+  let tabOpened = BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    DUMMY_URL
+  ).then(tab => {
+    moztab = tab;
+  });
+  let keywordForBM = "openNewWindowBookmarklet";
+
+  let bookmarkInfo;
+  let bookmarkCreated = makeBookmarkFor(
+    `javascript: window.open("about:blank")`,
+    keywordForBM
+  ).then(values => {
+    bookmarkInfo = values[0];
+  });
+  await Promise.all([tabOpened, bookmarkCreated]);
+
+  registerCleanupFunction(function() {
+    return Promise.all([
+      PlacesUtils.bookmarks.remove(bookmarkInfo),
+      PlacesUtils.keywords.remove(keywordForBM),
+    ]);
+  });
+  gURLBar.value = keywordForBM;
+  gURLBar.focus();
+
+  let tabCreatedPromise = BrowserTestUtils.waitForEvent(
+    gBrowser.tabContainer,
+    "TabOpen"
+  );
+  EventUtils.synthesizeKey("KEY_Enter");
+  info("Waiting for tab being created");
+  let { target: tab } = await tabCreatedPromise;
+  info("Got tab");
+  let browser = tab.linkedBrowser;
+  if (!browser.currentURI || browser.currentURI.spec != "about:blank") {
+    info("Waiting for browser load");
+    await BrowserTestUtils.browserLoaded(browser, false, "about:blank");
+  }
+  is(
+    browser.currentURI && browser.currentURI.spec,
+    "about:blank",
+    "Tab with expected URL loaded."
+  );
+  info("Waiting to remove tab");
+  BrowserTestUtils.removeTab(tab);
+  BrowserTestUtils.removeTab(moztab);
+});
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_test_browser_bookmarklets.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta http-equiv="X-UA-Compatible" content="ie=edge">
+    <title>Document</title>
+</head>
+<body>
+    <h1>Test-Document</h1>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/security/test/csp/file_test_browser_bookmarklets.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: script-src 'none'
+Cache-Control: no-cache
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -4618,27 +4618,29 @@ nsresult EditorBase::HandleKeyPressEvent
     case NS_VK_CONTROL:
     case NS_VK_ALT:
       aKeyboardEvent->PreventDefault();  // consumed
       return NS_OK;
   }
   return NS_OK;
 }
 
-nsresult EditorBase::HandleInlineSpellCheck(
-    EditSubAction aEditSubAction, nsINode* previousSelectedNode,
-    uint32_t previousSelectedOffset, nsINode* aStartContainer,
-    uint32_t aStartOffset, nsINode* aEndContainer, uint32_t aEndOffset) {
+nsresult EditorBase::HandleInlineSpellCheck(nsINode* previousSelectedNode,
+                                            uint32_t previousSelectedOffset,
+                                            nsINode* aStartContainer,
+                                            uint32_t aStartOffset,
+                                            nsINode* aEndContainer,
+                                            uint32_t aEndOffset) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   if (!mInlineSpellChecker) {
     return NS_OK;
   }
   return mInlineSpellChecker->SpellCheckAfterEditorChange(
-      aEditSubAction, *SelectionRefPtr(), previousSelectedNode,
+      GetTopLevelEditSubAction(), *SelectionRefPtr(), previousSelectedNode,
       previousSelectedOffset, aStartContainer, aStartOffset, aEndContainer,
       aEndOffset);
 }
 
 Element* EditorBase::FindSelectionRoot(nsINode* aNode) const {
   return GetRoot();
 }
 
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -633,16 +633,31 @@ class EditorBase : public nsIEditor,
 
     void UpdateEditAction(EditAction aEditAction) { mEditAction = aEditAction; }
 
     bool CanHandle() const { return mSelection && mEditorBase.IsInitialized(); }
 
     const RefPtr<Selection>& SelectionRefPtr() const { return mSelection; }
     EditAction GetEditAction() const { return mEditAction; }
 
+    template <typename PT, typename CT>
+    void SetSpellCheckRestartPoint(const EditorDOMPointBase<PT, CT>& aPoint) {
+      MOZ_ASSERT(aPoint.IsSet());
+      // We should store only container and offset because new content may
+      // be inserted before referring child.
+      // XXX Shouldn't we compare whether aPoint is before
+      //     mSpellCheckRestartPoint if it's set.
+      mSpellCheckRestartPoint =
+          EditorDOMPoint(aPoint.GetContainer(), aPoint.Offset());
+    }
+    void ClearSpellCheckRestartPoint() { mSpellCheckRestartPoint.Clear(); }
+    const EditorDOMPoint& GetSpellCheckRestartPoint() const {
+      return mSpellCheckRestartPoint;
+    }
+
     void SetData(const nsAString& aData) { mData = aData; }
     const nsString& GetData() const { return mData; }
 
     void SetColorData(const nsAString& aData);
 
     /**
      * InitializeDataTransfer(DataTransfer*) sets mDataTransfer to
      * aDataTransfer.  In this case, aDataTransfer should not be read/write
@@ -778,16 +793,20 @@ class EditorBase : public nsIEditor,
     RangeUpdater mRangeUpdater;
 
     // The data should be set to InputEvent.data.
     nsString mData;
 
     // The dataTransfer should be set to InputEvent.dataTransfer.
     RefPtr<dom::DataTransfer> mDataTransfer;
 
+    // Start point where spell checker should check from.  This is used only
+    // by TextEditor.
+    EditorDOMPoint mSpellCheckRestartPoint;
+
     EditAction mEditAction;
     EditSubAction mTopLevelEditSubAction;
     EDirection mDirectionOfTopLevelEditSubAction;
 
     AutoEditActionDataSetter() = delete;
     AutoEditActionDataSetter(const AutoEditActionDataSetter& aOther) = delete;
   };
 
@@ -891,16 +910,32 @@ class EditorBase : public nsIEditor,
     MOZ_ASSERT(IsEditActionDataAvailable());
     return mEditActionData->RangeUpdaterRef();
   }
   const RangeUpdater& RangeUpdaterRef() const {
     MOZ_ASSERT(IsEditActionDataAvailable());
     return mEditActionData->RangeUpdaterRef();
   }
 
+  template <typename PT, typename CT>
+  void SetSpellCheckRestartPoint(const EditorDOMPointBase<PT, CT>& aPoint) {
+    MOZ_ASSERT(IsEditActionDataAvailable());
+    return mEditActionData->SetSpellCheckRestartPoint(aPoint);
+  }
+
+  void ClearSpellCheckRestartPoint() {
+    MOZ_ASSERT(IsEditActionDataAvailable());
+    return mEditActionData->ClearSpellCheckRestartPoint();
+  }
+
+  const EditorDOMPoint& GetSpellCheckRestartPoint() const {
+    MOZ_ASSERT(IsEditActionDataAvailable());
+    return mEditActionData->GetSpellCheckRestartPoint();
+  }
+
   /**
    * GetCompositionStartPoint() and GetCompositionEndPoint() returns start and
    * end point of composition string if there is.  Otherwise, returns non-set
    * DOM point.
    */
   EditorRawDOMPoint GetCompositionStartPoint() const;
   EditorRawDOMPoint GetCompositionEndPoint() const;
 
@@ -1789,18 +1824,17 @@ class EditorBase : public nsIEditor,
    * MakeThisAllowTransactionsToChangeSelection() with true makes this editor
    * allow transactions to change Selection.  Otherwise, i.e., with false,
    * makes this editor not allow transactions to change Selection.
    */
   inline void MakeThisAllowTransactionsToChangeSelection(bool aAllow) {
     mAllowsTransactionsToChangeSelection = aAllow;
   }
 
-  nsresult HandleInlineSpellCheck(EditSubAction aEditSubAction,
-                                  nsINode* previousSelectedNode,
+  nsresult HandleInlineSpellCheck(nsINode* previousSelectedNode,
                                   uint32_t previousSelectedOffset,
                                   nsINode* aStartContainer,
                                   uint32_t aStartOffset, nsINode* aEndContainer,
                                   uint32_t aEndOffset);
 
   /**
    * Likewise, but gets the editor's root instead, which is different for HTML
    * editors.
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -184,32 +184,35 @@ class MOZ_RAII AutoSetTemporaryAncestorL
 };
 
 /********************************************************
  * mozilla::HTMLEditRules
  ********************************************************/
 
 HTMLEditRules::HTMLEditRules()
     : mHTMLEditor(nullptr),
+      mInitialized(false),
       mListenerEnabled(false),
       mReturnInEmptyLIKillsList(false),
       mDidDeleteSelection(false),
+      mDidExplicitlySetInterline(false),
       mDidRangedDelete(false),
       mDidEmptyParentBlocksRemoved(false),
       mRestoreContentEditableCount(false),
       mJoinOffset(0) {
   mIsHTMLEditRules = true;
   InitFields();
 }
 
 void HTMLEditRules::InitFields() {
   mHTMLEditor = nullptr;
   mDocChangeRange = nullptr;
   mReturnInEmptyLIKillsList = true;
   mDidDeleteSelection = false;
+  mDidExplicitlySetInterline = false;
   mDidRangedDelete = false;
   mDidEmptyParentBlocksRemoved = false;
   mRestoreContentEditableCount = false;
   mUtilRange = nullptr;
   mJoinOffset = 0;
   mNewBlock = nullptr;
   mRangeItem = new RangeItem();
 
@@ -278,19 +281,16 @@ nsresult HTMLEditRules::Init(TextEditor*
     node = HTMLEditorRef().GetDocument();
     if (NS_WARN_IF(!node)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   mUtilRange = new nsRange(node);
 
-  // set up mDocChangeRange to be whole doc
-  // temporarily turn off rules sniffing
-  AutoLockRulesSniffing lockIt(this);
   if (!mDocChangeRange) {
     mDocChangeRange = new nsRange(node);
   }
 
   if (node->IsElement()) {
     ErrorResult error;
     mDocChangeRange->SelectNode(*node, error);
     if (NS_WARN_IF(error.Failed())) {
@@ -302,205 +302,217 @@ nsresult HTMLEditRules::Init(TextEditor*
     }
     NS_WARNING_ASSERTION(
         NS_SUCCEEDED(rv),
         "Failed to insert <br> elements to empty list items and table cells");
   }
 
   StartToListenToEditSubActions();
 
+  mInitialized = true;  // Start to handle edit sub-actions.
+
   return NS_OK;
 }
 
 nsresult HTMLEditRules::DetachEditor() {
   EndListeningToEditSubActions();
   mHTMLEditor = nullptr;
   return TextEditRules::DetachEditor();
 }
 
-nsresult HTMLEditRules::BeforeEdit(EditSubAction aEditSubAction,
-                                   nsIEditor::EDirection aDirection) {
+nsresult HTMLEditRules::BeforeEdit() {
+  MOZ_ASSERT(!mIsHandling);
+
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
-  if (mLockRulesSniffing) {
-    return NS_OK;
-  }
-
-  AutoLockRulesSniffing lockIt(this);
+  if (!mInitialized) {
+    return NS_OK;  // We should do nothing if we're being initialized.
+  }
+
   mDidExplicitlySetInterline = false;
 
-  if (!mActionNesting) {
-    mActionNesting++;
-
-    // Clear our flag about if just deleted a range
-    mDidRangedDelete = false;
-
-    AutoSafeEditorData setData(*this, *mHTMLEditor);
-
-    // Remember where our selection was before edit action took place:
-    if (HTMLEditorRef().GetCompositionStartPoint().IsSet()) {
-      // If there is composition string, let's remember current composition
-      // range.
-      mRangeItem->StoreRange(HTMLEditorRef().GetCompositionStartPoint(),
-                             HTMLEditorRef().GetCompositionEndPoint());
-    } else {
-      // Get the selection location
-      if (!SelectionRefPtr()->RangeCount()) {
-        return NS_ERROR_UNEXPECTED;
-      }
-      mRangeItem->StoreRange(SelectionRefPtr()->GetRangeAt(0));
-    }
-    nsCOMPtr<nsINode> selStartNode = mRangeItem->mStartContainer;
-    nsCOMPtr<nsINode> selEndNode = mRangeItem->mEndContainer;
-
-    // Register with range updater to track this as we perturb the doc
-    HTMLEditorRef().RangeUpdaterRef().RegisterRangeItem(mRangeItem);
-
-    // Clear deletion state bool
-    mDidDeleteSelection = false;
-
-    // Clear out mDocChangeRange and mUtilRange
-    if (mDocChangeRange) {
-      // Clear out our accounting of what changed
-      mDocChangeRange->Reset();
-    }
-    if (mUtilRange) {
-      // Ditto for mUtilRange.
-      mUtilRange->Reset();
-    }
-
-    // Remember current inline styles for deletion and normal insertion ops
-    if (aEditSubAction == EditSubAction::eInsertText ||
-        aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
-        aEditSubAction == EditSubAction::eDeleteSelectedContent ||
-        IsStyleCachePreservingSubAction(aEditSubAction)) {
-      nsCOMPtr<nsINode> selNode =
-          aDirection == nsIEditor::eNext ? selEndNode : selStartNode;
-      nsresult rv = CacheInlineStyles(selNode);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    // Stabilize the document against contenteditable count changes
+#ifdef DEBUG
+  mIsHandling = true;
+#endif  // #ifdef DEBUG
+
+  // Clear our flag about if just deleted a range
+  mDidRangedDelete = false;
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor);
+
+  // Remember where our selection was before edit action took place:
+  if (HTMLEditorRef().GetCompositionStartPoint().IsSet()) {
+    // If there is composition string, let's remember current composition
+    // range.
+    mRangeItem->StoreRange(HTMLEditorRef().GetCompositionStartPoint(),
+                           HTMLEditorRef().GetCompositionEndPoint());
+  } else {
+    // Get the selection location
+    if (!SelectionRefPtr()->RangeCount()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    mRangeItem->StoreRange(SelectionRefPtr()->GetRangeAt(0));
+  }
+  nsCOMPtr<nsINode> selStartNode = mRangeItem->mStartContainer;
+  nsCOMPtr<nsINode> selEndNode = mRangeItem->mEndContainer;
+
+  // Register with range updater to track this as we perturb the doc
+  HTMLEditorRef().RangeUpdaterRef().RegisterRangeItem(mRangeItem);
+
+  // Clear deletion state bool
+  mDidDeleteSelection = false;
+
+  // Clear out mDocChangeRange and mUtilRange
+  if (mDocChangeRange) {
+    // Clear out our accounting of what changed
+    mDocChangeRange->Reset();
+  }
+  if (mUtilRange) {
+    // Ditto for mUtilRange.
+    mUtilRange->Reset();
+  }
+
+  // Remember current inline styles for deletion and normal insertion ops
+  bool cacheInlineStyles;
+  switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
+    case EditSubAction::eInsertText:
+    case EditSubAction::eInsertTextComingFromIME:
+    case EditSubAction::eDeleteSelectedContent:
+      cacheInlineStyles = true;
+      break;
+    default:
+      cacheInlineStyles = IsStyleCachePreservingSubAction(
+          HTMLEditorRef().GetTopLevelEditSubAction());
+      break;
+  }
+  if (cacheInlineStyles) {
+    nsCOMPtr<nsINode> selNode =
+        HTMLEditorRef().GetDirectionOfTopLevelEditSubAction() ==
+                nsIEditor::eNext
+            ? selEndNode
+            : selStartNode;
+    nsresult rv = CacheInlineStyles(selNode);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  // Stabilize the document against contenteditable count changes
+  Document* doc = HTMLEditorRef().GetDocument();
+  if (NS_WARN_IF(!doc)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (doc->GetEditingState() == Document::EditingState::eContentEditable) {
+    doc->ChangeContentEditableCount(nullptr, +1);
+    mRestoreContentEditableCount = true;
+  }
+
+  // Check that selection is in subtree defined by body node
+  nsresult rv = ConfirmSelectionInBody();
+  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
+  return NS_OK;
+}
+
+nsresult HTMLEditRules::AfterEdit() {
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
+  if (!mInitialized) {
+    return NS_OK;  // We should do nothing if we're being initialized.
+  }
+
+#ifdef DEBUG
+  MOZ_ASSERT(mIsHandling);
+  mIsHandling = false;
+#endif  // #ifdef DEBUG
+
+  AutoSafeEditorData setData(*this, *mHTMLEditor);
+
+  // Do all the tricky stuff
+  nsresult rv = AfterEditInner();
+  // Perhaps, we need to do the following jobs even if the editor has been
+  // destroyed since they adjust some states of HTML document but don't
+  // modify the DOM tree nor Selection.
+
+  // Free up selectionState range item
+  HTMLEditorRef().RangeUpdaterRef().DropRangeItem(mRangeItem);
+
+  // Reset the contenteditable count to its previous value
+  if (mRestoreContentEditableCount) {
     Document* doc = HTMLEditorRef().GetDocument();
     if (NS_WARN_IF(!doc)) {
       return NS_ERROR_FAILURE;
     }
     if (doc->GetEditingState() == Document::EditingState::eContentEditable) {
-      doc->ChangeContentEditableCount(nullptr, +1);
-      mRestoreContentEditableCount = true;
-    }
-
-    // Check that selection is in subtree defined by body node
-    nsresult rv = ConfirmSelectionInBody();
-    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-      return NS_ERROR_EDITOR_DESTROYED;
-    }
-    // Let rules remember the top level action
-    mTopLevelEditSubAction = aEditSubAction;
-  }
-  return NS_OK;
-}
-
-nsresult HTMLEditRules::AfterEdit(EditSubAction aEditSubAction,
-                                  nsIEditor::EDirection aDirection) {
-  if (NS_WARN_IF(!CanHandleEditAction())) {
-    return NS_ERROR_EDITOR_DESTROYED;
-  }
-
-  if (mLockRulesSniffing) {
-    return NS_OK;
-  }
-
-  AutoLockRulesSniffing lockIt(this);
-
-  MOZ_ASSERT(mActionNesting > 0);
-  nsresult rv = NS_OK;
-  mActionNesting--;
-  if (!mActionNesting) {
-    AutoSafeEditorData setData(*this, *mHTMLEditor);
-
-    // Do all the tricky stuff
-    rv = AfterEditInner(aEditSubAction, aDirection);
-    // Perhaps, we need to do the following jobs even if the editor has been
-    // destroyed since they adjust some states of HTML document but don't
-    // modify the DOM tree nor Selection.
-
-    // Free up selectionState range item
-    HTMLEditorRef().RangeUpdaterRef().DropRangeItem(mRangeItem);
-
-    // Reset the contenteditable count to its previous value
-    if (mRestoreContentEditableCount) {
-      Document* doc = HTMLEditorRef().GetDocument();
-      if (NS_WARN_IF(!doc)) {
-        return NS_ERROR_FAILURE;
-      }
-      if (doc->GetEditingState() == Document::EditingState::eContentEditable) {
-        doc->ChangeContentEditableCount(nullptr, -1);
-      }
-      mRestoreContentEditableCount = false;
-    }
-  }
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
-}
-
-nsresult HTMLEditRules::AfterEditInner(EditSubAction aEditSubAction,
-                                       nsIEditor::EDirection aDirection) {
+      doc->ChangeContentEditableCount(nullptr, -1);
+    }
+    mRestoreContentEditableCount = false;
+  }
+
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AfterEditInner() failed");
+  return rv;
+}
+
+nsresult HTMLEditRules::AfterEditInner() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsresult rv = ConfirmSelectionInBody();
   if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to normalize Selection");
-  if (aEditSubAction == EditSubAction::eReplaceHeadWithHTMLSource ||
-      aEditSubAction == EditSubAction::eCreatePaddingBRElementForEmptyEditor) {
-    return NS_OK;
+  switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
+    case EditSubAction::eReplaceHeadWithHTMLSource:
+    case EditSubAction::eCreatePaddingBRElementForEmptyEditor:
+      return NS_OK;
+    default:
+      break;
   }
 
   nsCOMPtr<nsINode> rangeStartContainer, rangeEndContainer;
   uint32_t rangeStartOffset = 0, rangeEndOffset = 0;
   // do we have a real range to act on?
   bool bDamagedRange = false;
   if (mDocChangeRange) {
     rangeStartContainer = mDocChangeRange->GetStartContainer();
     rangeEndContainer = mDocChangeRange->GetEndContainer();
     rangeStartOffset = mDocChangeRange->StartOffset();
     rangeEndOffset = mDocChangeRange->EndOffset();
     if (rangeStartContainer && rangeEndContainer) {
       bDamagedRange = true;
     }
   }
 
-  if (bDamagedRange && !((aEditSubAction == EditSubAction::eUndo) ||
-                         (aEditSubAction == EditSubAction::eRedo))) {
+  if (bDamagedRange &&
+      HTMLEditorRef().GetTopLevelEditSubAction() != EditSubAction::eUndo &&
+      HTMLEditorRef().GetTopLevelEditSubAction() != EditSubAction::eRedo) {
     // don't let any txns in here move the selection around behind our back.
     // Note that this won't prevent explicit selection setting from working.
     AutoTransactionsConserveSelection dontChangeMySelection(HTMLEditorRef());
 
     // expand the "changed doc range" as needed
-    PromoteRange(*mDocChangeRange, aEditSubAction);
+    PromoteRange(*mDocChangeRange, HTMLEditorRef().GetTopLevelEditSubAction());
 
     // if we did a ranged deletion or handling backspace key, make sure we have
     // a place to put caret.
     // Note we only want to do this if the overall operation was deletion,
     // not if deletion was done along the way for
     // EditSubAction::eInsertHTMLSource, EditSubAction::eInsertText, etc.
     // That's why this is here rather than DidDeleteSelection().
     // However, we shouldn't insert <br> elements if we've already removed
     // empty block parents because users may want to disappear the line by
     // the deletion.
-    if (aEditSubAction == EditSubAction::eDeleteSelectedContent &&
+    if (HTMLEditorRef().GetTopLevelEditSubAction() ==
+            EditSubAction::eDeleteSelectedContent &&
         mDidRangedDelete && !mDidEmptyParentBlocksRemoved) {
       nsresult rv = InsertBRIfNeeded();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
     // add in any needed <br>s, and remove any unneeded ones.
@@ -508,80 +520,93 @@ nsresult HTMLEditRules::AfterEditInner(E
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     NS_WARNING_ASSERTION(
         NS_SUCCEEDED(rv),
         "Failed to insert <br> elements to empty list items and table cells");
 
     // merge any adjacent text nodes
-    if (aEditSubAction != EditSubAction::eInsertText &&
-        aEditSubAction != EditSubAction::eInsertTextComingFromIME) {
-      RefPtr<nsRange> docChangeRange = mDocChangeRange;
-      nsresult rv = MOZ_KnownLive(HTMLEditorRef())
-                        .CollapseAdjacentTextNodes(docChangeRange);
-      if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
-      }
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+    switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
+      case EditSubAction::eInsertText:
+      case EditSubAction::eInsertTextComingFromIME:
+        break;
+      default: {
+        RefPtr<nsRange> docChangeRange = mDocChangeRange;
+        nsresult rv = MOZ_KnownLive(HTMLEditorRef())
+                          .CollapseAdjacentTextNodes(docChangeRange);
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        break;
       }
     }
 
     // clean up any empty nodes in the selection
     rv = RemoveEmptyNodesInChangedRange();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // attempt to transform any unneeded nbsp's into spaces after doing various
     // operations
-    if (aEditSubAction == EditSubAction::eInsertText ||
-        aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
-        aEditSubAction == EditSubAction::eDeleteSelectedContent ||
-        aEditSubAction == EditSubAction::eInsertLineBreak ||
-        aEditSubAction == EditSubAction::eInsertParagraphSeparator ||
-        aEditSubAction == EditSubAction::ePasteHTMLContent ||
-        aEditSubAction == EditSubAction::eInsertHTMLSource) {
-      // TODO: Temporarily, WSRunObject replaces ASCII whitespaces with NPSPs
-      //       and then, we'll replace them with ASCII whitespaces here.  We
-      //       should avoid this overwriting things as far as possible because
-      //       replacing characters in text nodes causes running mutation event
-      //       listeners which are really expensive.
-      // Adjust end of composition string if there is composition string.
-      EditorRawDOMPoint pointToAdjust(HTMLEditorRef().GetCompositionEndPoint());
-      if (!pointToAdjust.IsSet()) {
-        // Otherwise, adjust current selection start point.
-        pointToAdjust = EditorBase::GetStartPoint(*SelectionRefPtr());
-        if (NS_WARN_IF(!pointToAdjust.IsSet())) {
+    switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
+      case EditSubAction::eInsertText:
+      case EditSubAction::eInsertTextComingFromIME:
+      case EditSubAction::eDeleteSelectedContent:
+      case EditSubAction::eInsertLineBreak:
+      case EditSubAction::eInsertParagraphSeparator:
+      case EditSubAction::ePasteHTMLContent:
+      case EditSubAction::eInsertHTMLSource: {
+        // TODO: Temporarily, WSRunObject replaces ASCII whitespaces with NPSPs
+        //       and then, we'll replace them with ASCII whitespaces here.  We
+        //       should avoid this overwriting things as far as possible because
+        //       replacing characters in text nodes causes running mutation
+        //       event listeners which are really expensive.
+        // Adjust end of composition string if there is composition string.
+        EditorRawDOMPoint pointToAdjust(
+            HTMLEditorRef().GetCompositionEndPoint());
+        if (!pointToAdjust.IsSet()) {
+          // Otherwise, adjust current selection start point.
+          pointToAdjust = EditorBase::GetStartPoint(*SelectionRefPtr());
+          if (NS_WARN_IF(!pointToAdjust.IsSet())) {
+            return NS_ERROR_FAILURE;
+          }
+        }
+        rv = WSRunObject(&HTMLEditorRef(), pointToAdjust).AdjustWhitespace();
+        if (NS_WARN_IF(!CanHandleEditAction())) {
+          return NS_ERROR_EDITOR_DESTROYED;
+        }
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        // also do this for original selection endpoints.
+        if (NS_WARN_IF(!mRangeItem->mStartContainer) ||
+            NS_WARN_IF(!mRangeItem->mEndContainer)) {
           return NS_ERROR_FAILURE;
         }
-      }
-      rv = WSRunObject(&HTMLEditorRef(), pointToAdjust).AdjustWhitespace();
-      if (NS_WARN_IF(!CanHandleEditAction())) {
-        return NS_ERROR_EDITOR_DESTROYED;
-      }
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-
-      // also do this for original selection endpoints.
-      NS_ENSURE_STATE(mRangeItem->mStartContainer);
-      NS_ENSURE_STATE(mRangeItem->mEndContainer);
-      WSRunObject(&HTMLEditorRef(), mRangeItem->mStartContainer,
-                  mRangeItem->mStartOffset)
-          .AdjustWhitespace();
-      // we only need to handle old selection endpoint if it was different from
-      // start
-      if (mRangeItem->mStartContainer != mRangeItem->mEndContainer ||
-          mRangeItem->mStartOffset != mRangeItem->mEndOffset) {
-        WSRunObject(&HTMLEditorRef(), mRangeItem->mEndContainer,
-                    mRangeItem->mEndOffset)
+        WSRunObject(&HTMLEditorRef(), mRangeItem->mStartContainer,
+                    mRangeItem->mStartOffset)
             .AdjustWhitespace();
-      }
+        // we only need to handle old selection endpoint if it was different
+        // from start
+        if (mRangeItem->mStartContainer != mRangeItem->mEndContainer ||
+            mRangeItem->mStartOffset != mRangeItem->mEndOffset) {
+          WSRunObject(&HTMLEditorRef(), mRangeItem->mEndContainer,
+                      mRangeItem->mEndOffset)
+              .AdjustWhitespace();
+        }
+        break;
+      }
+      default:
+        break;
     }
 
     // if we created a new block, make sure selection lands in it
     if (mNewBlock) {
       rv = PinSelectionToNewBlock();
       if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
@@ -590,50 +615,60 @@ nsresult HTMLEditRules::AfterEditInner(E
       mNewBlock = nullptr;
     }
 
     // Adjust selection for insert text, html paste, and delete actions if
     // we haven't removed new empty blocks.  Note that if empty block parents
     // are removed, Selection should've been adjusted by the method which
     // did it.
     if (!mDidEmptyParentBlocksRemoved) {
-      switch (aEditSubAction) {
+      switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
         case EditSubAction::eInsertText:
         case EditSubAction::eInsertTextComingFromIME:
         case EditSubAction::eDeleteSelectedContent:
         case EditSubAction::eInsertLineBreak:
         case EditSubAction::eInsertParagraphSeparator:
         case EditSubAction::ePasteHTMLContent:
         case EditSubAction::eInsertHTMLSource:
-          rv = AdjustSelection(aDirection);
+          rv = AdjustSelection(
+              HTMLEditorRef().GetDirectionOfTopLevelEditSubAction());
           if (NS_WARN_IF(NS_FAILED(rv))) {
             return rv;
           }
           break;
         default:
           break;
       }
     }
 
     // check for any styles which were removed inappropriately
-    if (aEditSubAction == EditSubAction::eInsertText ||
-        aEditSubAction == EditSubAction::eInsertTextComingFromIME ||
-        aEditSubAction == EditSubAction::eDeleteSelectedContent ||
-        IsStyleCachePreservingSubAction(aEditSubAction)) {
+    bool reapplyCachedStyle;
+    switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
+      case EditSubAction::eInsertText:
+      case EditSubAction::eInsertTextComingFromIME:
+      case EditSubAction::eDeleteSelectedContent:
+        reapplyCachedStyle = true;
+        break;
+      default:
+        reapplyCachedStyle = IsStyleCachePreservingSubAction(
+            HTMLEditorRef().GetTopLevelEditSubAction());
+        break;
+    }
+    if (reapplyCachedStyle) {
       HTMLEditorRef().mTypeInState->UpdateSelState(SelectionRefPtr());
       rv = ReapplyCachedStyles();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       ClearCachedStyles();
     }
   }
 
   rv = HTMLEditorRef().HandleInlineSpellCheck(
-      aEditSubAction, mRangeItem->mStartContainer, mRangeItem->mStartOffset,
+      mRangeItem->mStartContainer, mRangeItem->mStartOffset,
       rangeStartContainer, rangeStartOffset, rangeEndContainer, rangeEndOffset);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // detect empty doc
   // XXX Need to investigate when the padding <br> element is removed because
   //     I don't see the <br> element with testing manually.  If it won't be
@@ -1308,28 +1343,35 @@ nsresult HTMLEditRules::WillInsert(bool*
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(error.Failed())) {
         return error.StealNSResult();
       }
     }
   }
 
-  if (mDidDeleteSelection &&
-      (mTopLevelEditSubAction == EditSubAction::eInsertText ||
-       mTopLevelEditSubAction == EditSubAction::eInsertTextComingFromIME ||
-       mTopLevelEditSubAction == EditSubAction::eDeleteSelectedContent)) {
-    nsresult rv = ReapplyCachedStyles();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+  if (mDidDeleteSelection) {
+    switch (HTMLEditorRef().GetTopLevelEditSubAction()) {
+      case EditSubAction::eInsertText:
+      case EditSubAction::eInsertTextComingFromIME:
+      case EditSubAction::eDeleteSelectedContent: {
+        nsresult rv = ReapplyCachedStyles();
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        break;
+      }
+      default:
+        break;
     }
   }
   // For most actions we want to clear the cached styles, but there are
   // exceptions
-  if (!IsStyleCachePreservingSubAction(mTopLevelEditSubAction)) {
+  if (!IsStyleCachePreservingSubAction(
+          HTMLEditorRef().GetTopLevelEditSubAction())) {
     ClearCachedStyles();
   }
   return NS_OK;
 }
 
 nsresult HTMLEditRules::WillInsertText(EditSubAction aEditSubAction,
                                        bool* aCancel, bool* aHandled,
                                        const nsAString* inString,
@@ -3886,21 +3928,22 @@ nsresult HTMLEditRules::DidDeleteSelecti
         NS_WARNING_ASSERTION(
             !error.Failed(),
             "Failed to collapse selection at the new <br> element");
       }
     }
   }
 
   // call through to base class
-  nsresult rv = TextEditRules::DidDeleteSelection();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
+  nsresult rv = TextEditRules::DidDeleteSelection(
+      mDidExplicitlySetInterline ? SetSelectionInterLinePosition::No
+                                 : SetSelectionInterLinePosition::Yes);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                       "TextEditRules::DidDeleteSelection() failed");
+  return rv;
 }
 
 nsresult HTMLEditRules::WillMakeList(const nsAString* aListType,
                                      bool aEntireList,
                                      const nsAString* aBulletType,
                                      bool* aCancel, bool* aHandled,
                                      const nsAString* aItemType) {
   MOZ_ASSERT(IsEditorDataAvailable());
@@ -9225,17 +9268,18 @@ nsresult HTMLEditRules::ReapplyCachedSty
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       // This style has disappeared through deletion.  Let's add the styles to
       // mTypeInState when same style isn't applied to the node already.
-      if ((!bAny || IsStyleCachePreservingSubAction(mTopLevelEditSubAction)) &&
+      if ((!bAny || IsStyleCachePreservingSubAction(
+                        HTMLEditorRef().GetTopLevelEditSubAction())) &&
           (!styleAtInsertionPoint[i].mPresent ||
            styleAtInsertionPoint[i].value != mCachedStyles[i].value)) {
         HTMLEditorRef().mTypeInState->SetProp(mCachedStyles[i].tag,
                                               mCachedStyles[i].attr,
                                               mCachedStyles[i].value);
       }
     }
   }
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -73,21 +73,18 @@ struct StyleCache final : public PropIte
 class HTMLEditRules : public TextEditRules {
  public:
   HTMLEditRules();
 
   // TextEditRules methods
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult Init(TextEditor* aTextEditor) override;
   virtual nsresult DetachEditor() override;
-  virtual nsresult BeforeEdit(EditSubAction aEditSubAction,
-                              nsIEditor::EDirection aDirection) override;
-  MOZ_CAN_RUN_SCRIPT
-  virtual nsresult AfterEdit(EditSubAction aEditSubAction,
-                             nsIEditor::EDirection aDirection) override;
+  virtual nsresult BeforeEdit() override;
+  MOZ_CAN_RUN_SCRIPT virtual nsresult AfterEdit() override;
   // NOTE: Don't mark WillDoAction() nor DidDoAction() as MOZ_CAN_RUN_SCRIPT
   //       because they are too generic and doing it makes a lot of public
   //       editor methods marked as MOZ_CAN_RUN_SCRIPT too, but some of them
   //       may not causes running script.  So, ideal fix must be that we make
   //       each method callsed by this method public.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual nsresult WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
                                 bool* aHandled) override;
@@ -696,19 +693,17 @@ class HTMLEditRules : public TextEditRul
   MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult ReturnInListItem(Element& aListItem, nsINode& aNode,
                                          int32_t aOffset);
 
   /**
    * Called after handling edit action.  This may adjust Selection, remove
    * unnecessary empty nodes, create <br> elements if needed, etc.
    */
-  MOZ_CAN_RUN_SCRIPT
-  MOZ_MUST_USE nsresult AfterEditInner(EditSubAction aEditSubAction,
-                                       nsIEditor::EDirection aDirection);
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult AfterEditInner();
 
   /**
    * IndentAroundSelectionWithCSS() indents around Selection with CSS.
    * This method creates AutoSelectionRestorer.  Therefore, each caller
    * need to check if the editor is still available even if this returns
    * NS_OK.
    */
   MOZ_CAN_RUN_SCRIPT
@@ -1348,19 +1343,21 @@ class HTMLEditRules : public TextEditRul
    * values of CSS properties.
    */
   MOZ_MUST_USE nsresult
   GetInlineStyles(nsINode* aNode, StyleCache aStyleCache[SIZE_STYLE_TABLE]);
 
  protected:
   HTMLEditor* mHTMLEditor;
   RefPtr<nsRange> mDocChangeRange;
+  bool mInitialized;
   bool mListenerEnabled;
   bool mReturnInEmptyLIKillsList;
   bool mDidDeleteSelection;
+  bool mDidExplicitlySetInterline;
   bool mDidRangedDelete;
   bool mDidEmptyParentBlocksRemoved;
   bool mRestoreContentEditableCount;
   RefPtr<nsRange> mUtilRange;
   // Need to remember an int across willJoin/didJoin...
   uint32_t mJoinOffset;
   RefPtr<Element> mNewBlock;
   RefPtr<RangeItem> mRangeItem;
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3371,16 +3371,18 @@ nsresult HTMLEditor::DeleteNodeWithTrans
   nsresult rv = EditorBase::DeleteNodeWithTransaction(aNode);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::DeleteAllChildrenWithTransaction(Element& aElement) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   // Prevent rules testing until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
       *this, EditSubAction::eDeleteNode, nsIEditor::eNext);
 
   while (nsCOMPtr<nsINode> child = aElement.GetLastChild()) {
     nsresult rv = DeleteNodeWithTransaction(*child);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -3704,31 +3706,28 @@ void HTMLEditor::OnStartToHandleTopLevel
 
   EditorBase::OnStartToHandleTopLevelEditSubAction(aEditSubAction, aDirection);
   if (!rules) {
     return;
   }
 
   MOZ_ASSERT(GetTopLevelEditSubAction() == aEditSubAction);
   MOZ_ASSERT(GetDirectionOfTopLevelEditSubAction() == aDirection);
-  DebugOnly<nsresult> rv = rules->BeforeEdit(aEditSubAction, aDirection);
+  DebugOnly<nsresult> rv = rules->BeforeEdit();
   NS_WARNING_ASSERTION(
       NS_SUCCEEDED(rv),
       "HTMLEditRules::BeforeEdit() failed to handle something");
 }
 
 void HTMLEditor::OnEndHandlingTopLevelEditSubAction() {
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // post processing
-  DebugOnly<nsresult> rv =
-      rules ? rules->AfterEdit(GetTopLevelEditSubAction(),
-                               GetDirectionOfTopLevelEditSubAction())
-            : NS_OK;
+  DebugOnly<nsresult> rv = rules ? rules->AfterEdit() : NS_OK;
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                        "HTMLEditRules::AfterEdit() failed to handle something");
   EditorBase::OnEndHandlingTopLevelEditSubAction();
   MOZ_ASSERT(!GetTopLevelEditSubAction());
   MOZ_ASSERT(GetDirectionOfTopLevelEditSubAction() == eNone);
 }
 
 bool HTMLEditor::TagCanContainTag(nsAtom& aParentTag, nsAtom& aChildTag) const {
--- a/editor/libeditor/HTMLTableEditor.cpp
+++ b/editor/libeditor/HTMLTableEditor.cpp
@@ -165,16 +165,18 @@ HTMLEditor::InsertTableCell(int32_t aNum
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::InsertTableCellsWithTransaction(
     int32_t aNumberOfCellsToInsert, InsertPosition aInsertPosition) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   RefPtr<Element> table;
   RefPtr<Element> curCell;
   nsCOMPtr<nsINode> cellParent;
   int32_t cellOffset, startRowIndex, startColIndex;
   nsresult rv = GetCellContext(getter_AddRefs(table), getter_AddRefs(curCell),
                                getter_AddRefs(cellParent), &cellOffset,
                                &startRowIndex, &startColIndex);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -582,16 +584,18 @@ HTMLEditor::InsertTableRow(int32_t aNumb
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::InsertTableRowsWithTransaction(
     int32_t aNumberOfRowsToInsert, InsertPosition aInsertPosition) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   RefPtr<Element> table;
   RefPtr<Element> curCell;
 
   int32_t startRowIndex, startColIndex;
   nsresult rv =
       GetCellContext(getter_AddRefs(table), getter_AddRefs(curCell), nullptr,
                      nullptr, &startRowIndex, &startColIndex);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1215,16 +1219,18 @@ HTMLEditor::DeleteTableColumn(int32_t aN
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::DeleteSelectedTableColumnsWithTransaction(
     int32_t aNumberOfColumnsToDelete) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   RefPtr<Element> table;
   RefPtr<Element> cell;
   int32_t startRowIndex, startColIndex;
   nsresult rv =
       GetCellContext(getter_AddRefs(table), getter_AddRefs(cell), nullptr,
                      nullptr, &startRowIndex, &startColIndex);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -1564,16 +1570,18 @@ nsresult HTMLEditor::DeleteSelectedTable
     }
   }
   return NS_OK;
 }
 
 // Helper that doesn't batch or change the selection
 nsresult HTMLEditor::DeleteTableRowWithTransaction(Element& aTableElement,
                                                    int32_t aRowIndex) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   ErrorResult error;
   TableSize tableSize(*this, aTableElement, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
   // Prevent rules testing until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
@@ -2756,17 +2764,21 @@ HTMLEditor::JoinTableCells(bool aMergeNo
     }
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::MergeCells(RefPtr<Element> aTargetCell,
                                 RefPtr<Element> aCellToMerge,
                                 bool aDeleteCellToMerge) {
-  NS_ENSURE_TRUE(aTargetCell && aCellToMerge, NS_ERROR_NULL_POINTER);
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  if (NS_WARN_IF(!aTargetCell) || NS_WARN_IF(!aCellToMerge)) {
+    return NS_ERROR_INVALID_ARG;
+  }
 
   // Prevent rules testing until we're done
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
       *this, EditSubAction::eDeleteNode, nsIEditor::eNext);
 
   // Don't need to merge if cell is empty
   if (!IsEmptyCell(aCellToMerge)) {
     // Get index of last child in target cell
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -58,62 +58,50 @@ using namespace dom;
 
 /********************************************************
  * mozilla::TextEditRules
  ********************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(TextEditRules)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TextEditRules)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedSelectionNode)
   if (HTMLEditRules* htmlEditRules = tmp->AsHTMLEditRules()) {
     HTMLEditRules* tmp = htmlEditRules;
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocChangeRange)
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mUtilRange)
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mNewBlock)
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mRangeItem)
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TextEditRules)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedSelectionNode)
   if (HTMLEditRules* htmlEditRules = tmp->AsHTMLEditRules()) {
     HTMLEditRules* tmp = htmlEditRules;
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocChangeRange)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUtilRange)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNewBlock)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRangeItem)
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(TextEditRules, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(TextEditRules, Release)
 
 TextEditRules::TextEditRules()
     : mTextEditor(nullptr),
       mData(nullptr),
-      mCachedSelectionOffset(0),
-      mActionNesting(0),
-      mLockRulesSniffing(false),
-      mDidExplicitlySetInterline(false),
-      mDeleteBidiImmediately(false),
-      mIsHTMLEditRules(false),
-      mTopLevelEditSubAction(EditSubAction::eNone) {
+#ifdef DEBUG
+      mIsHandling(false),
+#endif  // #ifdef DEBUG
+      mIsHTMLEditRules(false) {
   InitFields();
 }
 
 void TextEditRules::InitFields() {
   mTextEditor = nullptr;
-  mCachedSelectionNode = nullptr;
-  mCachedSelectionOffset = 0;
-  mActionNesting = 0;
-  mLockRulesSniffing = false;
-  mDidExplicitlySetInterline = false;
-  mDeleteBidiImmediately = false;
-  mTopLevelEditSubAction = EditSubAction::eNone;
 }
 
 HTMLEditRules* TextEditRules::AsHTMLEditRules() {
   return mIsHTMLEditRules ? static_cast<HTMLEditRules*>(this) : nullptr;
 }
 
 const HTMLEditRules* TextEditRules::AsHTMLEditRules() const {
   return mIsHTMLEditRules ? static_cast<const HTMLEditRules*>(this) : nullptr;
@@ -153,128 +141,78 @@ nsresult TextEditRules::Init(TextEditor*
   if (IsPlaintextEditor()) {
     // ensure trailing br node
     rv = CreateTrailingBRIfNeeded();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  // XXX We should use AddBoolVarCache and use "current" value at initializing.
-  mDeleteBidiImmediately =
-      Preferences::GetBool("bidi.edit.delete_immediately", false);
-
   return NS_OK;
 }
 
 nsresult TextEditRules::DetachEditor() {
   mTextEditor = nullptr;
   return NS_OK;
 }
 
-nsresult TextEditRules::BeforeEdit(EditSubAction aEditSubAction,
-                                   nsIEditor::EDirection aDirection) {
+nsresult TextEditRules::BeforeEdit() {
+  MOZ_ASSERT(!mIsHandling);
+
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+
+#ifdef DEBUG
+  mIsHandling = true;
+#endif  // #ifdef DEBUG
+
+  return NS_OK;
+}
+
+nsresult TextEditRules::AfterEdit() {
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
 
-  if (mLockRulesSniffing) {
-    return NS_OK;
-  }
+#ifdef DEBUG
+  MOZ_ASSERT(mIsHandling);
+  mIsHandling = false;
+#endif  // #ifdef DEBUG
 
-  AutoLockRulesSniffing lockIt(this);
-  mDidExplicitlySetInterline = false;
-  if (!mActionNesting) {
-    // let rules remember the top level action
-    mTopLevelEditSubAction = aEditSubAction;
-  }
-  mActionNesting++;
+  AutoSafeEditorData setData(*this, *mTextEditor);
 
-  if (aEditSubAction == EditSubAction::eSetText) {
-    // setText replaces all text, so mCachedSelectionNode might be invalid on
-    // AfterEdit.
-    // Since this will be used as start position of spellchecker, we should
-    // use root instead.
-    mCachedSelectionNode = mTextEditor->GetRoot();
-    mCachedSelectionOffset = 0;
-    return NS_OK;
-  }
-
-  Selection* selection = mTextEditor->GetSelection();
-  if (NS_WARN_IF(!selection)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (aEditSubAction == EditSubAction::eInsertText ||
-      aEditSubAction == EditSubAction::eInsertTextComingFromIME) {
-    // For spell checker, previous selected node should be text node if
-    // possible. If anchor is root of editor, it may become invalid offset
-    // after inserting text.
-    EditorRawDOMPoint point = mTextEditor->FindBetterInsertionPoint(
-        EditorRawDOMPoint(selection->AnchorRef()));
-    if (point.IsSet()) {
-      mCachedSelectionNode = point.GetContainer();
-      mCachedSelectionOffset = point.Offset();
-      return NS_OK;
-    }
+  // XXX Probably, we should spellcheck again after edit action (not top-level
+  //     sub-action) is handled because the ranges can be referred only by
+  //     users.
+  nsresult rv = TextEditorRef().HandleInlineSpellCheckAfterEdit();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
-  mCachedSelectionNode = selection->GetAnchorNode();
-  mCachedSelectionOffset = selection->AnchorOffset();
-
-  return NS_OK;
-}
-
-nsresult TextEditRules::AfterEdit(EditSubAction aEditSubAction,
-                                  nsIEditor::EDirection aDirection) {
-  if (NS_WARN_IF(!CanHandleEditAction())) {
-    return NS_ERROR_EDITOR_DESTROYED;
-  }
-
-  if (mLockRulesSniffing) {
-    return NS_OK;
+  rv = MOZ_KnownLive(TextEditorRef()).EnsurePaddingBRElementForEmptyEditor();
+  if (NS_FAILED(rv)) {
+    return rv;
   }
 
-  AutoLockRulesSniffing lockIt(this);
-
-  MOZ_ASSERT(mActionNesting > 0, "bad action nesting!");
-  if (!--mActionNesting) {
-    AutoSafeEditorData setData(*this, *mTextEditor);
-
-    nsresult rv = TextEditorRef().HandleInlineSpellCheck(
-        aEditSubAction, mCachedSelectionNode, mCachedSelectionOffset, nullptr,
-        0, nullptr, 0);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    // no longer uses mCachedSelectionNode, so release it.
-    mCachedSelectionNode = nullptr;
+  // ensure trailing br node
+  rv = CreateTrailingBRIfNeeded();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
-    rv = MOZ_KnownLive(TextEditorRef()).EnsurePaddingBRElementForEmptyEditor();
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    // ensure trailing br node
-    rv = CreateTrailingBRIfNeeded();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    // Collapse the selection to the trailing padding <br> element for empty
-    // last line if it's at the end of our text node.
-    rv = CollapseSelectionToTrailingBRIfNeeded();
-    if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
-      return NS_ERROR_EDITOR_DESTROYED;
-    }
-    NS_WARNING_ASSERTION(
-        NS_SUCCEEDED(rv),
-        "Failed to selection to after the text node in TextEditor");
+  // Collapse the selection to the trailing padding <br> element for empty
+  // last line if it's at the end of our text node.
+  rv = CollapseSelectionToTrailingBRIfNeeded();
+  if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
+    return NS_ERROR_EDITOR_DESTROYED;
   }
+  NS_WARNING_ASSERTION(
+      NS_SUCCEEDED(rv),
+      "Failed to selection to after the text node in TextEditor");
   return NS_OK;
 }
 
 nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
                                      bool* aHandled) {
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
@@ -349,17 +287,18 @@ nsresult TextEditRules::DidDoAction(Edit
   AutoSafeEditorData setData(*this, *mTextEditor);
 
   // don't let any txns in here move the selection around behind our back.
   // Note that this won't prevent explicit selection setting from working.
   AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
 
   switch (aInfo.mEditSubAction) {
     case EditSubAction::eDeleteSelectedContent:
-      return DidDeleteSelection();
+      MOZ_ASSERT(!mIsHTMLEditRules);
+      return DidDeleteSelection(SetSelectionInterLinePosition::Yes);
     case EditSubAction::eInsertElement:
     case EditSubAction::eUndo:
     case EditSubAction::eRedo:
       MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
       return NS_ERROR_UNEXPECTED;
     default:
       // Don't fail on transactions we don't handle here!
       return NS_OK;
@@ -836,16 +775,17 @@ nsresult TextEditRules::WillInsertText(E
 
   return NS_OK;
 }
 
 nsresult TextEditRules::WillSetText(bool* aCancel, bool* aHandled,
                                     const nsAString* aString,
                                     int32_t aMaxLength) {
   MOZ_ASSERT(IsEditorDataAvailable());
+  MOZ_ASSERT(!mIsHTMLEditRules);
   MOZ_ASSERT(aCancel);
   MOZ_ASSERT(aHandled);
   MOZ_ASSERT(aString);
   MOZ_ASSERT(aString->FindChar(static_cast<char16_t>('\r')) == kNotFound);
 
   // XXX If we're setting value, shouldn't we keep setting the new value here?
   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
 
@@ -950,17 +890,18 @@ nsresult TextEditRules::WillSetText(bool
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If we replaced non-empty value with empty string, we need to delete the
   // text node.
   if (tString.IsEmpty()) {
-    DebugOnly<nsresult> rvIgnored = DidDeleteSelection();
+    DebugOnly<nsresult> rvIgnored =
+        DidDeleteSelection(SetSelectionInterLinePosition::Yes);
     MOZ_ASSERT(rvIgnored != NS_ERROR_EDITOR_DESTROYED);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                          "DidDeleteSelection() failed");
   }
 
   *aHandled = true;
   return NS_OK;
 }
@@ -1076,17 +1017,18 @@ nsresult TextEditRules::DeleteSelectionW
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   *aHandled = true;
   return NS_OK;
 }
 
-nsresult TextEditRules::DidDeleteSelection() {
+nsresult TextEditRules::DidDeleteSelection(
+    SetSelectionInterLinePosition aSetSelectionInterLinePosition) {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   EditorDOMPoint selectionStartPoint(
       EditorBase::GetStartPoint(*SelectionRefPtr()));
   if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
     return NS_ERROR_FAILURE;
   }
 
@@ -1099,17 +1041,17 @@ nsresult TextEditRules::DidDeleteSelecti
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  if (mDidExplicitlySetInterline) {
+  if (aSetSelectionInterLinePosition != SetSelectionInterLinePosition::Yes) {
     return NS_OK;
   }
   // We prevent the caret from sticking on the left of prior BR
   // (i.e. the end of previous line) after this deletion.  Bug 92124
   ErrorResult err;
   SelectionRefPtr()->SetInterlinePosition(true, err);
   NS_WARNING_ASSERTION(!err.Failed(), "Failed to set interline position");
   return err.StealNSResult();
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -16,17 +16,16 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIEditor.h"
 #include "nsISupportsImpl.h"
 #include "nsString.h"
 #include "nscore.h"
 
 namespace mozilla {
 
-class AutoLockRulesSniffing;
 class EditSubActionInfo;
 class HTMLEditor;
 class HTMLEditRules;
 namespace dom {
 class HTMLBRElement;
 class Selection;
 }  // namespace dom
 
@@ -75,21 +74,18 @@ class TextEditRules {
   TextEditRules();
 
   HTMLEditRules* AsHTMLEditRules();
   const HTMLEditRules* AsHTMLEditRules() const;
 
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult Init(TextEditor* aTextEditor);
   virtual nsresult DetachEditor();
-  virtual nsresult BeforeEdit(EditSubAction aEditSubAction,
-                              nsIEditor::EDirection aDirection);
-  MOZ_CAN_RUN_SCRIPT
-  virtual nsresult AfterEdit(EditSubAction aEditSubAction,
-                             nsIEditor::EDirection aDirection);
+  virtual nsresult BeforeEdit();
+  MOZ_CAN_RUN_SCRIPT virtual nsresult AfterEdit();
   // NOTE: Don't mark WillDoAction() nor DidDoAction() as MOZ_CAN_RUN_SCRIPT
   //       because they are too generic and doing it makes a lot of public
   //       editor methods marked as MOZ_CAN_RUN_SCRIPT too, but some of them
   //       may not causes running script.  So, ideal fix must be that we make
   //       each method callsed by this method public.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual nsresult WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
                                 bool* aHandled);
@@ -213,17 +209,22 @@ class TextEditRules {
   MOZ_MUST_USE nsresult DeleteSelectionWithTransaction(
       nsIEditor::EDirection aCollapsedAction, bool* aCancel, bool* aHandled);
 
   /**
    * Called after deleted selected content.
    * This method may remove empty text node and makes guarantee that caret
    * is never at left of <br> element.
    */
-  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult DidDeleteSelection();
+  enum class SetSelectionInterLinePosition {
+    Yes,
+    No,
+  };
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult DidDeleteSelection(
+      SetSelectionInterLinePosition aSetSelectionInterLinePosition);
 
   nsresult WillSetTextProperty(bool* aCancel, bool* aHandled);
 
   nsresult WillRemoveTextProperty(bool* aCancel, bool* aHandled);
 
   /**
    * Called prior to nsIEditor::OutputToString.
    *
@@ -358,32 +359,21 @@ class TextEditRules {
   /**
    * GetTextNodeAroundSelectionStartContainer() may return a Text node around
    * start container of Selection.  If current selection container is not
    * a text node, this will look for descendants and next siblings of the
    * container.
    */
   inline already_AddRefed<nsINode> GetTextNodeAroundSelectionStartContainer();
 
-  // Cached selected node.
-  nsCOMPtr<nsINode> mCachedSelectionNode;
-  // Cached selected offset.
-  uint32_t mCachedSelectionOffset;
-  uint32_t mActionNesting;
-  bool mLockRulesSniffing;
-  bool mDidExplicitlySetInterline;
-  // In bidirectional text, delete characters not visually adjacent to the
-  // caret without moving the caret first.
-  bool mDeleteBidiImmediately;
+#ifdef DEBUG
+  bool mIsHandling;
+#endif  // #ifdef DEBUG
+
   bool mIsHTMLEditRules;
-  // The top level editor action.
-  EditSubAction mTopLevelEditSubAction;
-
-  // friends
-  friend class AutoLockRulesSniffing;
 };
 
 /**
  * An object to encapsulate any additional info needed to be passed
  * to rules system by the editor.
  * TODO: This class (almost struct, though) is ugly and its size isn't
  *       optimized.  Should be refined later.
  */
@@ -425,39 +415,16 @@ class MOZ_STACK_CLASS EditSubActionInfo 
   // EditSubAction::eSetOrClearAlignment
   const nsAString* alignType;
 
   // EditSubAction::eCreateOrRemoveBlock
   const nsAString* blockType;
 };
 
 /**
- * Stack based helper class for managing TextEditRules::mLockRluesSniffing.
- * This class sets a bool letting us know to ignore any rules sniffing
- * that tries to occur reentrantly.
- */
-class MOZ_STACK_CLASS AutoLockRulesSniffing final {
- public:
-  explicit AutoLockRulesSniffing(TextEditRules* aRules) : mRules(aRules) {
-    if (mRules) {
-      mRules->mLockRulesSniffing = true;
-    }
-  }
-
-  ~AutoLockRulesSniffing() {
-    if (mRules) {
-      mRules->mLockRulesSniffing = false;
-    }
-  }
-
- protected:
-  TextEditRules* mRules;
-};
-
-/**
  * Stack based helper class for turning on/off the edit listener.
  */
 class MOZ_STACK_CLASS AutoLockListener final {
  public:
   explicit AutoLockListener(bool* aEnabled)
       : mEnabled(aEnabled), mOldState(false) {
     if (mEnabled) {
       mOldState = *mEnabled;
--- a/editor/libeditor/TextEditRulesBidi.cpp
+++ b/editor/libeditor/TextEditRulesBidi.cpp
@@ -1,15 +1,16 @@
 /* -*- 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/TextEditRules.h"
 
+#include "mozilla/StaticPrefs_bidi.h"
 #include "mozilla/TextEditor.h"
 #include "mozilla/dom/Selection.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsFrameSelection.h"
 #include "nsIContent.h"
 #include "nsIEditor.h"
@@ -65,17 +66,18 @@ nsresult TextEditRules::CheckBidiLevelFo
       (nsIEditor::eNext == aAction || nsIEditor::eNextWord == aAction)
           ? levelAfter
           : levelBefore;
 
   if (currentCaretLevel == levelOfDeletion) {
     return NS_OK;  // perform the deletion
   }
 
-  if (!mDeleteBidiImmediately && levelBefore != levelAfter) {
+  if (!StaticPrefs::bidi_edit_delete_immediately() &&
+      levelBefore != levelAfter) {
     *aCancel = true;
   }
 
   // Set the bidi level of the caret to that of the
   // character that will be (or would have been) deleted
   frameSelection->SetCaretBidiLevel(levelOfDeletion);
   return NS_OK;
 }
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -2032,43 +2032,72 @@ nsresult TextEditor::SharedOutputString(
     aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
   }
   // If the selection isn't collapsed, we'll use the whole document.
   return ComputeValueInternal(NS_LITERAL_STRING("text/plain"), aFlags, aResult);
 }
 
 void TextEditor::OnStartToHandleTopLevelEditSubAction(
     EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   EditorBase::OnStartToHandleTopLevelEditSubAction(aEditSubAction, aDirection);
   if (!rules) {
     return;
   }
 
   MOZ_ASSERT(GetTopLevelEditSubAction() == aEditSubAction);
   MOZ_ASSERT(GetDirectionOfTopLevelEditSubAction() == aDirection);
-  DebugOnly<nsresult> rv = rules->BeforeEdit(aEditSubAction, aDirection);
+
+  if (aEditSubAction == EditSubAction::eSetText) {
+    // SetText replaces all text, so spell checker handles starting from the
+    // start of new value.
+    SetSpellCheckRestartPoint(EditorDOMPoint(mRootElement, 0));
+  } else {
+    bool useSelectionAnchor = true;
+    if (aEditSubAction == EditSubAction::eInsertText ||
+        aEditSubAction == EditSubAction::eInsertTextComingFromIME) {
+      // For spell checker, previous selected node should be text node if
+      // possible. If anchor is root of editor, it may become invalid offset
+      // after inserting text.
+      EditorRawDOMPoint point = FindBetterInsertionPoint(
+          EditorRawDOMPoint(SelectionRefPtr()->AnchorRef()));
+      if (point.IsSet()) {
+        SetSpellCheckRestartPoint(point);
+        useSelectionAnchor = false;
+      }
+    }
+    if (useSelectionAnchor && SelectionRefPtr()->AnchorRef().IsSet()) {
+      SetSpellCheckRestartPoint(
+          EditorRawDOMPoint(SelectionRefPtr()->AnchorRef()));
+    }
+  }
+
+  DebugOnly<nsresult> rv = rules->BeforeEdit();
   NS_WARNING_ASSERTION(
       NS_SUCCEEDED(rv),
       "TextEditRules::BeforeEdit() failed to handle something");
 }
 
 void TextEditor::OnEndHandlingTopLevelEditSubAction() {
-  // Protect the edit rules object from dying
-  RefPtr<TextEditRules> rules(mRules);
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  if (mRules) {
+    // Protect the edit rules object from dying
+    RefPtr<TextEditRules> rules(mRules);
 
-  // post processing
-  DebugOnly<nsresult> rv =
-      rules ? rules->AfterEdit(GetTopLevelEditSubAction(),
-                               GetDirectionOfTopLevelEditSubAction())
-            : NS_OK;
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
-                       "TextEditRules::AfterEdit() failed to handle something");
+    // post processing
+    DebugOnly<nsresult> rv = rules->AfterEdit();
+    NS_WARNING_ASSERTION(
+        NS_SUCCEEDED(rv),
+        "TextEditRules::AfterEdit() failed to handle something");
+  }
   EditorBase::OnEndHandlingTopLevelEditSubAction();
   MOZ_ASSERT(!GetTopLevelEditSubAction());
   MOZ_ASSERT(GetDirectionOfTopLevelEditSubAction() == eNone);
 }
 
 nsresult TextEditor::SelectEntireDocument() {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -586,16 +586,33 @@ class TextEditor : public EditorBase,
 
   /**
    * EnsurePaddingBRElementForEmptyEditor() creates padding <br> element for
    * empty editor or changes padding <br> element for empty last line to for
    * empty editor when we're empty.
    */
   MOZ_CAN_RUN_SCRIPT nsresult EnsurePaddingBRElementForEmptyEditor();
 
+  /**
+   * HandleInlineSpellCheckAfterEdit() does spell-check after handling top level
+   * edit subaction.
+   */
+  nsresult HandleInlineSpellCheckAfterEdit() {
+    MOZ_ASSERT(IsEditActionDataAvailable());
+    if (!GetSpellCheckRestartPoint().IsSet()) {
+      return NS_OK;  // Maybe being initialized.
+    }
+    nsresult rv = HandleInlineSpellCheck(
+        GetSpellCheckRestartPoint().GetContainer(),
+        GetSpellCheckRestartPoint().Offset(), nullptr, 0, nullptr, 0);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to spellcheck");
+    ClearSpellCheckRestartPoint();
+    return rv;
+  }
+
  protected:  // Shouldn't be used by friend classes
   virtual ~TextEditor();
 
   int32_t WrapWidth() const { return mWrapColumn; }
 
   /**
    * Make the given selection span the entire document.
    */
--- a/gfx/layers/apz/test/gtest/mvm/TestMobileViewportManager.cpp
+++ b/gfx/layers/apz/test/gtest/mvm/TestMobileViewportManager.cpp
@@ -6,16 +6,17 @@
 
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 
 #include <functional>
 
 #include "MobileViewportManager.h"
 #include "mozilla/MVMContext.h"
+#include "mozilla/dom/Event.h"
 
 using namespace mozilla;
 
 class MockMVMContext : public MVMContext {
   using AutoSizeFlag = nsViewportInfo::AutoSizeFlag;
   using AutoScaleFlag = nsViewportInfo::AutoScaleFlag;
   using ZoomFlag = nsViewportInfo::ZoomFlag;
 
@@ -35,51 +36,72 @@ class MockMVMContext : public MVMContext
                                  bool aOwnsWeak));
   MOCK_METHOD2(RemoveObserver,
                void(nsIObserver* aObserver, const char* aTopic));
   MOCK_METHOD0(Destroy, void());
 
   MOCK_METHOD1(SetVisualViewportSize, void(const CSSSize& aSize));
   MOCK_METHOD0(UpdateDisplayPortMargins, void());
 
+  void SetMVM(MobileViewportManager* aMVM) { mMVM = aMVM; }
+
   // MVMContext method implementations.
   nsViewportInfo GetViewportInfo(const ScreenIntSize& aDisplaySize) const {
-    return nsViewportInfo(mDefaultScale, mMinScale, mMaxScale,
-                          mDisplaySize / mDeviceScale, mAutoSizeFlag,
-                          mAutoScaleFlag, mZoomFlag);
+    // This is a very basic approximation of what Document::GetViewportInfo()
+    // does in the most common cases.
+    // Ideally, we would invoke the algorithm in Document::GetViewportInfo()
+    // itself, but that would require refactoring it a bit to remove
+    // dependencies on the actual Document which we don't have available in
+    // this test harness.
+    CSSSize viewportSize = mDisplaySize / mDeviceScale;
+    if (mAutoSizeFlag == AutoSizeFlag::FixedSize) {
+      viewportSize = CSSSize(mFixedViewportWidth,
+                             mFixedViewportWidth * (float(mDisplaySize.height) /
+                                                    mDisplaySize.width));
+    }
+    return nsViewportInfo(mDefaultScale, mMinScale, mMaxScale, viewportSize,
+                          mAutoSizeFlag, mAutoScaleFlag, mZoomFlag);
   }
   CSSToLayoutDeviceScale CSSToDevPixelScale() const { return mDeviceScale; }
   float GetResolution() const { return mResolution; }
   bool SubjectMatchesDocument(nsISupports* aSubject) const { return true; }
   Maybe<CSSRect> CalculateScrollableRectForRSF() const {
     return Some(CSSRect(CSSPoint(), mContentSize));
   }
   bool IsResolutionUpdatedByApz() const { return false; }
   LayoutDeviceMargin ScrollbarAreaToExcludeFromCompositionBounds() const {
     return LayoutDeviceMargin();
   }
   Maybe<LayoutDeviceIntSize> GetContentViewerSize() const {
     return Some(mDisplaySize);
   }
   bool AllowZoomingForDocument() const { return true; }
 
-  void SetResolutionAndScaleTo(float aResolution) { mResolution = aResolution; }
+  void SetResolutionAndScaleTo(float aResolution,
+                               ResolutionChangeOrigin aOrigin) {
+    mResolution = aResolution;
+    mMVM->ResolutionUpdated(aOrigin);
+  }
   void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize,
               ResizeEventFlag aResizeEventFlag) {
     mICBSize = aNewSize;
     mContentSize = mLayoutFunction(mICBSize);
   }
 
   // Allow test code to modify the input metrics.
   void SetMinScale(CSSToScreenScale aMinScale) { mMinScale = aMinScale; }
   void SetMaxScale(CSSToScreenScale aMaxScale) { mMaxScale = aMaxScale; }
   void SetInitialScale(CSSToScreenScale aInitialScale) {
     mDefaultScale = aInitialScale;
     mAutoScaleFlag = AutoScaleFlag::FixedScale;
   }
+  void SetFixedViewportWidth(CSSCoord aWidth) {
+    mFixedViewportWidth = aWidth;
+    mAutoSizeFlag = AutoSizeFlag::FixedSize;
+  }
   void SetDisplaySize(const LayoutDeviceIntSize& aNewDisplaySize) {
     mDisplaySize = aNewDisplaySize;
   }
   void SetLayoutFunction(const LayoutFunction& aLayoutFunction) {
     mLayoutFunction = aLayoutFunction;
   }
 
   // Allow test code to query the output metrics.
@@ -88,33 +110,38 @@ class MockMVMContext : public MVMContext
 
  private:
   // Input metrics, with some sensible defaults.
   LayoutDeviceIntSize mDisplaySize{300, 600};
   CSSToScreenScale mDefaultScale{1.0f};
   CSSToScreenScale mMinScale{0.25f};
   CSSToScreenScale mMaxScale{10.0f};
   CSSToLayoutDeviceScale mDeviceScale{1.0f};
+  CSSCoord mFixedViewportWidth;
   AutoSizeFlag mAutoSizeFlag = AutoSizeFlag::AutoSize;
   AutoScaleFlag mAutoScaleFlag = AutoScaleFlag::AutoScale;
   ZoomFlag mZoomFlag = ZoomFlag::AllowZoom;
   // As a default layout function, just set the content size to the ICB size.
   LayoutFunction mLayoutFunction = [](CSSSize aICBSize) { return aICBSize; };
 
   // Output metrics.
   float mResolution = 1.0f;
   CSSSize mICBSize;
   CSSSize mContentSize;
+
+  MobileViewportManager* mMVM = nullptr;
 };
 
 class MVMTester : public ::testing::Test {
  public:
   MVMTester()
       : mMVMContext(new MockMVMContext()),
-        mMVM(new MobileViewportManager(mMVMContext)) {}
+        mMVM(new MobileViewportManager(mMVMContext)) {
+    mMVMContext->SetMVM(mMVM.get());
+  }
 
   void Resize(const LayoutDeviceIntSize& aNewDisplaySize) {
     mMVMContext->SetDisplaySize(aNewDisplaySize);
     mMVM->RequestReflow(false);
   }
 
  protected:
   RefPtr<MockMVMContext> mMVMContext;
@@ -142,8 +169,47 @@ TEST_F(MVMTester, ZoomBoundsRespectedAft
 
   // Now rotate the screen, and check that the minimum and maximum
   // scales are still respected after the rotation.
   Resize(LayoutDeviceIntSize(300, 600));
   EXPECT_EQ(CSSSize(300, 600), mMVMContext->GetICBSize());
   EXPECT_EQ(CSSSize(300, 600), mMVMContext->GetContentSize());
   EXPECT_EQ(1.0f, mMVMContext->GetResolution());
 }
+
+TEST_F(MVMTester, LandscapeToPortraitRotation_Bug1523844) {
+  // Set up initial conditions.
+  mMVMContext->SetDisplaySize(LayoutDeviceIntSize(300, 600));
+  // Set a layout function that simulates a page with a fixed
+  // content size that's as wide as the screen in one orientation
+  // (and wider in the other).
+  mMVMContext->SetLayoutFunction(
+      [](CSSSize aICBSize) { return CSSSize(600, 1200); });
+
+  // Simulate a "DOMMetaAdded" event being fired before calling
+  // SetInitialViewport(). This matches what typically happens
+  // during real usage (the MVM receives the "DOMMetaAdded"
+  // before the "load", and it's the "load" that calls
+  // SetInitialViewport()), and is important to trigger this
+  // bug, because it causes the MVM to be stuck with an
+  // "mRestoreResolution" (prior to the fix).
+  mMVM->HandleDOMMetaAdded();
+
+  // Perform an initial viewport computation and reflow, and
+  // sanity-check the results.
+  mMVM->SetInitialViewport();
+  EXPECT_EQ(CSSSize(300, 600), mMVMContext->GetICBSize());
+  EXPECT_EQ(CSSSize(600, 1200), mMVMContext->GetContentSize());
+  EXPECT_EQ(0.5f, mMVMContext->GetResolution());
+
+  // Rotate to landscape.
+  Resize(LayoutDeviceIntSize(600, 300));
+  EXPECT_EQ(CSSSize(600, 300), mMVMContext->GetICBSize());
+  EXPECT_EQ(CSSSize(600, 1200), mMVMContext->GetContentSize());
+  EXPECT_EQ(1.0f, mMVMContext->GetResolution());
+
+  // Rotate back to portrait and check that we have returned
+  // to the portrait resolution.
+  Resize(LayoutDeviceIntSize(300, 600));
+  EXPECT_EQ(CSSSize(300, 600), mMVMContext->GetICBSize());
+  EXPECT_EQ(CSSSize(600, 1200), mMVMContext->GetContentSize());
+  EXPECT_EQ(0.5f, mMVMContext->GetResolution());
+}
--- a/js/src/builtin/intl/Collator.cpp
+++ b/js/src/builtin/intl/Collator.cpp
@@ -7,16 +7,17 @@
 /* Intl.Collator implementation. */
 
 #include "builtin/intl/Collator.h"
 
 #include "mozilla/Assertions.h"
 
 #include "jsapi.h"
 
+#include "builtin/Array.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
 #include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
@@ -93,19 +94,18 @@ static bool Collator(JSContext* cx, cons
   }
 
   Rooted<CollatorObject*> collator(
       cx, NewObjectWithGivenProto<CollatorObject>(cx, proto));
   if (!collator) {
     return false;
   }
 
-  collator->setReservedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
-  collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT,
-                            PrivateValue(nullptr));
+  collator->setFixedSlot(CollatorObject::INTERNALS_SLOT, NullValue());
+  collator->setCollator(nullptr);
 
   HandleValue locales = args.get(0);
   HandleValue options = args.get(1);
 
   // Step 6.
   if (!intl::InitializeObject(cx, collator, cx->names().InitializeCollator,
                               locales, options)) {
     return false;
@@ -126,19 +126,17 @@ bool js::intl_Collator(JSContext* cx, un
   MOZ_ASSERT(!args.isConstructing());
 
   return Collator(cx, args);
 }
 
 void js::CollatorObject::finalize(JSFreeOp* fop, JSObject* obj) {
   MOZ_ASSERT(fop->onMainThread());
 
-  const Value& slot =
-      obj->as<CollatorObject>().getReservedSlot(CollatorObject::UCOLLATOR_SLOT);
-  if (UCollator* coll = static_cast<UCollator*>(slot.toPrivate())) {
+  if (UCollator* coll = obj->as<CollatorObject>().getCollator()) {
     ucol_close(coll);
   }
 }
 
 JSObject* js::CreateCollatorPrototype(JSContext* cx, HandleObject Intl,
                                       Handle<GlobalObject*> global) {
   RootedFunction ctor(cx, GlobalObject::createConstructor(
                               cx, &Collator, cx->names().Collator, 0));
@@ -213,25 +211,22 @@ bool js::intl_availableCollations(JSCont
     return false;
   }
 
   RootedObject collations(cx, NewDenseEmptyArray(cx));
   if (!collations) {
     return false;
   }
 
-  uint32_t index = 0;
-
   // The first element of the collations array must be |null| per
   // ES2017 Intl, 10.2.3 Internal Slots.
-  if (!DefineDataElement(cx, collations, index++, NullHandleValue)) {
+  if (!NewbornArrayPush(cx, collations, NullValue())) {
     return false;
   }
 
-  RootedValue element(cx);
   for (uint32_t i = 0; i < count; i++) {
     const char* collation = uenum_next(values, nullptr, &status);
     if (U_FAILURE(status)) {
       ReportInternalError(cx);
       return false;
     }
 
     // Per ECMA-402, 10.2.3, we don't include standard and search:
@@ -249,18 +244,17 @@ bool js::intl_availableCollations(JSCont
       ReportInternalError(cx);
       return false;
     }
 
     JSString* jscollation = NewStringCopyZ<CanGC>(cx, collation);
     if (!jscollation) {
       return false;
     }
-    element = StringValue(jscollation);
-    if (!DefineDataElement(cx, collations, index++, element)) {
+    if (!NewbornArrayPush(cx, collations, StringValue(jscollation))) {
       return false;
     }
   }
 
   args.rval().setObject(*collations);
   return true;
 }
 
@@ -476,26 +470,23 @@ bool js::intl_CompareStrings(JSContext* 
   MOZ_ASSERT(args[0].isObject());
   MOZ_ASSERT(args[1].isString());
   MOZ_ASSERT(args[2].isString());
 
   Rooted<CollatorObject*> collator(cx,
                                    &args[0].toObject().as<CollatorObject>());
 
   // Obtain a cached UCollator object.
-  void* priv =
-      collator->getReservedSlot(CollatorObject::UCOLLATOR_SLOT).toPrivate();
-  UCollator* coll = static_cast<UCollator*>(priv);
+  UCollator* coll = collator->getCollator();
   if (!coll) {
     coll = NewUCollator(cx, collator);
     if (!coll) {
       return false;
     }
-    collator->setReservedSlot(CollatorObject::UCOLLATOR_SLOT,
-                              PrivateValue(coll));
+    collator->setCollator(coll);
   }
 
   // Use the UCollator to actually compare the strings.
   RootedString str1(cx, args[1].toString());
   RootedString str2(cx, args[2].toString());
   return intl_CompareStrings(cx, coll, str1, str2, args.rval());
 }
 
--- a/js/src/builtin/intl/Collator.h
+++ b/js/src/builtin/intl/Collator.h
@@ -10,16 +10,18 @@
 #include "mozilla/Attributes.h"
 
 #include <stdint.h>
 
 #include "builtin/SelfHostingDefines.h"
 #include "js/Class.h"
 #include "vm/NativeObject.h"
 
+struct UCollator;
+
 namespace js {
 
 class GlobalObject;
 
 /******************** Collator ********************/
 
 class CollatorObject : public NativeObject {
  public:
@@ -28,16 +30,28 @@ class CollatorObject : public NativeObje
   static constexpr uint32_t INTERNALS_SLOT = 0;
   static constexpr uint32_t UCOLLATOR_SLOT = 1;
   static constexpr uint32_t SLOT_COUNT = 2;
 
   static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
                 "INTERNALS_SLOT must match self-hosting define for internals "
                 "object slot");
 
+  UCollator* getCollator() const {
+    const auto& slot = getFixedSlot(UCOLLATOR_SLOT);
+    if (slot.isUndefined()) {
+      return nullptr;
+    }
+    return static_cast<UCollator*>(slot.toPrivate());
+  }
+
+  void setCollator(UCollator* collator) {
+    setFixedSlot(UCOLLATOR_SLOT, PrivateValue(collator));
+  }
+
  private:
   static const ClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 extern JSObject* CreateCollatorPrototype(JSContext* cx,
                                          JS::Handle<JSObject*> Intl,
--- a/js/src/builtin/intl/DateTimeFormat.cpp
+++ b/js/src/builtin/intl/DateTimeFormat.cpp
@@ -8,16 +8,17 @@
 
 #include "builtin/intl/DateTimeFormat.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Range.h"
 
 #include "jsfriendapi.h"
 
+#include "builtin/Array.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "builtin/intl/SharedIntlData.h"
 #include "builtin/intl/TimeZoneDataGenerated.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
 #include "js/Date.h"
@@ -105,20 +106,19 @@ static bool DateTimeFormat(JSContext* cx
   }
 
   Rooted<DateTimeFormatObject*> dateTimeFormat(cx);
   dateTimeFormat = NewObjectWithGivenProto<DateTimeFormatObject>(cx, proto);
   if (!dateTimeFormat) {
     return false;
   }
 
-  dateTimeFormat->setReservedSlot(DateTimeFormatObject::INTERNALS_SLOT,
-                                  NullValue());
-  dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
-                                  PrivateValue(nullptr));
+  dateTimeFormat->setFixedSlot(DateTimeFormatObject::INTERNALS_SLOT,
+                               NullValue());
+  dateTimeFormat->setDateFormat(nullptr);
 
   RootedValue thisValue(
       cx, construct ? ObjectValue(*dateTimeFormat) : args.thisv());
   HandleValue locales = args.get(0);
   HandleValue options = args.get(1);
 
   // Step 3.
   return intl::LegacyInitializeObject(
@@ -154,19 +154,17 @@ bool js::intl_DateTimeFormat(JSContext* 
   // cannot be used with "new", but it still has to be treated as a
   // constructor.
   return DateTimeFormat(cx, args, true, DateTimeFormatOptions::Standard);
 }
 
 void js::DateTimeFormatObject::finalize(JSFreeOp* fop, JSObject* obj) {
   MOZ_ASSERT(fop->onMainThread());
 
-  const Value& slot = obj->as<DateTimeFormatObject>().getReservedSlot(
-      DateTimeFormatObject::UDATE_FORMAT_SLOT);
-  if (UDateFormat* df = static_cast<UDateFormat*>(slot.toPrivate())) {
+  if (UDateFormat* df = obj->as<DateTimeFormatObject>().getDateFormat()) {
     udat_close(df);
   }
 }
 
 JSObject* js::CreateDateTimeFormatPrototype(
     JSContext* cx, JS::Handle<JSObject*> Intl, JS::Handle<GlobalObject*> global,
     JS::MutableHandle<JSObject*> constructor,
     DateTimeFormatOptions dtfOptions) {
@@ -282,25 +280,24 @@ bool js::intl_availableCalendars(JSConte
   if (!locale) {
     return false;
   }
 
   RootedObject calendars(cx, NewDenseEmptyArray(cx));
   if (!calendars) {
     return false;
   }
-  uint32_t index = 0;
 
   // We need the default calendar for the locale as the first result.
-  RootedValue element(cx);
-  if (!DefaultCalendar(cx, locale, &element)) {
+  RootedValue defaultCalendar(cx);
+  if (!DefaultCalendar(cx, locale, &defaultCalendar)) {
     return false;
   }
 
-  if (!DefineDataElement(cx, calendars, index++, element)) {
+  if (!NewbornArrayPush(cx, calendars, defaultCalendar)) {
     return false;
   }
 
   // Now get the calendars that "would make a difference", i.e., not the
   // default.
   UErrorCode status = U_ZERO_ERROR;
   UEnumeration* values =
       ucal_getKeywordValuesForLocale("ca", locale.get(), false, &status);
@@ -329,30 +326,28 @@ bool js::intl_availableCalendars(JSConte
       intl::ReportInternalError(cx);
       return false;
     }
 
     JSString* jscalendar = NewStringCopyZ<CanGC>(cx, calendar);
     if (!jscalendar) {
       return false;
     }
-    element = StringValue(jscalendar);
-    if (!DefineDataElement(cx, calendars, index++, element)) {
+    if (!NewbornArrayPush(cx, calendars, StringValue(jscalendar))) {
       return false;
     }
 
     // ICU doesn't return calendar aliases, append them here.
     for (const auto& calendarAlias : calendarAliases) {
       if (StringsAreEqual(calendar, calendarAlias.calendar)) {
         JSString* jscalendar = NewStringCopyZ<CanGC>(cx, calendarAlias.alias);
         if (!jscalendar) {
           return false;
         }
-        element = StringValue(jscalendar);
-        if (!DefineDataElement(cx, calendars, index++, element)) {
+        if (!NewbornArrayPush(cx, calendars, StringValue(jscalendar))) {
           return false;
         }
       }
     }
   }
 
   args.rval().setObject(*calendars);
   return true;
@@ -852,50 +847,46 @@ static bool intl_FormatToPartsDateTime(J
   if (overallResult->length() == 0) {
     // An empty string contains no parts, so avoid extra work below.
     result.setObject(*partsArray);
     return true;
   }
 
   size_t lastEndIndex = 0;
 
-  uint32_t partIndex = 0;
   RootedObject singlePart(cx);
-  RootedValue partType(cx);
   RootedValue val(cx);
 
   auto AppendPart = [&](FieldType type, size_t beginIndex, size_t endIndex) {
     singlePart = NewBuiltinClassInstance<PlainObject>(cx);
     if (!singlePart) {
       return false;
     }
 
-    partType = StringValue(cx->names().*type);
-    if (!DefineDataProperty(cx, singlePart, cx->names().type, partType)) {
+    val = StringValue(cx->names().*type);
+    if (!DefineDataProperty(cx, singlePart, cx->names().type, val)) {
       return false;
     }
 
     JSLinearString* partSubstr = NewDependentString(
         cx, overallResult, beginIndex, endIndex - beginIndex);
     if (!partSubstr) {
       return false;
     }
 
     val = StringValue(partSubstr);
     if (!DefineDataProperty(cx, singlePart, cx->names().value, val)) {
       return false;
     }
 
-    val = ObjectValue(*singlePart);
-    if (!DefineDataElement(cx, partsArray, partIndex, val)) {
+    if (!NewbornArrayPush(cx, partsArray, ObjectValue(*singlePart))) {
       return false;
     }
 
     lastEndIndex = endIndex;
-    partIndex++;
     return true;
   };
 
   int32_t fieldInt, beginIndexInt, endIndexInt;
   while ((fieldInt = ufieldpositer_next(fpositer, &beginIndexInt,
                                         &endIndexInt)) >= 0) {
     MOZ_ASSERT(beginIndexInt >= 0);
     MOZ_ASSERT(endIndexInt >= 0);
@@ -954,25 +945,21 @@ bool js::intl_FormatDateTime(JSContext* 
   if (!x.isValid()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_DATE_NOT_FINITE, "DateTimeFormat",
                               formatToParts ? "formatToParts" : "format");
     return false;
   }
 
   // Obtain a cached UDateFormat object.
-  void* priv =
-      dateTimeFormat->getReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT)
-          .toPrivate();
-  UDateFormat* df = static_cast<UDateFormat*>(priv);
+  UDateFormat* df = dateTimeFormat->getDateFormat();
   if (!df) {
     df = NewUDateFormat(cx, dateTimeFormat);
     if (!df) {
       return false;
     }
-    dateTimeFormat->setReservedSlot(DateTimeFormatObject::UDATE_FORMAT_SLOT,
-                                    PrivateValue(df));
+    dateTimeFormat->setDateFormat(df);
   }
 
   // Use the UDateFormat to actually format the time stamp.
   return formatToParts ? intl_FormatToPartsDateTime(cx, df, x, args.rval())
                        : intl_FormatDateTime(cx, df, x, args.rval());
 }
--- a/js/src/builtin/intl/DateTimeFormat.h
+++ b/js/src/builtin/intl/DateTimeFormat.h
@@ -10,32 +10,46 @@
 #include "mozilla/Attributes.h"
 
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/SelfHostingDefines.h"
 #include "js/Class.h"
 #include "js/RootingAPI.h"
 #include "vm/NativeObject.h"
 
+using UDateFormat = void*;
+
 namespace js {
 
 class GlobalObject;
 
 class DateTimeFormatObject : public NativeObject {
  public:
   static const Class class_;
 
   static constexpr uint32_t INTERNALS_SLOT = 0;
   static constexpr uint32_t UDATE_FORMAT_SLOT = 1;
   static constexpr uint32_t SLOT_COUNT = 2;
 
   static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
                 "INTERNALS_SLOT must match self-hosting define for internals "
                 "object slot");
 
+  UDateFormat* getDateFormat() const {
+    const auto& slot = getFixedSlot(UDATE_FORMAT_SLOT);
+    if (slot.isUndefined()) {
+      return nullptr;
+    }
+    return static_cast<UDateFormat*>(slot.toPrivate());
+  }
+
+  void setDateFormat(UDateFormat* dateFormat) {
+    setFixedSlot(UDATE_FORMAT_SLOT, PrivateValue(dateFormat));
+  }
+
  private:
   static const ClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 extern JSObject* CreateDateTimeFormatPrototype(
     JSContext* cx, JS::Handle<JSObject*> Intl, JS::Handle<GlobalObject*> global,
--- a/js/src/builtin/intl/IntlObject.cpp
+++ b/js/src/builtin/intl/IntlObject.cpp
@@ -9,16 +9,17 @@
 #include "builtin/intl/IntlObject.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Range.h"
 
 #include "jsapi.h"
 
+#include "builtin/Array.h"
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/RelativeTimeFormat.h"
 #include "builtin/intl/ScopedICUObject.h"
@@ -27,16 +28,17 @@
 #include "js/PropertySpec.h"
 #include "js/StableStringChars.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/StringType.h"
 
 #include "vm/JSObject-inl.h"
+#include "vm/NativeObject-inl.h"
 
 using namespace js;
 
 using mozilla::Range;
 using mozilla::RangedPtr;
 
 using JS::AutoStableStringChars;
 
@@ -401,20 +403,21 @@ bool js::intl_ComputeDisplayNames(JSCont
 
   // 3. Assert: keys is an Array.
   RootedArrayObject keys(cx, &args[2].toObject().as<ArrayObject>());
   if (!keys) {
     return false;
   }
 
   // 4. Let result be ArrayCreate(0).
-  RootedArrayObject result(cx, NewDenseUnallocatedArray(cx, keys->length()));
+  RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, keys->length()));
   if (!result) {
     return false;
   }
+  result->ensureDenseInitializedLength(cx, 0, keys->length());
 
   UErrorCode status = U_ZERO_ERROR;
 
   UDateFormat* fmt =
       udat_open(UDAT_DEFAULT, UDAT_DEFAULT, IcuLocale(locale.get()), nullptr, 0,
                 nullptr, 0, &status);
   if (U_FAILURE(status)) {
     intl::ReportInternalError(cx);
@@ -455,20 +458,17 @@ bool js::intl_ComputeDisplayNames(JSCont
                                        stablePatternChars.latin1Range())
             : ComputeSingleDisplayName(cx, fmt, dtpg, dnStyle,
                                        stablePatternChars.twoByteRange());
     if (!displayName) {
       return false;
     }
 
     // 5.b. Append the result string to result.
-    v.setString(displayName);
-    if (!DefineDataElement(cx, result, i, v)) {
-      return false;
-    }
+    result->setDenseElement(i, StringValue(displayName));
   }
 
   // 6. Return result.
   args.rval().setObject(*result);
   return true;
 }
 
 bool js::intl_GetLocaleInfo(JSContext* cx, unsigned argc, Value* vp) {
--- a/js/src/builtin/intl/NumberFormat.cpp
+++ b/js/src/builtin/intl/NumberFormat.cpp
@@ -17,16 +17,17 @@
 #include <algorithm>
 #include <cstring>
 #include <iterator>
 #include <stddef.h>
 #include <stdint.h>
 #include <string>
 #include <type_traits>
 
+#include "builtin/Array.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "ds/Sort.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
 #include "js/PropertySpec.h"
 #include "js/RootingAPI.h"
@@ -1337,17 +1338,16 @@ ArrayObject* NumberFormatFields::toArray
       *hasPart = true;
       lastEnd = part->end;
       return true;
     }
   };
 
   // Finally, generate the result array.
   size_t lastEndIndex = 0;
-  uint32_t partIndex = 0;
   RootedObject singlePart(cx);
   RootedValue propVal(cx);
 
   RootedArrayObject partsArray(cx, NewDenseEmptyArray(cx));
   if (!partsArray) {
     return nullptr;
   }
 
@@ -1391,23 +1391,21 @@ ArrayObject* NumberFormatFields::toArray
 
     if (unitType != nullptr && type != &JSAtomState::literal) {
       propVal.setString(cx->names().*unitType);
       if (!DefineDataProperty(cx, singlePart, cx->names().unit, propVal)) {
         return nullptr;
       }
     }
 
-    propVal.setObject(*singlePart);
-    if (!DefineDataElement(cx, partsArray, partIndex, propVal)) {
+    if (!NewbornArrayPush(cx, partsArray, ObjectValue(*singlePart))) {
       return nullptr;
     }
 
     lastEndIndex = endIndex;
-    partIndex++;
   } while (true);
 
   MOZ_ASSERT(lastEndIndex == overallResult->length(),
              "result array must partition the entire string");
 
   return partsArray;
 }
 
--- a/js/src/builtin/intl/PluralRules.cpp
+++ b/js/src/builtin/intl/PluralRules.cpp
@@ -6,16 +6,17 @@
 
 /* Implementation of the Intl.PluralRules proposal. */
 
 #include "builtin/intl/PluralRules.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 
+#include "builtin/Array.h"
 #include "builtin/intl/CommonFunctions.h"
 #include "builtin/intl/ICUStubs.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/ScopedICUObject.h"
 #include "gc/FreeOp.h"
 #include "js/CharacterEncoding.h"
 #include "js/PropertySpec.h"
 #include "vm/GlobalObject.h"
@@ -407,19 +408,16 @@ bool js::intl_GetPluralCategories(JSCont
   }
   ScopedICUObject<UEnumeration, uenum_close> closeEnum(ue);
 
   RootedObject res(cx, NewDenseEmptyArray(cx));
   if (!res) {
     return false;
   }
 
-  RootedValue element(cx);
-  uint32_t i = 0;
-
   do {
     int32_t catSize;
     const char* cat = uenum_next(ue, &catSize, &status);
     if (U_FAILURE(status)) {
       intl::ReportInternalError(cx);
       return false;
     }
 
@@ -428,17 +426,16 @@ bool js::intl_GetPluralCategories(JSCont
     }
 
     MOZ_ASSERT(catSize >= 0);
     JSString* str = NewStringCopyN<CanGC>(cx, cat, catSize);
     if (!str) {
       return false;
     }
 
-    element.setString(str);
-    if (!DefineDataElement(cx, res, i++, element)) {
+    if (!NewbornArrayPush(cx, res, StringValue(str))) {
       return false;
     }
   } while (true);
 
   args.rval().setObject(*res);
   return true;
 }
--- a/js/src/builtin/intl/RelativeTimeFormat.cpp
+++ b/js/src/builtin/intl/RelativeTimeFormat.cpp
@@ -101,21 +101,19 @@ static bool RelativeTimeFormat(JSContext
 
   Rooted<RelativeTimeFormatObject*> relativeTimeFormat(cx);
   relativeTimeFormat =
       NewObjectWithGivenProto<RelativeTimeFormatObject>(cx, proto);
   if (!relativeTimeFormat) {
     return false;
   }
 
-  relativeTimeFormat->setReservedSlot(RelativeTimeFormatObject::INTERNALS_SLOT,
-                                      NullValue());
-  relativeTimeFormat->setReservedSlot(
-      RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT,
-      PrivateValue(nullptr));
+  relativeTimeFormat->setFixedSlot(RelativeTimeFormatObject::INTERNALS_SLOT,
+                                   NullValue());
+  relativeTimeFormat->setRelativeDateTimeFormatter(nullptr);
 
   HandleValue locales = args.get(0);
   HandleValue options = args.get(1);
 
   // Step 3.
   if (!intl::InitializeObject(cx, relativeTimeFormat,
                               cx->names().InitializeRelativeTimeFormat, locales,
                               options)) {
@@ -124,22 +122,18 @@ static bool RelativeTimeFormat(JSContext
 
   args.rval().setObject(*relativeTimeFormat);
   return true;
 }
 
 void js::RelativeTimeFormatObject::finalize(JSFreeOp* fop, JSObject* obj) {
   MOZ_ASSERT(fop->onMainThread());
 
-  constexpr auto RT_FORMAT_SLOT =
-      RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT;
-  const Value& slot =
-      obj->as<RelativeTimeFormatObject>().getReservedSlot(RT_FORMAT_SLOT);
   if (URelativeDateTimeFormatter* rtf =
-          static_cast<URelativeDateTimeFormatter*>(slot.toPrivate())) {
+          obj->as<RelativeTimeFormatObject>().getRelativeDateTimeFormatter()) {
     ureldatefmt_close(rtf);
   }
 }
 
 JSObject* js::CreateRelativeTimeFormatPrototype(JSContext* cx,
                                                 HandleObject Intl,
                                                 Handle<GlobalObject*> global) {
   RootedFunction ctor(cx);
@@ -360,27 +354,24 @@ bool js::intl_FormatRelativeTime(JSConte
   if (!mozilla::IsFinite(t)) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_DATE_NOT_FINITE, "RelativeTimeFormat",
                               formatToParts ? "formatToParts" : "format");
     return false;
   }
 
   // Obtain a cached URelativeDateTimeFormatter object.
-  constexpr auto RT_FORMAT_SLOT =
-      RelativeTimeFormatObject::URELATIVE_TIME_FORMAT_SLOT;
-  void* priv = relativeTimeFormat->getReservedSlot(RT_FORMAT_SLOT).toPrivate();
   URelativeDateTimeFormatter* rtf =
-      static_cast<URelativeDateTimeFormatter*>(priv);
+      relativeTimeFormat->getRelativeDateTimeFormatter();
   if (!rtf) {
     rtf = NewURelativeDateTimeFormatter(cx, relativeTimeFormat);
     if (!rtf) {
       return false;
     }
-    relativeTimeFormat->setReservedSlot(RT_FORMAT_SLOT, PrivateValue(rtf));
+    relativeTimeFormat->setRelativeDateTimeFormatter(rtf);
   }
 
   URelativeDateTimeUnit relDateTimeUnit;
   {
     JSLinearString* unit = args[2].toString()->ensureLinear(cx);
     if (!unit) {
       return false;
     }
--- a/js/src/builtin/intl/RelativeTimeFormat.h
+++ b/js/src/builtin/intl/RelativeTimeFormat.h
@@ -10,30 +10,44 @@
 #include "mozilla/Attributes.h"
 
 #include <stdint.h>
 
 #include "builtin/SelfHostingDefines.h"
 #include "js/Class.h"
 #include "vm/NativeObject.h"
 
+struct URelativeDateTimeFormatter;
+
 namespace js {
 
 class RelativeTimeFormatObject : public NativeObject {
  public:
   static const Class class_;
 
   static constexpr uint32_t INTERNALS_SLOT = 0;
   static constexpr uint32_t URELATIVE_TIME_FORMAT_SLOT = 1;
   static constexpr uint32_t SLOT_COUNT = 2;
 
   static_assert(INTERNALS_SLOT == INTL_INTERNALS_OBJECT_SLOT,
                 "INTERNALS_SLOT must match self-hosting define for internals "
                 "object slot");
 
+  URelativeDateTimeFormatter* getRelativeDateTimeFormatter() const {
+    const auto& slot = getFixedSlot(URELATIVE_TIME_FORMAT_SLOT);
+    if (slot.isUndefined()) {
+      return nullptr;
+    }
+    return static_cast<URelativeDateTimeFormatter*>(slot.toPrivate());
+  }
+
+  void setRelativeDateTimeFormatter(URelativeDateTimeFormatter* rtf) {
+    setFixedSlot(URELATIVE_TIME_FORMAT_SLOT, PrivateValue(rtf));
+  }
+
  private:
   static const ClassOps classOps_;
 
   static void finalize(JSFreeOp* fop, JSObject* obj);
 };
 
 extern JSObject* CreateRelativeTimeFormatPrototype(
     JSContext* cx, JS::Handle<JSObject*> Intl,
--- a/js/src/gc/Cell.h
+++ b/js/src/gc/Cell.h
@@ -522,16 +522,21 @@ class CellWithLengthAndFlags : public Ba
   // To get back the data, values to safely re-initialize clobbered flags
   // must be provided.
   uintptr_t unsetTemporaryGCUnsafeData(uint32_t len, uint32_t flags) {
     uintptr_t data = flags_;
     setLengthAndFlags(len, flags);
     return data;
   }
 
+  // Returns the offset of flags_. JIT code should use offsetOfFlags below.
+  static constexpr size_t offsetOfRawFlagsField() {
+    return offsetof(CellWithLengthAndFlags, flags_);
+  }
+
   // Offsets for direct field from jit code. A number of places directly
   // access 32-bit length and flags fields so do endian trickery here.
 #if JS_BITS_PER_WORD == 32
   static constexpr size_t offsetOfFlags() {
     return offsetof(CellWithLengthAndFlags, flags_);
   }
   static constexpr size_t offsetOfLength() {
     return offsetof(CellWithLengthAndFlags, length_);
--- a/js/src/jit-test/lib/wasm-testharness.js
+++ b/js/src/jit-test/lib/wasm-testharness.js
@@ -46,18 +46,18 @@ function promise_test(func, description)
         maybeError = err;
     });
     drainJobQueue();
     if (maybeError)
         throw maybeError;
 }
 
 let assert_equals = assertEq;
-let assert_true = (x, errMsg) => { assertEq(x, true); }
-let assert_false = (x, errMsg) => { assertEq(x, false); }
+let assert_true = (x, errMsg) => { assertEq(x, true, errMsg || ''); }
+let assert_false = (x, errMsg) => { assertEq(x, false, errMsg || ''); }
 
 function assert_unreached(description) {
     throw new Error(`unreachable:\n${description}`);
 }
 
 function assert_not_equals(actual, not_expected, description) {
     let caught = false;
     try {
--- a/js/src/jit-test/tests/wasm/declared-segs.js
+++ b/js/src/jit-test/tests/wasm/declared-segs.js
@@ -1,9 +1,9 @@
-// |jit-test| skip-if: !wasmBulkMemSupported()
+// |jit-test| skip-if: !wasmBulkMemSupported() || !wasmReftypesEnabled()
 
 // Declared segments parse and validate
 wasmFullPass(`
 	(module
 		(func $f1)
 		(elem declared $f1)
 		(func $run)
 		(export "run" $run)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/gc/ref-func.js
@@ -0,0 +1,95 @@
+// |jit-test| skip-if: !wasmReftypesEnabled()
+
+// 'ref.func' parses, validates and returns a non-null value
+wasmFullPass(`
+	(module
+		(elem declared $run)
+		(func $run (result i32)
+			ref.func $run
+			ref.is_null
+		)
+		(export "run" $run)
+	)
+`, 0);
+
+// function returning reference to itself
+{
+	let {f1} = wasmEvalText(`
+		(module
+			(elem declared $f1)
+			(func $f1 (result funcref) ref.func $f1)
+			(export "f1" $f1)
+		)
+	`).exports;
+	assertEq(f1(), f1);
+}
+
+// function returning reference to a different function
+{
+	let {f1, f2} = wasmEvalText(`
+		(module
+			(elem declared $f1)
+			(func $f1)
+			(func $f2 (result funcref) ref.func $f1)
+			(export "f1" $f1)
+			(export "f2" $f2)
+		)
+	`).exports;
+	assertEq(f2(), f1);
+}
+
+// function returning reference to function in a different module
+{
+	let i1 = wasmEvalText(`
+		(module
+			(elem declared $f1)
+			(func $f1)
+			(export "f1" $f1)
+		)
+	`);
+	let i2 = wasmEvalText(`
+		(module
+			(import $f1 "" "f1" (func))
+			(elem declared $f1)
+			(func $f2 (result funcref) ref.func $f1)
+			(export "f1" $f1)
+			(export "f2" $f2)
+		)
+	`, {"": i1.exports});
+
+	let f1 = i1.exports.f1;
+	let f2 = i2.exports.f2;
+	assertEq(f2(), f1);
+}
+
+// function index must be valid
+assertErrorMessage(() => {
+	wasmEvalText(`
+		(module
+			(func (result funcref) ref.func 10)
+		)
+	`);
+}, WebAssembly.CompileError, /function index out of range/);
+
+function validFuncRefText(forwardDeclare) {
+	return wasmEvalText(`
+		(module
+			(table 1 funcref)
+			(func $test (result funcref) ref.func $referenced)
+			(func $referenced)
+			${forwardDeclare}
+		)
+	`);
+}
+
+// referenced function must be forward declared somehow
+assertErrorMessage(() => validFuncRefText(''), WebAssembly.CompileError, /function index is not in an element segment/);
+
+// referenced function can be forward declared via segments
+assertEq(validFuncRefText('(elem 0 (i32.const 0) $referenced)') instanceof WebAssembly.Instance, true);
+assertEq(validFuncRefText('(elem passive $referenced)') instanceof WebAssembly.Instance, true);
+assertEq(validFuncRefText('(elem declared $referenced)') instanceof WebAssembly.Instance, true);
+
+// referenced function cannot be forward declared via start section or export
+assertErrorMessage(() => validFuncRefText('(start $referenced)'), WebAssembly.CompileError, /function index is not in an element segment/);
+assertErrorMessage(() => validFuncRefText('(export "referenced" $referenced)'), WebAssembly.CompileError, /function index is not in an element segment/);
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -329,17 +329,17 @@ class JSString : public js::gc::CellWith
         "Inline Latin1 chars must fit in a JSString");
     static_assert(
         sizeof(JSString) == (offsetof(JSString, d.inlineStorageTwoByte) +
                              NUM_INLINE_CHARS_TWO_BYTE * sizeof(char16_t)),
         "Inline char16_t chars must fit in a JSString");
 
     /* Ensure js::shadow::String has the same layout. */
     using JS::shadow::String;
-    static_assert(JSString::offsetOfFlags() == offsetof(String, flags_),
+    static_assert(JSString::offsetOfRawFlagsField() == offsetof(String, flags_),
                   "shadow::String flags offset must match JSString");
 #if JS_BITS_PER_WORD == 32
     static_assert(JSString::offsetOfLength() == offsetof(String, length_),
                   "shadow::String length offset must match JSString");
 #endif
     static_assert(offsetof(JSString, d.s.u2.nonInlineCharsLatin1) ==
                       offsetof(String, nonInlineCharsLatin1),
                   "shadow::String nonInlineChars offset must match JSString");
--- a/js/src/wasm/WasmAST.h
+++ b/js/src/wasm/WasmAST.h
@@ -412,16 +412,17 @@ enum class AstExprKind {
   Load,
   MemFill,
   MemOrTableCopy,
   MemOrTableInit,
   MemoryGrow,
   MemorySize,
   Nop,
   Pop,
+  RefFunc,
   RefNull,
   Return,
   SetGlobal,
   SetLocal,
 #ifdef ENABLE_WASM_GC
   StructNew,
   StructGet,
   StructSet,
@@ -1571,16 +1572,27 @@ class AstExtraConversionOperator final :
   static const AstExprKind Kind = AstExprKind::ExtraConversionOperator;
   explicit AstExtraConversionOperator(MiscOp op, AstExpr* operand)
       : AstExpr(Kind, ExprType::Limit), op_(op), operand_(operand) {}
 
   MiscOp op() const { return op_; }
   AstExpr* operand() const { return operand_; }
 };
 
+class AstRefFunc final : public AstExpr {
+  AstRef func_;
+
+ public:
+  static const AstExprKind Kind = AstExprKind::RefFunc;
+  explicit AstRefFunc(AstRef func)
+      : AstExpr(Kind, ExprType::FuncRef), func_(func) {}
+
+  AstRef& func() { return func_; }
+};
+
 class AstRefNull final : public AstExpr {
  public:
   static const AstExprKind Kind = AstExprKind::RefNull;
   AstRefNull() : AstExpr(Kind, ExprType::Limit) {}
 };
 
 // This is an artificial AST node which can fill operand slots in an AST
 // constructed from parsing or decoding stack-machine code that doesn't have
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -6837,16 +6837,17 @@ class BaseCompiler final : public BaseCo
   void emitReinterpretI64AsF64();
   void emitRound(RoundingMode roundingMode, ValType operandType);
   MOZ_MUST_USE bool emitInstanceCall(uint32_t lineOrBytecode,
                                      const SymbolicAddressSignature& builtin,
                                      bool pushReturnedValue = true);
   MOZ_MUST_USE bool emitMemoryGrow();
   MOZ_MUST_USE bool emitMemorySize();
 
+  MOZ_MUST_USE bool emitRefFunc();
   MOZ_MUST_USE bool emitRefNull();
   void emitRefIsNull();
 
   MOZ_MUST_USE bool emitAtomicCmpXchg(ValType type, Scalar::Type viewType);
   MOZ_MUST_USE bool emitAtomicLoad(ValType type, Scalar::Type viewType);
   MOZ_MUST_USE bool emitAtomicRMW(ValType type, Scalar::Type viewType,
                                   AtomicOp op);
   MOZ_MUST_USE bool emitAtomicStore(ValType type, Scalar::Type viewType);
@@ -9853,16 +9854,30 @@ bool BaseCompiler::emitMemorySize() {
 
   if (deadCode_) {
     return true;
   }
 
   return emitInstanceCall(lineOrBytecode, SASigMemorySize);
 }
 
+bool BaseCompiler::emitRefFunc() {
+  uint32_t lineOrBytecode = readCallSiteLineOrBytecode();
+  uint32_t funcIndex;
+  if (!iter_.readRefFunc(&funcIndex)) {
+    return false;
+  }
+  if (deadCode_) {
+    return true;
+  }
+
+  pushI32(funcIndex);
+  return emitInstanceCall(lineOrBytecode, SASigFuncRef);
+}
+
 bool BaseCompiler::emitRefNull() {
   if (!iter_.readRefNull()) {
     return false;
   }
 
   if (deadCode_) {
     return true;
   }
@@ -11482,16 +11497,19 @@ bool BaseCompiler::emitBody() {
       case uint16_t(Op::RefEq):
         if (!env_.gcTypesEnabled()) {
           return iter_.unrecognizedOpcode(&op);
         }
         CHECK_NEXT(
             emitComparison(emitCompareRef, ValType::AnyRef, Assembler::Equal));
 #endif
 #ifdef ENABLE_WASM_REFTYPES
+      case uint16_t(Op::RefFunc):
+        CHECK_NEXT(emitRefFunc());
+        break;
       case uint16_t(Op::RefNull):
         CHECK_NEXT(emitRefNull());
         break;
       case uint16_t(Op::RefIsNull):
         CHECK_NEXT(
             emitConversion(emitRefIsNull, ValType::AnyRef, ValType::I32));
         break;
 #endif
--- a/js/src/wasm/WasmBuiltins.cpp
+++ b/js/src/wasm/WasmBuiltins.cpp
@@ -167,16 +167,18 @@ const SymbolicAddressSignature SASigTabl
     {_PTR, _I32, _I32, _I32, _I32, _I32, _END}};
 const SymbolicAddressSignature SASigTableSet = {SymbolicAddress::TableSet,
                                                 _VOID,
                                                 _FailOnNegI32,
                                                 4,
                                                 {_PTR, _I32, _RoN, _I32, _END}};
 const SymbolicAddressSignature SASigTableSize = {
     SymbolicAddress::TableSize, _I32, _Infallible, 2, {_PTR, _I32, _END}};
+const SymbolicAddressSignature SASigFuncRef = {
+    SymbolicAddress::FuncRef, _RoN, _FailOnInvalidRef, 2, {_PTR, _I32, _END}};
 const SymbolicAddressSignature SASigPostBarrier = {
     SymbolicAddress::PostBarrier, _VOID, _Infallible, 2, {_PTR, _PTR, _END}};
 const SymbolicAddressSignature SASigPostBarrierFiltering = {
     SymbolicAddress::PostBarrierFiltering,
     _VOID,
     _Infallible,
     2,
     {_PTR, _PTR, _END}};
@@ -831,16 +833,19 @@ void* wasm::AddressOf(SymbolicAddress im
       *abiType = Args_General4;
       return FuncCast(Instance::tableGrow, *abiType);
     case SymbolicAddress::TableSet:
       *abiType = Args_General4;
       return FuncCast(Instance::tableSet, *abiType);
     case SymbolicAddress::TableSize:
       *abiType = Args_General2;
       return FuncCast(Instance::tableSize, *abiType);
+    case SymbolicAddress::FuncRef:
+      *abiType = Args_General2;
+      return FuncCast(Instance::funcRef, *abiType);
     case SymbolicAddress::PostBarrier:
       *abiType = Args_General2;
       return FuncCast(Instance::postBarrier, *abiType);
     case SymbolicAddress::PostBarrierFiltering:
       *abiType = Args_General2;
       return FuncCast(Instance::postBarrierFiltering, *abiType);
     case SymbolicAddress::StructNew:
       *abiType = Args_General2;
@@ -952,16 +957,17 @@ bool wasm::NeedsBuiltinThunk(SymbolicAdd
     case SymbolicAddress::TableCopy:
     case SymbolicAddress::ElemDrop:
     case SymbolicAddress::TableFill:
     case SymbolicAddress::TableGet:
     case SymbolicAddress::TableGrow:
     case SymbolicAddress::TableInit:
     case SymbolicAddress::TableSet:
     case SymbolicAddress::TableSize:
+    case SymbolicAddress::FuncRef:
     case SymbolicAddress::PostBarrier:
     case SymbolicAddress::PostBarrierFiltering:
     case SymbolicAddress::StructNew:
     case SymbolicAddress::StructNarrow:
       return true;
     case SymbolicAddress::Limit:
       break;
   }
--- a/js/src/wasm/WasmBuiltins.h
+++ b/js/src/wasm/WasmBuiltins.h
@@ -59,16 +59,17 @@ extern const SymbolicAddressSignature SA
 extern const SymbolicAddressSignature SASigTableCopy;
 extern const SymbolicAddressSignature SASigElemDrop;
 extern const SymbolicAddressSignature SASigTableFill;
 extern const SymbolicAddressSignature SASigTableGet;
 extern const SymbolicAddressSignature SASigTableGrow;
 extern const SymbolicAddressSignature SASigTableInit;
 extern const SymbolicAddressSignature SASigTableSet;
 extern const SymbolicAddressSignature SASigTableSize;
+extern const SymbolicAddressSignature SASigFuncRef;
 extern const SymbolicAddressSignature SASigPostBarrier;
 extern const SymbolicAddressSignature SASigPostBarrierFiltering;
 extern const SymbolicAddressSignature SASigStructNew;
 extern const SymbolicAddressSignature SASigStructNarrow;
 
 // A SymbolicAddress that NeedsBuiltinThunk() will call through a thunk to the
 // C++ function. This will be true for all normal calls from normal wasm
 // function code. Only calls to C++ from other exits/thunks do not need a thunk.
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -1371,16 +1371,18 @@ static const char* ThunkedNativeToDescri
     case SymbolicAddress::TableGrow:
       return "call to native table.grow function";
     case SymbolicAddress::TableInit:
       return "call to native table.init function";
     case SymbolicAddress::TableSet:
       return "call to native table.set function";
     case SymbolicAddress::TableSize:
       return "call to native table.size function";
+    case SymbolicAddress::FuncRef:
+      return "call to native func.ref function";
     case SymbolicAddress::PostBarrier:
       return "call to native GC postbarrier (in wasm)";
     case SymbolicAddress::PostBarrierFiltering:
       return "call to native filtering GC postbarrier (in wasm)";
     case SymbolicAddress::StructNew:
       return "call to native struct.new (in wasm)";
     case SymbolicAddress::StructNarrow:
       return "call to native struct.narrow (in wasm)";
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -993,16 +993,43 @@ void Instance::initElems(uint32_t tableI
 
 /* static */ uint32_t Instance::tableSize(Instance* instance,
                                           uint32_t tableIndex) {
   MOZ_ASSERT(SASigTableSize.failureMode == FailureMode::Infallible);
   Table& table = *instance->tables()[tableIndex];
   return table.length();
 }
 
+/* static */ void* Instance::funcRef(Instance* instance, uint32_t funcIndex) {
+  MOZ_ASSERT(SASigFuncRef.failureMode == FailureMode::FailOnInvalidRef);
+  JSContext* cx = TlsContext.get();
+
+  Tier tier = instance->code().bestTier();
+  const MetadataTier& metadataTier = instance->metadata(tier);
+  const FuncImportVector& funcImports = metadataTier.funcImports;
+
+  // If this is an import, we need to recover the original wrapper function to
+  // maintain referential equality between a re-exported function and
+  // 'ref.func'. The imported function object is stable across tiers, which is
+  // what we want.
+  if (funcIndex < funcImports.length()) {
+    FuncImportTls& import = instance->funcImportTls(funcImports[funcIndex]);
+    return AnyRef::fromJSObject(import.fun).forCompiledCode();
+  }
+
+  RootedFunction fun(cx);
+  RootedWasmInstanceObject instanceObj(cx, instance->object());
+  if (WasmInstanceObject::getExportedFunction(cx, instanceObj, funcIndex,
+                                              &fun)) {
+    return AnyRef::fromJSObject(fun).forCompiledCode();
+  }
+
+  return AnyRef::invalid().forCompiledCode();
+}
+
 /* static */ void Instance::postBarrier(Instance* instance,
                                         gc::Cell** location) {
   MOZ_ASSERT(SASigPostBarrier.failureMode == FailureMode::Infallible);
   MOZ_ASSERT(location);
   TlsContext.get()->runtime()->gc.storeBuffer().putCell(
       reinterpret_cast<JSObject**>(location));
 }
 
--- a/js/src/wasm/WasmInstance.h
+++ b/js/src/wasm/WasmInstance.h
@@ -211,16 +211,17 @@ class Instance {
   static uint32_t tableGrow(Instance* instance, void* initValue, uint32_t delta,
                             uint32_t tableIndex);
   static int32_t tableSet(Instance* instance, uint32_t index, void* value,
                           uint32_t tableIndex);
   static uint32_t tableSize(Instance* instance, uint32_t tableIndex);
   static int32_t tableInit(Instance* instance, uint32_t dstOffset,
                            uint32_t srcOffset, uint32_t len, uint32_t segIndex,
                            uint32_t tableIndex);
+  static void* funcRef(Instance* instance, uint32_t funcIndex);
   static void postBarrier(Instance* instance, gc::Cell** location);
   static void postBarrierFiltering(Instance* instance, gc::Cell** location);
   static void* structNew(Instance* instance, uint32_t typeIndex);
   static void* structNarrow(Instance* instance, uint32_t mustUnboxAnyref,
                             uint32_t outputTypeIndex, void* maybeNullPtr);
 };
 
 typedef UniquePtr<Instance> UniqueInstance;
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -3307,16 +3307,57 @@ static bool EmitTableSize(FunctionCompil
   }
 
   f.iter().setResult(ret);
   return true;
 }
 #endif  // ENABLE_WASM_REFTYPES
 
 #ifdef ENABLE_WASM_REFTYPES
+static bool EmitRefFunc(FunctionCompiler& f) {
+  uint32_t funcIndex;
+  if (!f.iter().readRefFunc(&funcIndex)) {
+    return false;
+  }
+
+  if (f.inDeadCode()) {
+    return true;
+  }
+
+  uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode();
+
+  const SymbolicAddressSignature& callee = SASigFuncRef;
+  CallCompileState args;
+  if (!f.passInstance(callee.argTypes[0], &args)) {
+    return false;
+  }
+
+  MDefinition* funcIndexArg = f.constant(Int32Value(funcIndex), MIRType::Int32);
+  if (!funcIndexArg) {
+    return false;
+  }
+  if (!f.passArg(funcIndexArg, callee.argTypes[1], &args)) {
+    return false;
+  }
+
+  if (!f.finishCall(&args)) {
+    return false;
+  }
+
+  // The return value here is either null, denoting an error, or a short-lived
+  // pointer to a location containing a possibly-null ref.
+  MDefinition* ret;
+  if (!f.builtinInstanceMethodCall(callee, lineOrBytecode, args, &ret)) {
+    return false;
+  }
+
+  f.iter().setResult(ret);
+  return true;
+}
+
 static bool EmitRefNull(FunctionCompiler& f) {
   if (!f.iter().readRefNull()) {
     return false;
   }
 
   if (f.inDeadCode()) {
     return true;
   }
@@ -3783,16 +3824,18 @@ static bool EmitBodyExprs(FunctionCompil
       case uint16_t(Op::RefEq):
         if (!f.env().gcTypesEnabled()) {
           return f.iter().unrecognizedOpcode(&op);
         }
         CHECK(EmitComparison(f, ValType::AnyRef, JSOP_EQ,
                              MCompare::Compare_RefOrNull));
 #endif
 #ifdef ENABLE_WASM_REFTYPES
+      case uint16_t(Op::RefFunc):
+        CHECK(EmitRefFunc(f));
       case uint16_t(Op::RefNull):
         CHECK(EmitRefNull(f));
       case uint16_t(Op::RefIsNull):
         CHECK(EmitRefIsNull(f));
 #endif
 
       // Sign extensions
       case uint16_t(Op::I32Extend8S):
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -424,16 +424,17 @@ class MOZ_STACK_CLASS OpIter : private P
                                  Value* value);
   MOZ_MUST_USE bool readGetGlobal(uint32_t* id);
   MOZ_MUST_USE bool readSetGlobal(uint32_t* id, Value* value);
   MOZ_MUST_USE bool readTeeGlobal(uint32_t* id, Value* value);
   MOZ_MUST_USE bool readI32Const(int32_t* i32);
   MOZ_MUST_USE bool readI64Const(int64_t* i64);
   MOZ_MUST_USE bool readF32Const(float* f32);
   MOZ_MUST_USE bool readF64Const(double* f64);
+  MOZ_MUST_USE bool readRefFunc(uint32_t* funcTypeIndex);
   MOZ_MUST_USE bool readRefNull();
   MOZ_MUST_USE bool readCall(uint32_t* calleeIndex, ValueVector* argValues);
   MOZ_MUST_USE bool readCallIndirect(uint32_t* funcTypeIndex,
                                      uint32_t* tableIndex, Value* callee,
                                      ValueVector* argValues);
   MOZ_MUST_USE bool readOldCallDirect(uint32_t numFuncImports,
                                       uint32_t* funcIndex,
                                       ValueVector* argValues);
@@ -1468,16 +1469,32 @@ inline bool OpIter<Policy>::readF32Const
 template <typename Policy>
 inline bool OpIter<Policy>::readF64Const(double* f64) {
   MOZ_ASSERT(Classify(op_) == OpKind::F64);
 
   return readFixedF64(f64) && push(ValType::F64);
 }
 
 template <typename Policy>
+inline bool OpIter<Policy>::readRefFunc(uint32_t* funcTypeIndex) {
+  MOZ_ASSERT(Classify(op_) == OpKind::RefFunc);
+
+  if (!readVarU32(funcTypeIndex)) {
+    return fail("unable to read function index");
+  }
+  if (*funcTypeIndex >= env_.funcTypes.length()) {
+    return fail("function index out of range");
+  }
+  if (!env_.validForRefFunc.getBit(*funcTypeIndex)) {
+    return fail("function index is not in an element segment");
+  }
+  return push(StackType(ValType::FuncRef));
+}
+
+template <typename Policy>
 inline bool OpIter<Policy>::readRefNull() {
   MOZ_ASSERT(Classify(op_) == OpKind::RefNull);
 
   return push(StackType(ValType::NullRef));
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readValType(ValType* type) {
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -118,16 +118,17 @@ class WasmToken {
     StructNarrow,
 #endif
     Nop,
     Offset,
     OpenParen,
     Param,
     Passive,
     Ref,
+    RefFunc,
     RefNull,
     Result,
     Return,
     SetGlobal,
     SetLocal,
     Shared,
     SignedInteger,
     Start,
@@ -320,16 +321,17 @@ class WasmToken {
 #ifdef ENABLE_WASM_GC
       case StructNew:
       case StructGet:
       case StructSet:
       case StructNarrow:
 #endif
       case Nop:
       case RefNull:
+      case RefFunc:
       case Return:
       case SetGlobal:
       case SetLocal:
       case Store:
       case TableCopy:
       case TableInit:
 #ifdef ENABLE_WASM_REFTYPES
       case TableFill:
@@ -2180,16 +2182,19 @@ WasmToken WasmTokenStream::next() {
       }
       if (consume(u"return")) {
         return WasmToken(WasmToken::Return, begin, cur_);
       }
       if (consume(u"ref")) {
         if (consume(u".eq")) {
           return WasmToken(WasmToken::ComparisonOpcode, Op::RefEq, begin, cur_);
         }
+        if (consume(u".func")) {
+          return WasmToken(WasmToken::RefFunc, begin, cur_);
+        }
         if (consume(u".null")) {
           return WasmToken(WasmToken::RefNull, begin, cur_);
         }
         if (consume(u".is_null")) {
           return WasmToken(WasmToken::UnaryOpcode, Op::RefIsNull, begin, cur_);
         }
         return WasmToken(WasmToken::Ref, begin, cur_);
       }
@@ -3967,16 +3972,25 @@ static AstExpr* ParseStructNarrow(WasmPa
   if (!ptr) {
     return nullptr;
   }
 
   return new (c.lifo) AstStructNarrow(inputType, outputType, ptr);
 }
 #endif
 
+static AstExpr* ParseRefFunc(WasmParseContext& c) {
+  AstRef func;
+  if (!c.ts.matchRef(&func, c.error)) {
+    return nullptr;
+  }
+
+  return new (c.lifo) AstRefFunc(func);
+}
+
 static AstExpr* ParseRefNull(WasmParseContext& c) {
   return new (c.lifo) AstRefNull();
 }
 
 static AstExpr* ParseExprBody(WasmParseContext& c, WasmToken token,
                               bool inParens) {
   if (!CheckRecursionLimitDontReport(c.stackLimit)) {
     return nullptr;
@@ -4083,16 +4097,18 @@ static AstExpr* ParseExprBody(WasmParseC
       return ParseStructNew(c, inParens);
     case WasmToken::StructGet:
       return ParseStructGet(c, inParens);
     case WasmToken::StructSet:
       return ParseStructSet(c, inParens);
     case WasmToken::StructNarrow:
       return ParseStructNarrow(c, inParens);
 #endif
+    case WasmToken::RefFunc:
+      return ParseRefFunc(c);
     case WasmToken::RefNull:
       return ParseRefNull(c);
     default:
       c.ts.generateError(token, c.error);
       return nullptr;
   }
 }
 
@@ -5770,25 +5786,31 @@ static bool ResolveStructNarrow(Resolver
   if (!ResolveType(r, s.outputStruct())) {
     return false;
   }
 
   return ResolveExpr(r, s.ptr());
 }
 #endif
 
+static bool ResolveRefFunc(Resolver& r, AstRefFunc& s) {
+  return r.resolveFunction(s.func());
+}
+
 static bool ResolveRefNull(Resolver& r, AstRefNull& s) { return true; }
 
 static bool ResolveExpr(Resolver& r, AstExpr& expr) {
   switch (expr.kind()) {
     case AstExprKind::Nop:
     case AstExprKind::Pop:
     case AstExprKind::Unreachable:
     case AstExprKind::MemorySize:
       return true;
+    case AstExprKind::RefFunc:
+      return ResolveRefFunc(r, expr.as<AstRefFunc>());
     case AstExprKind::RefNull:
       return ResolveRefNull(r, expr.as<AstRefNull>());
     case AstExprKind::Drop:
       return ResolveDropOperator(r, expr.as<AstDrop>());
     case AstExprKind::BinaryOperator:
       return ResolveBinaryOperator(r, expr.as<AstBinaryOperator>());
     case AstExprKind::Block:
       return ResolveBlock(r, expr.as<AstBlock>());
@@ -6574,28 +6596,34 @@ static bool EncodeStructNarrow(Encoder& 
   }
   if (!e.writeValType(s.outputStruct().type())) {
     return false;
   }
   return true;
 }
 #endif
 
+static bool EncodeRefFunc(Encoder& e, AstRefFunc& s) {
+  return e.writeOp(Op::RefFunc) && e.writeVarU32(s.func().index());
+}
+
 static bool EncodeRefNull(Encoder& e, AstRefNull& s) {
   return e.writeOp(Op::RefNull);
 }
 
 static bool EncodeExpr(Encoder& e, AstExpr& expr) {
   switch (expr.kind()) {
     case AstExprKind::Pop:
       return true;
     case AstExprKind::Nop:
       return e.writeOp(Op::Nop);
     case AstExprKind::Unreachable:
       return e.writeOp(Op::Unreachable);
+    case AstExprKind::RefFunc:
+      return EncodeRefFunc(e, expr.as<AstRefFunc>());
     case AstExprKind::RefNull:
       return EncodeRefNull(e, expr.as<AstRefNull>());
     case AstExprKind::BinaryOperator:
       return EncodeBinaryOperator(e, expr.as<AstBinaryOperator>());
     case AstExprKind::Block:
       return EncodeBlock(e, expr.as<AstBlock>());
     case AstExprKind::Branch:
       return EncodeBranch(e, expr.as<AstBranch>());
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1894,16 +1894,17 @@ enum class SymbolicAddress {
   TableCopy,
   ElemDrop,
   TableFill,
   TableGet,
   TableGrow,
   TableInit,
   TableSet,
   TableSize,
+  FuncRef,
   PostBarrier,
   PostBarrierFiltering,
   StructNew,
   StructNarrow,
 #if defined(JS_CODEGEN_MIPS32)
   js_jit_gAtomic64Lock,
 #endif
 #ifdef WASM_CODEGEN_DEBUG
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -982,16 +982,21 @@ static bool DecodeFunctionBodyExprs(cons
         if (!env.gcTypesEnabled()) {
           return iter.unrecognizedOpcode(&op);
         }
         CHECK(iter.readComparison(ValType::AnyRef, &nothing, &nothing));
         break;
       }
 #endif
 #ifdef ENABLE_WASM_REFTYPES
+      case uint16_t(Op::RefFunc): {
+        uint32_t unusedIndex;
+        CHECK(iter.readRefFunc(&unusedIndex));
+        break;
+      }
       case uint16_t(Op::RefNull): {
         if (!env.refTypesEnabled()) {
           return iter.unrecognizedOpcode(&op);
         }
         CHECK(iter.readRefNull());
         break;
       }
       case uint16_t(Op::RefIsNull): {
@@ -2465,16 +2470,19 @@ static bool DecodeElemSection(Decoder& d
       if (payload == ElemSegmentPayload::ElemExpression) {
         OpBytes end;
         if (!d.readOp(&end) || end.b0 != uint16_t(Op::End)) {
           return d.fail("failed to read end of initializer expression");
         }
       }
 
       seg->elemFuncIndices.infallibleAppend(funcIndex);
+      if (funcIndex != NullFuncIndex) {
+        env->validForRefFunc.setBit(funcIndex);
+      }
     }
 
     env->elemSegments.infallibleAppend(std::move(seg));
   }
 
   return d.finishSection(*range, "elem");
 }
 
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -16,16 +16,18 @@
  * limitations under the License.
  */
 
 #ifndef wasm_validate_h
 #define wasm_validate_h
 
 #include "mozilla/TypeTraits.h"
 
+#include "ds/Bitmap.h"
+
 #include "wasm/WasmTypes.h"
 
 namespace js {
 namespace wasm {
 
 // This struct captures the bytecode offset of a section's payload (so not
 // including the header) and the size of the payload.
 
@@ -159,16 +161,17 @@ struct ModuleEnvironment {
   GlobalDescVector globals;
   TableDescVector tables;
   Uint32Vector asmJSSigToTableIndex;
   ImportVector imports;
   ExportVector exports;
   Maybe<uint32_t> startFuncIndex;
   ElemSegmentVector elemSegments;
   MaybeSectionRange codeSection;
+  SparseBitmap validForRefFunc;
 
   // Fields decoded as part of the wasm module tail:
   DataSegmentEnvVector dataSegments;
   CustomSectionEnvVector customSections;
   Maybe<uint32_t> nameCustomSectionIndex;
   Maybe<Name> moduleName;
   NameVector funcNames;
 
--- a/js/src/wasm/cranelift/Cargo.toml
+++ b/js/src/wasm/cranelift/Cargo.toml
@@ -11,18 +11,18 @@ name = "baldrdash"
 [dependencies]
 # The build system redirects the versions of cranelift-codegen and
 # cranelift-wasm to pinned commits. If you want to update Cranelift in Gecko,
 # you should update the following files:
 # - $TOP_LEVEL/Cargo.toml, look for the revision (rev) hashes of both cranelift
 # dependencies (codegen and wasm).
 # - $TOP_LEVEL/.cargo/config.in, look for the revision (rev) field of the
 # Cranelift source.
-cranelift-codegen = { version = "0.37", default-features = false }
-cranelift-wasm = "0.37"
+cranelift-codegen = { version = "0.38", default-features = false }
+cranelift-wasm = "0.38"
 target-lexicon = "0.4.0"
 log = { version = "0.4.6", default-features = false, features = ["release_max_level_info"] }
 env_logger = "0.5.6"
 
 [build-dependencies]
 bindgen = {version = "0.51", default-features = false} # disable `logging` to reduce code size
 
 [features]
--- a/js/src/wasm/cranelift/src/wasm2clif.rs
+++ b/js/src/wasm/cranelift/src/wasm2clif.rs
@@ -433,17 +433,17 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra
             ty: mem_ty,
             offset,
         })
     }
 
     fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> WasmResult<ir::Heap> {
         // Currently, Baldrdash doesn't support multiple memories.
         if index.index() != 0 {
-            return Err(WasmError::Unsupported("only one wasm memory supported"));
+            return Err(WasmError::Unsupported("only one wasm memory supported".to_string()));
         }
 
         // Get the address of the `TlsData::memoryBase` field.
         let base_addr = self.get_vmctx_gv(func);
         // Get the `TlsData::memoryBase` field. We assume this is never modified during execution
         // of the function.
         let base = func.create_global_value(ir::GlobalValueData::Load {
             base: base_addr,
@@ -553,17 +553,17 @@ impl<'a, 'b, 'c> FuncEnvironment for Tra
         sig_ref: ir::SigRef,
         callee: ir::Value,
         call_args: &[ir::Value],
     ) -> WasmResult<ir::Inst> {
         let wsig = self.env.signature(sig_index);
 
         // Currently, Baldrdash doesn't support multiple tables.
         if table_index.index() != 0 {
-            return Err(WasmError::Unsupported("only one wasm table supported"));
+            return Err(WasmError::Unsupported("only one wasm table supported".to_string()));
         }
         let wtable = self.get_table(pos.func, table_index);
 
         // Follows `MacroAssembler::wasmCallIndirect`:
 
         // 1. Materialize the signature ID.
         let sigid_value = match wsig.id_kind() {
             bindings::FuncTypeIdDescKind::None => None,
--- a/layout/base/GeckoMVMContext.cpp
+++ b/layout/base/GeckoMVMContext.cpp
@@ -119,20 +119,20 @@ Maybe<LayoutDeviceIntSize> GeckoMVMConte
   return Nothing();
 }
 
 bool GeckoMVMContext::AllowZoomingForDocument() const {
   MOZ_ASSERT(mDocument);
   return nsLayoutUtils::AllowZoomingForDocument(mDocument);
 }
 
-void GeckoMVMContext::SetResolutionAndScaleTo(float aResolution) {
+void GeckoMVMContext::SetResolutionAndScaleTo(float aResolution,
+                                              ResolutionChangeOrigin aOrigin) {
   MOZ_ASSERT(mPresShell);
-  mPresShell->SetResolutionAndScaleTo(aResolution,
-                                      ResolutionChangeOrigin::MainThread);
+  mPresShell->SetResolutionAndScaleTo(aResolution, aOrigin);
 }
 
 void GeckoMVMContext::SetVisualViewportSize(const CSSSize& aSize) {
   MOZ_ASSERT(mPresShell);
   nsLayoutUtils::SetVisualViewportSize(mPresShell, aSize);
 }
 
 void GeckoMVMContext::UpdateDisplayPortMargins() {
--- a/layout/base/GeckoMVMContext.h
+++ b/layout/base/GeckoMVMContext.h
@@ -43,17 +43,18 @@ class GeckoMVMContext : public MVMContex
   bool SubjectMatchesDocument(nsISupports* aSubject) const override;
   Maybe<CSSRect> CalculateScrollableRectForRSF() const override;
   bool IsResolutionUpdatedByApz() const override;
   LayoutDeviceMargin ScrollbarAreaToExcludeFromCompositionBounds()
       const override;
   Maybe<LayoutDeviceIntSize> GetContentViewerSize() const override;
   bool AllowZoomingForDocument() const override;
 
-  void SetResolutionAndScaleTo(float aResolution) override;
+  void SetResolutionAndScaleTo(float aResolution,
+                               ResolutionChangeOrigin aOrigin) override;
   void SetVisualViewportSize(const CSSSize& aSize) override;
   void UpdateDisplayPortMargins() override;
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize,
               ResizeEventFlag aResizeEventFlag) override;
 
  private:
   RefPtr<dom::Document> mDocument;
--- a/layout/base/MVMContext.h
+++ b/layout/base/MVMContext.h
@@ -5,16 +5,17 @@
 #ifndef MVMContext_h_
 #define MVMContext_h_
 
 #include "Units.h"
 #include "mozilla/Maybe.h"
 #include "nsISupportsImpl.h"
 #include "nsStringFwd.h"
 #include "nsViewportInfo.h"
+#include "PresShell.h"
 
 class nsIDOMEventListener;
 class nsIObserver;
 class nsISupports;
 
 namespace mozilla {
 
 /**
@@ -46,17 +47,18 @@ class MVMContext {
   virtual bool SubjectMatchesDocument(nsISupports* aSubject) const = 0;
   virtual Maybe<CSSRect> CalculateScrollableRectForRSF() const = 0;
   virtual bool IsResolutionUpdatedByApz() const = 0;
   virtual LayoutDeviceMargin ScrollbarAreaToExcludeFromCompositionBounds()
       const = 0;
   virtual Maybe<LayoutDeviceIntSize> GetContentViewerSize() const = 0;
   virtual bool AllowZoomingForDocument() const = 0;
 
-  virtual void SetResolutionAndScaleTo(float aResolution) = 0;
+  virtual void SetResolutionAndScaleTo(float aResolution,
+                                       ResolutionChangeOrigin aOrigin) = 0;
   virtual void SetVisualViewportSize(const CSSSize& aSize) = 0;
   virtual void UpdateDisplayPortMargins() = 0;
 
   enum class ResizeEventFlag {
     IfNecessary,  // resize events will be fired if necessary.
     Suppress,     // resize events will never be fired.
   };
   virtual void Reflow(const CSSSize& aNewSize, const CSSSize& aOldSize,
--- a/layout/base/MobileViewportManager.cpp
+++ b/layout/base/MobileViewportManager.cpp
@@ -100,55 +100,61 @@ mozilla::CSSToScreenScale MobileViewport
 }
 
 void MobileViewportManager::RequestReflow(bool aForceAdjustResolution) {
   MVM_LOG("%p: got a reflow request with force resolution: %d\n", this,
           aForceAdjustResolution);
   RefreshViewportSize(aForceAdjustResolution);
 }
 
-void MobileViewportManager::ResolutionUpdated() {
+void MobileViewportManager::ResolutionUpdated(
+    mozilla::ResolutionChangeOrigin aOrigin) {
   MVM_LOG("%p: resolution updated\n", this);
 
   if (!mContext) {
     return;
   }
 
-  if (!mPainted) {
+  if (!mPainted &&
+      aOrigin == mozilla::ResolutionChangeOrigin::MainThreadRestore) {
     // Save the value, so our default zoom calculation
     // can take it into account later on.
     SetRestoreResolution(mContext->GetResolution());
   }
   RefreshVisualViewportSize();
 }
 
 NS_IMETHODIMP
 MobileViewportManager::HandleEvent(dom::Event* event) {
   nsAutoString type;
   event->GetType(type);
 
   if (type.Equals(DOM_META_ADDED)) {
-    MVM_LOG("%p: got a dom-meta-added event\n", this);
-    RefreshViewportSize(mPainted);
+    HandleDOMMetaAdded();
   } else if (type.Equals(DOM_META_CHANGED)) {
     MVM_LOG("%p: got a dom-meta-changed event\n", this);
     RefreshViewportSize(mPainted);
   } else if (type.Equals(FULL_ZOOM_CHANGE)) {
     MVM_LOG("%p: got a full-zoom-change event\n", this);
     RefreshViewportSize(false);
   } else if (type.Equals(LOAD)) {
     MVM_LOG("%p: got a load event\n", this);
     if (!mPainted) {
       // Load event got fired before the before-first-paint message
       SetInitialViewport();
     }
   }
   return NS_OK;
 }
 
+void MobileViewportManager::HandleDOMMetaAdded() {
+  MVM_LOG("%p: got a dom-meta-added event\n", this);
+  RefreshViewportSize(mPainted);
+}
+
 NS_IMETHODIMP
 MobileViewportManager::Observe(nsISupports* aSubject, const char* aTopic,
                                const char16_t* aData) {
   if (!mContext) {
     return NS_OK;
   }
 
   if (mContext->SubjectMatchesDocument(aSubject) &&
@@ -405,24 +411,27 @@ void MobileViewportManager::UpdateResolu
 
       if (clampedZoom != zoom) {
         newZoom = Some(clampedZoom);
       }
     }
   }
 
   // If the zoom has changed, update the pres shell resolution accordingly.
+  // We characterize this as MainThreadAdjustment, because we don't want our
+  // change here to be remembered as a restore resolution.
   if (newZoom) {
     // Non-positive zoom factors can produce NaN or negative viewport sizes,
     // so we better be sure we've got a positive zoom factor.
     MOZ_ASSERT(*newZoom > CSSToScreenScale(0.0f),
                "zoom factor must be positive");
     LayoutDeviceToLayerScale resolution = ZoomToResolution(*newZoom, cssToDev);
     MVM_LOG("%p: setting resolution %f\n", this, resolution.scale);
-    mContext->SetResolutionAndScaleTo(resolution.scale);
+    mContext->SetResolutionAndScaleTo(
+        resolution.scale, ResolutionChangeOrigin::MainThreadAdjustment);
 
     MVM_LOG("%p: New zoom is %f\n", this, newZoom->scale);
   }
 
   // The visual viewport size depends on both the zoom and the display size,
   // and needs to be updated if either might have changed.
   if (newZoom || aType == UpdateType::ViewportSize) {
     UpdateVisualViewportSize(aDisplaySize, newZoom ? *newZoom : zoom);
--- a/layout/base/MobileViewportManager.h
+++ b/layout/base/MobileViewportManager.h
@@ -49,27 +49,32 @@ class MobileViewportManager final : publ
    * resolution at which they are the same size.)
    *
    * The returned resolution is suitable for passing to
    * PresShell::SetResolutionAndScaleTo(). It's not in typed units for
    * reasons explained at the declaration of FrameMetrics::mPresShellResolution.
    */
   float ComputeIntrinsicResolution() const;
 
+  /* The only direct calls to this should be in test code.
+   * Normally, it gets called by HandleEvent().
+   */
+  void HandleDOMMetaAdded();
+
  private:
   void SetRestoreResolution(float aResolution);
 
  public:
   /* Notify the MobileViewportManager that a reflow was requested in the
    * presShell.*/
   void RequestReflow(bool aForceAdjustResolution);
 
   /* Notify the MobileViewportManager that the resolution on the presShell was
    * updated, and the visual viewport size needs to be updated. */
-  void ResolutionUpdated();
+  void ResolutionUpdated(mozilla::ResolutionChangeOrigin aOrigin);
 
   /* Called to compute the initial viewport on page load or before-first-paint,
    * whichever happens first. Also called directly if we are created after the
    * presShell is initialized. */
   void SetInitialViewport();
 
   const mozilla::LayoutDeviceIntSize& DisplaySize() const {
     return mDisplaySize;
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -5180,17 +5180,17 @@ nsresult PresShell::SetResolutionAndScal
   if (aResolution == mResolution.valueOr(0.0)) {
     MOZ_ASSERT(mResolution.isSome());
     return NS_OK;
   }
   RenderingState state(this);
   state.mResolution = Some(aResolution);
   SetRenderingState(state);
   if (mMobileViewportManager) {
-    mMobileViewportManager->ResolutionUpdated();
+    mMobileViewportManager->ResolutionUpdated(aOrigin);
   }
   if (aOrigin == ResolutionChangeOrigin::Apz) {
     mResolutionUpdatedByApz = true;
   } else {
     mResolutionUpdated = true;
   }
 
   if (auto* window = nsGlobalWindowInner::Cast(mDocument->GetInnerWindow())) {
--- a/layout/base/PresShellForwards.h
+++ b/layout/base/PresShellForwards.h
@@ -170,17 +170,18 @@ enum class RenderImageFlags {
   IsImage = 1 << 0,
   AutoScale = 1 << 1,
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(RenderImageFlags)
 
 enum class ResolutionChangeOrigin : uint8_t {
   Apz,
-  MainThread,
+  MainThreadRestore,
+  MainThreadAdjustment,
 };
 
 // See comment at declaration of AddCanvasBackgroundColorItem() for the detail.
 enum class AddCanvasBackgroundColorFlags {
   None = 0,
   ForceDraw = 1 << 0,
   AddForSubDocument = 1 << 1,
   AppendUnscrolledOnly = 1 << 2,
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -6616,17 +6616,17 @@ void ScrollFrameHelper::RestoreState(Pre
   mDidHistoryRestore = true;
   mLastPos = mScrolledFrame ? GetLogicalVisualViewportOffset() : nsPoint(0, 0);
 
   // Resolution properties should only exist on root scroll frames.
   MOZ_ASSERT(mIsRoot || aState->resolution() == 1.0);
 
   if (mIsRoot) {
     mOuter->PresShell()->SetResolutionAndScaleTo(
-        aState->resolution(), ResolutionChangeOrigin::MainThread);
+        aState->resolution(), ResolutionChangeOrigin::MainThreadRestore);
   }
 }
 
 void ScrollFrameHelper::PostScrolledAreaEvent() {
   if (mScrolledAreaEvent.IsPending()) {
     return;
   }
   mScrolledAreaEvent = new ScrolledAreaEvent(this);
--- a/layout/reftests/mathml/mathbackground-4-ref.xml
+++ b/layout/reftests/mathml/mathbackground-4-ref.xml
@@ -1,7 +1,7 @@
-<!-- test mathbackground on a non-presentation element -->
+<!-- test mathbackground on the semantics element -->
 <math xmlns="http://www.w3.org/1998/Math/MathML">
   <semantics mathbackground="red">
     <mtext>□■□■□■□</mtext>
   </semantics>
 </math>
 
--- a/layout/reftests/mathml/mathbackground-4.xml
+++ b/layout/reftests/mathml/mathbackground-4.xml
@@ -1,7 +1,7 @@
-<!-- test mathbackground on a non-presentation element -->
+<!-- test mathbackground on the semantics element -->
 <math xmlns="http://www.w3.org/1998/Math/MathML">
   <semantics mathbackground="green">
     <mtext>□■□■□■□</mtext>
   </semantics>
 </math>
 
--- a/layout/reftests/mathml/mathcolor-4-ref.xml
+++ b/layout/reftests/mathml/mathcolor-4-ref.xml
@@ -1,7 +1,7 @@
-<!-- test mathcolor on a non-presentation element -->
+<!-- test mathcolor on the semantics element -->
 <math xmlns="http://www.w3.org/1998/Math/MathML">
   <semantics mathcolor="red">
     <mtext>□■□■□■□</mtext>
   </semantics>
 </math>
 
--- a/layout/reftests/mathml/mathcolor-4.xml
+++ b/layout/reftests/mathml/mathcolor-4.xml
@@ -1,7 +1,7 @@
-<!-- test mathcolor on a non-presentation element -->
+<!-- test mathcolor on the semantics element -->
 <math xmlns="http://www.w3.org/1998/Math/MathML">
   <semantics mathcolor="green">
     <mtext>□■□■□■□</mtext>
   </semantics>
 </math>
 
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -116,21 +116,21 @@ fails == stretchy-mover-2a.html stretchy
 == embellished-op-5-2.html embellished-op-5-ref.html
 fails-if(gtkWidget||Android) random-if(winWidget) == semantics-1.xhtml semantics-1-ref.xhtml # bug 1309429, bug 1328771
 == semantics-2.html semantics-2-ref.html
 == semantics-3.html semantics-3-ref.html
 == semantics-4.html semantics-4-ref.html
 != mathcolor-1.xml mathcolor-1-ref.xml
 != mathcolor-2.xml mathcolor-2-ref.xml
 != mathcolor-3.xml mathcolor-3-ref.xml
-== mathcolor-4.xml mathcolor-4-ref.xml
+!= mathcolor-4.xml mathcolor-4-ref.xml
 != mathbackground-1.xml mathbackground-1-ref.xml
 != mathbackground-2.xml mathbackground-2-ref.xml
 != mathbackground-3.xml mathbackground-3-ref.xml
-== mathbackground-4.xml mathbackground-4-ref.xml
+!= mathbackground-4.xml mathbackground-4-ref.xml
 == mstyle-1.xhtml mstyle-1-ref.xhtml
 == mstyle-2.xhtml mstyle-2-ref.xhtml
 fuzzy-if(OSX,0-16,0-8) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == mstyle-3.xhtml mstyle-3-ref.xhtml # Bug 1392106
 fuzzy-if(OSX,0-1,0-4) == mstyle-4.xhtml mstyle-4-ref.xhtml
 == mstyle-5.xhtml mstyle-5-ref.xhtml # Bug 787215
 == scale-stretchy-1.xhtml scale-stretchy-1-ref.xhtml
 != scale-stretchy-2.xhtml scale-stretchy-2-ref.xhtml
 fails-if(skiaContent&&OSX>=1010) == scale-stretchy-3.xhtml scale-stretchy-3-ref.xhtml
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -583,16 +583,28 @@
 
 # Is support for Navigator.sendBeacon enabled?
 - name: beacon.enabled
   type: bool
   value: true
   mirror: always
 
 #---------------------------------------------------------------------------
+# Prefs starting with "bidi."
+#---------------------------------------------------------------------------
+
+# Whether delete and backspace should immediately delete characters not
+# visually adjacent to the caret, or adjust the visual position of the caret
+# on the first keypress and delete the character on a second keypress
+- name: bidi.edit.delete_immediately
+  type: bool
+  value: true
+  mirror: always
+
+#---------------------------------------------------------------------------
 # Prefs starting with "browser."
 #---------------------------------------------------------------------------
 
 - name: browser.active_color
   type: String
   value: ""
   mirror: never
 - name: browser.anchor_color
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2865,20 +2865,16 @@ pref("bidi.texttype", 1);
 // 0 = nominalnumeralBidi *
 // 1 = regularcontextnumeralBidi
 // 2 = hindicontextnumeralBidi
 // 3 = arabicnumeralBidi
 // 4 = hindinumeralBidi
 // 5 = persiancontextnumeralBidi
 // 6 = persiannumeralBidi
 pref("bidi.numeral", 0);
-// Whether delete and backspace should immediately delete characters not
-// visually adjacent to the caret, or adjust the visual position of the caret
-// on the first keypress and delete the character on a second keypress
-pref("bidi.edit.delete_immediately", true);
 
 // Bidi caret movement style:
 // 0 = logical
 // 1 = visual
 // 2 = visual, but logical during selection
 pref("bidi.edit.caret_movement_style", 2);
 
 // Setting this pref to |true| forces Bidi UI menu items and keyboard shortcuts
--- a/modules/libpref/moz.build
+++ b/modules/libpref/moz.build
@@ -23,16 +23,17 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'pref'
 
 pref_groups = [
     'accessibility',
     'apz',
     'beacon',
+    'bidi',
     'browser',
     'canvas',
     'channelclassifier',
     'clipboard',
     'content',
     'device',
     'devtools',
     'dom',
--- a/mozglue/baseprofiler/public/BlocksRingBuffer.h
+++ b/mozglue/baseprofiler/public/BlocksRingBuffer.h
@@ -13,19 +13,23 @@
 
 #include "mozilla/Maybe.h"
 
 #include <functional>
 #include <utility>
 
 namespace mozilla {
 
-// Thread-safe Ring buffer that can store blocks of different sizes.
+// Thread-safe Ring buffer that can store blocks of different sizes during
+// defined sessions.
 // Each *block* contains an *entry* and the entry size:
 // [ entry_size | entry ] [ entry_size | entry ] ...
+// *In-session* is a period of time during which `BlocksRingBuffer` allows
+// reading and writing. *Out-of-session*, the `BlocksRingBuffer` object is
+// still valid, but contains no data, and gracefully denies accesses.
 //
 // To write an entry, the buffer reserves a block of sufficient size (to contain
 // user data of predetermined size), writes the entry size, and lets the caller
 // fill the entry contents using ModuloBuffer::Iterator APIs and a few entry-
 // specific APIs. E.g.:
 // ```
 // BlockRingsBuffer brb(PowerOfTwo<BlockRingsBuffer::Length>(1024));
 // brb.Put([&](BlocksRingBuffer::EntryReserver aER) {
@@ -121,72 +125,145 @@ class BlocksRingBuffer {
     // and `Index`.
     friend class BlocksRingBuffer;
     explicit BlockIndex(Index aBlockIndex) : mBlockIndex(aBlockIndex) {}
     explicit operator Index() const { return mBlockIndex; }
 
     Index mBlockIndex;
   };
 
+  // Default constructor starts out-of-session (nothing to read or write).
+  BlocksRingBuffer() = default;
+
   // Constructors with no entry destructor, the oldest entries will be silently
   // overwritten/destroyed.
 
   // Create a buffer of the given length.
-  explicit BlocksRingBuffer(PowerOfTwo<Length> aLength) : mBuffer(aLength) {}
+  explicit BlocksRingBuffer(PowerOfTwo<Length> aLength)
+      : mMaybeUnderlyingBuffer(Some(UnderlyingBuffer(aLength))) {}
 
   // Take ownership of an existing buffer.
   BlocksRingBuffer(UniquePtr<Buffer::Byte[]> aExistingBuffer,
                    PowerOfTwo<Length> aLength)
-      : mBuffer(std::move(aExistingBuffer), aLength) {}
+      : mMaybeUnderlyingBuffer(
+            Some(UnderlyingBuffer(std::move(aExistingBuffer), aLength))) {}
 
   // Use an externally-owned buffer.
   BlocksRingBuffer(Buffer::Byte* aExternalBuffer, PowerOfTwo<Length> aLength)
-      : mBuffer(aExternalBuffer, aLength) {}
+      : mMaybeUnderlyingBuffer(
+            Some(UnderlyingBuffer(aExternalBuffer, aLength))) {}
 
   // Constructors with an entry destructor, which will be called with an
   // `EntryReader` before the oldest entries get overwritten/destroyed.
   // Note that this entry destructor may be invoked from another caller's
   // function that writes/clears data, be aware of this re-entrancy! (Details
   // above class.)
 
   // Create a buffer of the given length.
   template <typename EntryDestructor>
   explicit BlocksRingBuffer(PowerOfTwo<Length> aLength,
                             EntryDestructor&& aEntryDestructor)
-      : mBuffer(aLength),
-        mEntryDestructor(std::forward<EntryDestructor>(aEntryDestructor)) {}
+      : mMaybeUnderlyingBuffer(Some(UnderlyingBuffer(
+            aLength, std::forward<EntryDestructor>(aEntryDestructor)))) {}
 
   // Take ownership of an existing buffer.
   template <typename EntryDestructor>
   explicit BlocksRingBuffer(UniquePtr<Buffer::Byte[]> aExistingBuffer,
                             PowerOfTwo<Length> aLength,
                             EntryDestructor&& aEntryDestructor)
-      : mBuffer(std::move(aExistingBuffer), aLength),
-        mEntryDestructor(std::forward<EntryDestructor>(aEntryDestructor)) {}
+      : mMaybeUnderlyingBuffer(Some(UnderlyingBuffer(
+            std::move(aExistingBuffer), aLength,
+            std::forward<EntryDestructor>(aEntryDestructor)))) {}
 
   // Use an externally-owned buffer.
   template <typename EntryDestructor>
   explicit BlocksRingBuffer(Buffer::Byte* aExternalBuffer,
                             PowerOfTwo<Length> aLength,
                             EntryDestructor&& aEntryDestructor)
-      : mBuffer(aExternalBuffer, aLength),
-        mEntryDestructor(std::forward<EntryDestructor>(aEntryDestructor)) {}
+      : mMaybeUnderlyingBuffer(Some(UnderlyingBuffer(
+            aExternalBuffer, aLength,
+            std::forward<EntryDestructor>(aEntryDestructor)))) {}
 
   // Destructor explictly destroys all remaining entries, this may invoke the
   // caller-provided entry destructor.
   ~BlocksRingBuffer() {
 #ifdef DEBUG
     // Needed because of lock DEBUG-check in `DestroyAllEntries()`.
     baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
 #endif  // DEBUG
     DestroyAllEntries();
   }
 
-  // Buffer length, constant. No need for locking.
-  PowerOfTwo<Length> BufferLength() const { return mBuffer.BufferLength(); }
+  // Remove underlying buffer, if any.
+  void Reset() {
+    baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    ResetUnderlyingBuffer();
+  }
+
+  // Create a buffer of the given length.
+  void Set(PowerOfTwo<Length> aLength) {
+    baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    ResetUnderlyingBuffer();
+    mMaybeUnderlyingBuffer.emplace(aLength);
+  }
+
+  // Take ownership of an existing buffer.
+  void Set(UniquePtr<Buffer::Byte[]> aExistingBuffer,
+           PowerOfTwo<Length> aLength) {
+    baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    ResetUnderlyingBuffer();
+    mMaybeUnderlyingBuffer.emplace(std::move(aExistingBuffer), aLength);
+  }
+
+  // Use an externally-owned buffer.
+  void Set(Buffer::Byte* aExternalBuffer, PowerOfTwo<Length> aLength) {
+    baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    ResetUnderlyingBuffer();
+    mMaybeUnderlyingBuffer.emplace(aExternalBuffer, aLength);
+  }
+
+  // Create a buffer of the given length, with entry destructor.
+  template <typename EntryDestructor>
+  void Set(PowerOfTwo<Length> aLength, EntryDestructor&& aEntryDestructor) {
+    baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    ResetUnderlyingBuffer();
+    mMaybeUnderlyingBuffer.emplace(
+        aLength, std::forward<EntryDestructor>(aEntryDestructor));
+  }
+
+  // Take ownership of an existing buffer, with entry destructor.
+  template <typename EntryDestructor>
+  void Set(UniquePtr<Buffer::Byte[]> aExistingBuffer,
+           PowerOfTwo<Length> aLength, EntryDestructor&& aEntryDestructor) {
+    baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    ResetUnderlyingBuffer();
+    mMaybeUnderlyingBuffer.emplace(
+        std::move(aExistingBuffer), aLength,
+        std::forward<EntryDestructor>(aEntryDestructor));
+  }
+
+  // Use an externally-owned buffer, with entry destructor.
+  template <typename EntryDestructor>
+  void Set(Buffer::Byte* aExternalBuffer, PowerOfTwo<Length> aLength,
+           EntryDestructor&& aEntryDestructor) {
+    baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    ResetUnderlyingBuffer();
+    mMaybeUnderlyingBuffer.emplace(
+        aExternalBuffer, aLength,
+        std::forward<EntryDestructor>(aEntryDestructor));
+  }
+
+  // Buffer length in bytes.
+  Maybe<PowerOfTwo<Length>> BufferLength() const {
+    baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    return mMaybeUnderlyingBuffer.map([](const UnderlyingBuffer& aBuffer) {
+      return aBuffer.mBuffer.BufferLength();
+    });
+    ;
+  }
 
   // Snapshot of the buffer state.
   struct State {
     // Index to the first block.
     BlockIndex mRangeStart;
 
     // Index past the last block. Equals mRangeStart if empty.
     BlockIndex mRangeEnd;
@@ -195,22 +272,27 @@ class BlocksRingBuffer {
     uint64_t mPushedBlockCount = 0;
 
     // Number of blocks that have been removed from this buffer.
     // Note: Live entries = pushed - cleared.
     uint64_t mClearedBlockCount = 0;
   };
 
   // Get a snapshot of the current state.
+  // When out-of-session, mFirstReadIndex==mNextWriteIndex, and
+  // mPushedBlockCount==mClearedBlockCount==0.
   // Note that these may change right after this thread-safe call, so they
   // should only be used for statistical purposes.
   State GetState() const {
     baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
-    return {mFirstReadIndex, mNextWriteIndex, mPushedBlockCount,
-            mClearedBlockCount};
+    return {
+        mFirstReadIndex, mNextWriteIndex,
+        mMaybeUnderlyingBuffer ? mMaybeUnderlyingBuffer->mPushedBlockCount : 0,
+        mMaybeUnderlyingBuffer ? mMaybeUnderlyingBuffer->mClearedBlockCount
+                               : 0};
   }
 
   // Iterator-like class used to read from an entry.
   // Created through `BlockIterator`, or a `GetEntryAt()` function, lives
   // within a lock guard lifetime.
   class EntryReader : public BufferReader {
    public:
     // Allow move-construction.
@@ -288,17 +370,18 @@ class BlocksRingBuffer {
       return Nothing();
     }
 
    private:
     // Only a BlocksRingBuffer can instantiate an EntryReader.
     friend class BlocksRingBuffer;
 
     explicit EntryReader(const BlocksRingBuffer& aRing, BlockIndex aBlockIndex)
-        : BufferReader(aRing.mBuffer.ReaderAt(Index(aBlockIndex))),
+        : BufferReader(aRing.mMaybeUnderlyingBuffer->mBuffer.ReaderAt(
+              Index(aBlockIndex))),
           mRing(aRing),
           mEntryBytes(BufferReader::ReadULEB128<Length>()),
           mEntryStart(CurrentIndex()) {
       // No EntryReader should live outside of a mutexed call.
       mRing.mMutex.AssertCurrentThreadOwns();
     }
 
     // Using a non-null pointer instead of a reference, to allow copying.
@@ -350,17 +433,18 @@ class BlocksRingBuffer {
     }
 
     // Can be used as reference to come back to this entry with `GetEntryAt()`.
     BlockIndex CurrentBlockIndex() const { return mBlockIndex; }
 
     // Index past the end of this block, which is the start of the next block.
     BlockIndex NextBlockIndex() const {
       MOZ_ASSERT(!IsAtEnd());
-      BufferReader reader = mRing->mBuffer.ReaderAt(Index(mBlockIndex));
+      BufferReader reader =
+          mRing->mMaybeUnderlyingBuffer->mBuffer.ReaderAt(Index(mBlockIndex));
       Length entrySize = reader.ReadULEB128<Length>();
       return BlockIndex(reader.CurrentIndex() + entrySize);
     }
 
     // Index of the first block in the whole buffer.
     BlockIndex BufferRangeStart() const { return mRing->mFirstReadIndex; }
 
     // Index past the last block in the whole buffer.
@@ -429,50 +513,58 @@ class BlocksRingBuffer {
     }
 
     // Using a non-null pointer instead of a reference, to allow copying.
     // This Reader should only live inside one of the thread-safe
     // BlocksRingBuffer functions, for this reference to stay valid.
     NotNull<const BlocksRingBuffer*> mRing;
   };
 
-  // Call `aCallback(BlocksRingBuffer::Reader)` with temporary Reader, and
-  // return whatever `aCallback` returns.
-  // Callback should not store `Reader`, as it may become invalid after this
-  // call.
+  // Call `aCallback(Maybe<BlocksRingBuffer::Reader>&&)`, and return whatever
+  // `aCallback` returns. `Maybe` may be `Nothing` when out-of-session.
+  // Callback should not store `Reader`, because it may become invalid after
+  // this call.
   template <typename Callback>
   auto Read(Callback&& aCallback) const {
     baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
-    return std::forward<Callback>(aCallback)(Reader(*this));
+    Maybe<Reader> maybeReader;
+    if (MOZ_LIKELY(mMaybeUnderlyingBuffer)) {
+      maybeReader.emplace(Reader(*this));
+    }
+    return std::forward<Callback>(aCallback)(std::move(maybeReader));
   }
 
   // Call `aCallback(BlocksRingBuffer::EntryReader&)` on each item.
-  // Callback should not store `EntryReader`, as it may become invalid after
-  // this thread-safe call.
+  // Callback should not store `EntryReader`, because it may become invalid
+  // after this call.
   template <typename Callback>
   void ReadEach(Callback&& aCallback) const {
-    Read([&](const Reader& aReader) { aReader.ForEach(aCallback); });
+    Read([&](Maybe<Reader>&& aMaybeReader) {
+      if (MOZ_LIKELY(aMaybeReader)) {
+        std::move(aMaybeReader)->ForEach(aCallback);
+      }
+    });
   }
 
   // Call `aCallback(Maybe<BlocksRingBuffer::EntryReader>&&)` on the entry at
-  // the given BlockIndex; The `Maybe` will be `Nothing` if that entry doesn't
-  // exist anymore, or if we've reached just past the last entry. Return
-  // whatever `aCallback` returns.
-  // Callback should not store `EntryReader`, as it may become invalid after
-  // this thread-safe call.
+  // the given BlockIndex; The `Maybe` will be `Nothing` if out-of-session, or
+  // if that entry doesn't exist anymore, or if we've reached just past the
+  // last entry. Return whatever `aCallback` returns. Callback should not
+  // store `EntryReader`, because it may become invalid after this call.
   template <typename Callback>
   auto ReadAt(BlockIndex aBlockIndex, Callback&& aCallback) const {
     baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
     MOZ_ASSERT(aBlockIndex <= mNextWriteIndex);
-    Maybe<EntryReader> maybeReader;
-    if (aBlockIndex >= mFirstReadIndex && aBlockIndex < mNextWriteIndex) {
+    Maybe<EntryReader> maybeEntryReader;
+    if (MOZ_LIKELY(mMaybeUnderlyingBuffer) && aBlockIndex >= mFirstReadIndex &&
+        aBlockIndex < mNextWriteIndex) {
       AssertBlockIndexIsValid(aBlockIndex);
-      maybeReader.emplace(ReaderInBlockAt(aBlockIndex));
+      maybeEntryReader.emplace(ReaderInBlockAt(aBlockIndex));
     }
-    return std::forward<Callback>(aCallback)(std::move(maybeReader));
+    return std::forward<Callback>(aCallback)(std::move(maybeEntryReader));
   }
 
   class EntryReserver;
 
   // Class used to write an entry contents.
   // Created through `EntryReserver`, lives within a lock guard lifetime.
   class EntryWriter : public BufferWriter {
    public:
@@ -562,17 +654,18 @@ class BlocksRingBuffer {
     // `aEntryBytes`.
     static Length BlockSizeForEntrySize(Length aEntryBytes) {
       return aEntryBytes +
              static_cast<Length>(BufferWriter::ULEB128Size(aEntryBytes));
     }
 
     EntryWriter(BlocksRingBuffer& aRing, BlockIndex aBlockIndex,
                 Length aEntryBytes)
-        : BufferWriter(aRing.mBuffer.WriterAt(Index(aBlockIndex))),
+        : BufferWriter(aRing.mMaybeUnderlyingBuffer->mBuffer.WriterAt(
+              Index(aBlockIndex))),
           mRing(aRing),
           mEntryBytes(aEntryBytes),
           mEntryStart([&]() {
             // BufferWriter is at `aBlockIndex`. Write the entry size...
             BufferWriter::WriteULEB128(aEntryBytes);
             // ... BufferWriter now at start of entry section.
             return CurrentIndex();
           }()) {
@@ -602,39 +695,43 @@ class BlocksRingBuffer {
     // return whatever `aCallback` returns.
     // Callback should not store `EntryWriter`, as it may become invalid after
     // this thread-safe call.
     template <typename Callback>
     auto Reserve(Length aBytes, Callback&& aCallback) {
       // Don't allow even half of the buffer length. More than that would
       // probably be unreasonable, and much more would risk having an entry
       // wrapping around and overwriting itself!
-      MOZ_RELEASE_ASSERT(aBytes < mRing->BufferLength().Value() / 2);
+      MOZ_RELEASE_ASSERT(
+          aBytes <
+          mRing->mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value() / 2);
       // COmpute block size from the requested entry size.
       const Length blockBytes = EntryWriter::BlockSizeForEntrySize(aBytes);
       // We will put this new block at the end of the current buffer.
       const BlockIndex blockIndex = mRing->mNextWriteIndex;
       // Compute the end of this new block...
       const Index blockEnd = Index(blockIndex) + blockBytes;
       // ... which is where the following block will go.
       mRing->mNextWriteIndex = BlockIndex(blockEnd);
-      while (blockEnd >
-             Index(mRing->mFirstReadIndex) + mRing->BufferLength().Value()) {
+      while (
+          blockEnd >
+          Index(mRing->mFirstReadIndex) +
+              mRing->mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value()) {
         // About to trample on an old block.
         EntryReader reader = mRing->ReaderInBlockAt(mRing->mFirstReadIndex);
         // Call provided entry destructor for that entry.
-        if (mRing->mEntryDestructor) {
-          mRing->mEntryDestructor(reader);
+        if (mRing->mMaybeUnderlyingBuffer->mEntryDestructor) {
+          mRing->mMaybeUnderlyingBuffer->mEntryDestructor(reader);
         }
-        mRing->mClearedBlockCount += 1;
+        mRing->mMaybeUnderlyingBuffer->mClearedBlockCount += 1;
         MOZ_ASSERT(reader.CurrentIndex() <= Index(reader.NextBlockIndex()));
         // Move the buffer reading start past this cleared block.
         mRing->mFirstReadIndex = reader.NextBlockIndex();
       }
-      mRing->mPushedBlockCount += 1;
+      mRing->mMaybeUnderlyingBuffer->mPushedBlockCount += 1;
       // Finally, let aCallback write into the entry.
       return std::forward<Callback>(aCallback)(
           EntryWriter(*mRing, blockIndex, aBytes));
     }
 
     // Write a new entry copied from the given buffer, return block index.
     BlockIndex Write(const void* aSrc, Length aBytes) {
       return Reserve(aBytes, [&](EntryWriter aEW) {
@@ -682,120 +779,154 @@ class BlocksRingBuffer {
     }
 
     // Using a non-null pointer instead of a reference, to allow copying.
     // This EntryReserver should only live inside one of the thread-safe
     // BlocksRingBuffer functions, for this reference to stay valid.
     NotNull<BlocksRingBuffer*> mRing;
   };
 
-  // Add a new entry, call `aCallback` with a temporary EntryReserver (so that
-  // `aCallback` can reserve an entry or just write something), and return
-  // whatever `aCallback` returns.
-  // Callback should not store `EntryReserver`, as it may become invalid after
-  // this thread-safe call.
+  // Main function to write entries.
+  // Call `aCallback(Maybe<BlocksRingBuffer::EntryReserver>&&)`, and return
+  // whatever `aCallback` returns. `Maybe` may be `Nothing` when out-of-session.
+  // Callback should not store `EntryReserver`, because it may become invalid
+  // after this call. The `EntryReserver` can then be used to reserve one or
+  // more entries; another callback can then fill each.
   template <typename Callback>
   auto Put(Callback&& aCallback) {
     // Implementation note: We are locking during the whole operation (reserving
     // and writing entry), which means slow writers could block the buffer for a
     // while. It should be possible to only lock when reserving the space, and
     // then letting the callback write the entry without a need for the lock, as
     // it's the only thread that should be accessing this particular entry.
     // Extra safety would be necessary to ensure the entry cannot be read, and
     // fast writers going around the ring cannot trample on this entry until it
     // is fully written.
     // TODO: Investigate this potential improvement as part of bug 1562604.
     baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
-    return std::forward<Callback>(aCallback)(EntryReserver(*this));
+    Maybe<EntryReserver> maybeEntryReserver;
+    if (MOZ_LIKELY(mMaybeUnderlyingBuffer)) {
+      maybeEntryReserver.emplace(EntryReserver(*this));
+    }
+    return std::forward<Callback>(aCallback)(std::move(maybeEntryReserver));
   }
 
   // Add a new entry of known size, call `aCallback` with a temporary
-  // EntryWriter, and return whatever `aCallback` returns.
-  // Callback should not store `EntryWriter`, as it may become invalid after
-  // this thread-safe call.
+  // EntryWriter, and return whatever `aCallback` returns. Callback should not
+  // store `EntryWriter`, as it may become invalid after this thread-safe call.
   template <typename Callback>
   auto Put(Length aLength, Callback&& aCallback) {
-    return Put([&](EntryReserver aER) {
-      return aER.Reserve(aLength, std::forward<Callback>(aCallback));
+    return Put([&](Maybe<EntryReserver>&& aER) {
+      if (MOZ_LIKELY(aER)) {
+        // We are in-session, with an EntryReserver at the ready.
+        // Reserve the requested space, then invoke the callback with the given
+        // EntryWriter inserted into a Maybe.
+        return aER->Reserve(aLength, [&](EntryWriter aEW) {
+          return std::forward<Callback>(aCallback)(Some(std::move(aEW)));
+        });
+      }
+      // Out-of-session, just invoke the callback with Nothing.
+      return std::forward<Callback>(aCallback)(Maybe<EntryWriter>{});
     });
   }
 
   // Add a new entry copied from the given buffer, return block index.
   BlockIndex PutFrom(const void* aSrc, Length aBytes) {
-    return Put([&](EntryReserver aER) { return aER.Write(aSrc, aBytes); });
+    return Put([&](Maybe<EntryReserver>&& aER) {
+      if (MOZ_LIKELY(aER)) {
+        return std::move(aER)->Write(aSrc, aBytes);
+      }
+      // Out-of-session, return "empty" BlockIndex.
+      return BlockIndex{};
+    });
   }
 
   // Add a new entry copied from the given object, return block index.
   // Restricted to trivially-copyable types.
   // TODO: Allow more types (follow-up patches in progress, see bug 1562604).
   template <typename T>
   BlockIndex PutObject(const T& aOb) {
-    return Put([&](EntryReserver aER) { return aER.WriteObject<T>(aOb); });
+    return Put([&](Maybe<EntryReserver>&& aER) {
+      if (MOZ_LIKELY(aER)) {
+        return std::move(aER)->WriteObject<T>(aOb);
+      }
+      // Out-of-session, return "empty" BlockIndex.
+      return BlockIndex{};
+    });
   }
 
   // Clear all entries, calling entry destructor (if any), and move read index
   // to the end so that these entries cannot be read anymore.
   void Clear() {
     baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
     ClearAllEntries();
   }
 
   // Clear all entries strictly before aBlockIndex, calling calling entry
   // destructor (if any), and move read index to the end so that these entries
   // cannot be read anymore.
   void ClearBefore(BlockIndex aBlockIndex) {
     baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    if (!mMaybeUnderlyingBuffer) {
+      return;
+    }
     // Don't accept a not-yet-written index. One-past-the-end is ok.
     MOZ_ASSERT(aBlockIndex <= mNextWriteIndex);
     if (aBlockIndex <= mFirstReadIndex) {
       // Already cleared.
       return;
     }
     if (aBlockIndex == mNextWriteIndex) {
       // Right past the end, just clear everything.
       ClearAllEntries();
       return;
     }
     // Otherwise we need to clear a subset of entries.
     AssertBlockIndexIsValid(aBlockIndex);
-    if (mEntryDestructor) {
+    if (mMaybeUnderlyingBuffer->mEntryDestructor) {
       // We have an entry destructor, destroy entries before aBlockIndex.
       Reader reader(*this);
       BlockIterator it = reader.begin();
       for (; it.CurrentBlockIndex() < aBlockIndex; ++it) {
         MOZ_ASSERT(it.CurrentBlockIndex() < reader.end().CurrentBlockIndex());
         EntryReader reader = *it;
-        mEntryDestructor(reader);
-        mClearedBlockCount += 1;
+        mMaybeUnderlyingBuffer->mEntryDestructor(reader);
+        mMaybeUnderlyingBuffer->mClearedBlockCount += 1;
       }
       MOZ_ASSERT(it.CurrentBlockIndex() == aBlockIndex);
     } else {
       // No entry destructor, just count skipped entries.
       Reader reader(*this);
       BlockIterator it = reader.begin();
       for (; it.CurrentBlockIndex() < aBlockIndex; ++it) {
         MOZ_ASSERT(it.CurrentBlockIndex() < reader.end().CurrentBlockIndex());
-        mClearedBlockCount += 1;
+        mMaybeUnderlyingBuffer->mClearedBlockCount += 1;
       }
       MOZ_ASSERT(it.CurrentBlockIndex() == aBlockIndex);
     }
     // Move read index to given index, so there's effectively no more entries
     // before.
     mFirstReadIndex = aBlockIndex;
   }
 
 #ifdef DEBUG
   void Dump() const {
     baseprofiler::detail::BaseProfilerAutoLock lock(mMutex);
+    if (!mMaybeUnderlyingBuffer) {
+      printf("empty BlocksRingBuffer\n");
+      return;
+    }
     using ULL = unsigned long long;
     printf("start=%llu (%llu) end=%llu (%llu) - ", ULL(Index(mFirstReadIndex)),
-           ULL(Index(mFirstReadIndex) & (BufferLength().Value() - 1)),
+           ULL(Index(mFirstReadIndex) &
+               (mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value() - 1)),
            ULL(Index(mNextWriteIndex)),
-           ULL(Index(mNextWriteIndex) & (BufferLength().Value() - 1)));
-    mBuffer.Dump();
+           ULL(Index(mNextWriteIndex) &
+               (mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value() - 1)));
+    mMaybeUnderlyingBuffer->mBuffer.Dump();
   }
 #endif  // DEBUG
 
  private:
   // In DEBUG mode, assert that `aBlockIndex` is a valid index for a live block.
   // (Not just in range, but points exactly at the start of a block.)
   // Slow, so avoid it for internal checks; this is more to check what callers
   // provide us.
@@ -803,21 +934,23 @@ class BlocksRingBuffer {
     mMutex.AssertCurrentThreadOwns();
 #ifdef DEBUG
     MOZ_ASSERT(aBlockIndex >= mFirstReadIndex);
     MOZ_ASSERT(aBlockIndex < mNextWriteIndex);
     // Quick check (default), or slow check (change '1' to '0') below:
 #  if 1
     // Quick check that this looks like a valid block start.
     // Read the entry size at the start of the block.
-    BufferReader br = mBuffer.ReaderAt(Index(aBlockIndex));
+    BufferReader br =
+        mMaybeUnderlyingBuffer->mBuffer.ReaderAt(Index(aBlockIndex));
     Length entryBytes = br.ReadULEB128<Length>();
     // It should be between 1 and half of the buffer length max.
     MOZ_ASSERT(entryBytes > 0);
-    MOZ_ASSERT(entryBytes < BufferLength().Value() / 2);
+    MOZ_ASSERT(entryBytes <
+               mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value() / 2);
     // The end of the block should be inside the live buffer range.
     MOZ_ASSERT(Index(aBlockIndex) + BufferReader::ULEB128Size(entryBytes) +
                    entryBytes <=
                Index(mNextWriteIndex));
 #  else
     // Slow check that the index is really the start of the block.
     // This kills performances, as it reads from the first index until
     // aBlockIndex. Only use to debug issues locally.
@@ -839,49 +972,124 @@ class BlocksRingBuffer {
     return EntryReader(*this, aBlockIndex);
   }
 
   // Call entry destructor (if any) on all entries.
   // Note: The read index is not moved; this should only be called from the
   // destructor or ClearAllEntries.
   void DestroyAllEntries() {
     mMutex.AssertCurrentThreadOwns();
-    if (mEntryDestructor) {
+    if (!mMaybeUnderlyingBuffer) {
+      return;
+    }
+    if (mMaybeUnderlyingBuffer->mEntryDestructor) {
       // We have an entry destructor, destroy all the things!
-      Reader(*this).ForEach(
-          [this](EntryReader& aReader) { mEntryDestructor(aReader); });
+      Reader(*this).ForEach([this](EntryReader& aReader) {
+        mMaybeUnderlyingBuffer->mEntryDestructor(aReader);
+      });
     }
-    mClearedBlockCount = mPushedBlockCount;
+    mMaybeUnderlyingBuffer->mClearedBlockCount =
+        mMaybeUnderlyingBuffer->mPushedBlockCount;
   }
 
   // Clear all entries, calling entry destructor (if any), and move read index
   // to the end so that these entries cannot be read anymore.
   void ClearAllEntries() {
     mMutex.AssertCurrentThreadOwns();
+    if (!mMaybeUnderlyingBuffer) {
+      return;
+    }
     DestroyAllEntries();
     // Move read index to write index, so there's effectively no more entries
     // that can be read. (Not setting both to 0, in case user is keeping
     // `BlockIndex`'es to old entries.)
     mFirstReadIndex = mNextWriteIndex;
   }
 
+  // If there is an underlying buffer (with optional entry destructor), destroy
+  // all entries, move read index to the end, and discard the buffer and entry
+  // destructor. This BlocksRingBuffer will now gracefully reject all API calls,
+  // and is in a state where a new underlying buffer&entry deleter may be
+  // installed.
+  void ResetUnderlyingBuffer() {
+    if (!mMaybeUnderlyingBuffer) {
+      return;
+    }
+    ClearAllEntries();
+    mMaybeUnderlyingBuffer.reset();
+  }
+
   // Mutex guarding the following members.
   mutable baseprofiler::detail::BaseProfilerMutex mMutex;
 
-  // Underlying circular byte buffer.
-  Buffer mBuffer;
+  struct UnderlyingBuffer {
+    // Create a buffer of the given length.
+    explicit UnderlyingBuffer(PowerOfTwo<Length> aLength) : mBuffer(aLength) {}
+
+    // Take ownership of an existing buffer.
+    UnderlyingBuffer(UniquePtr<Buffer::Byte[]> aExistingBuffer,
+                     PowerOfTwo<Length> aLength)
+        : mBuffer(std::move(aExistingBuffer), aLength) {}
+
+    // Use an externally-owned buffer.
+    UnderlyingBuffer(Buffer::Byte* aExternalBuffer, PowerOfTwo<Length> aLength)
+        : mBuffer(aExternalBuffer, aLength) {}
+
+    // Create a buffer of the given length.
+    template <typename EntryDestructor>
+    explicit UnderlyingBuffer(PowerOfTwo<Length> aLength,
+                              EntryDestructor&& aEntryDestructor)
+        : mBuffer(aLength),
+          mEntryDestructor(std::forward<EntryDestructor>(aEntryDestructor)) {}
+
+    // Take ownership of an existing buffer.
+    template <typename EntryDestructor>
+    explicit UnderlyingBuffer(UniquePtr<Buffer::Byte[]> aExistingBuffer,
+                              PowerOfTwo<Length> aLength,
+                              EntryDestructor&& aEntryDestructor)
+        : mBuffer(std::move(aExistingBuffer), aLength),
+          mEntryDestructor(std::forward<EntryDestructor>(aEntryDestructor)) {}
+
+    // Use an externally-owned buffer.
+    template <typename EntryDestructor>
+    explicit UnderlyingBuffer(Buffer::Byte* aExternalBuffer,
+                              PowerOfTwo<Length> aLength,
+                              EntryDestructor&& aEntryDestructor)
+        : mBuffer(aExternalBuffer, aLength),
+          mEntryDestructor(std::forward<EntryDestructor>(aEntryDestructor)) {}
+
+    // Only allow move-construction.
+    UnderlyingBuffer(UnderlyingBuffer&&) = default;
+
+    // Copies and move-assignment are explictly disallowed.
+    UnderlyingBuffer(const UnderlyingBuffer&) = delete;
+    UnderlyingBuffer& operator=(const UnderlyingBuffer&) = delete;
+    UnderlyingBuffer& operator=(UnderlyingBuffer&&) = delete;
+
+    // Underlying circular byte buffer.
+    Buffer mBuffer;
+    // If set, function to call for each entry that is about to be destroyed.
+    std::function<void(EntryReader&)> mEntryDestructor;
+
+    // Statistics.
+    uint64_t mPushedBlockCount = 0;
+    uint64_t mClearedBlockCount = 0;
+  };
+
+  // Underlying buffer, with entry destructor and stats.
+  // Only valid during in-session period.
+  Maybe<UnderlyingBuffer> mMaybeUnderlyingBuffer;
+
   // Index to the first block to be read (or cleared). Initialized to 1 because
-  // 0 is reserved for the "empty" BlockIndex value.
+  // 0 is reserved for the "empty" BlockIndex value. Kept between sessions, so
+  // that stored indices from one session will be gracefully denied in future
+  // sessions.
   BlockIndex mFirstReadIndex = BlockIndex(Index(1));
   // Index where the next new block should be allocated. Initialized to 1
-  // because 0 is reserved for the "empty" BlockIndex value.
+  // because 0 is reserved for the "empty" BlockIndex value. Kept between
+  // sessions, so that stored indices from one session will be gracefully denied
+  // in future sessions.
   BlockIndex mNextWriteIndex = BlockIndex(Index(1));
-  // If set, function to call for each entry that is about to be destroyed.
-  std::function<void(EntryReader&)> mEntryDestructor;
-
-  // Statistics.
-  uint64_t mPushedBlockCount = 0;
-  uint64_t mClearedBlockCount = 0;
 };
 
 }  // namespace mozilla
 
 #endif  // BlocksRingBuffer_h
--- a/mozglue/tests/TestBaseProfiler.cpp
+++ b/mozglue/tests/TestBaseProfiler.cpp
@@ -520,18 +520,19 @@ void TestBlocksRingBufferAPI() {
 
     // Push `1` directly.
     MOZ_RELEASE_ASSERT(ExtractBlockIndex(rb.PutObject(uint32_t(1))) == 1);
     //   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     //   - S[4 |    int(1)    ]E
     VERIFY_START_END_DESTROYED(1, 6, 0);
 
     // Push `2` through EntryReserver, check output BlockIndex.
-    auto bi2 = rb.Put([](BlocksRingBuffer::EntryReserver aER) {
-      return aER.WriteObject(uint32_t(2));
+    auto bi2 = rb.Put([](Maybe<BlocksRingBuffer::EntryReserver>&& aER) {
+      MOZ_RELEASE_ASSERT(aER.isSome());
+      return aER->WriteObject(uint32_t(2));
     });
     static_assert(
         std::is_same<decltype(bi2), BlocksRingBuffer::BlockIndex>::value,
         "All index-returning functions should return a "
         "BlocksRingBuffer::BlockIndex");
     MOZ_RELEASE_ASSERT(ExtractBlockIndex(bi2) == 6);
     //   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     //   - S[4 |    int(1)    ] [4 |    int(2)    ]E
@@ -596,18 +597,19 @@ void TestBlocksRingBufferAPI() {
     MOZ_RELEASE_ASSERT(bi2Next > bi2);
     MOZ_RELEASE_ASSERT(bi2Next >= bi2);
     MOZ_RELEASE_ASSERT(!(bi2Next == bi2));
     MOZ_RELEASE_ASSERT(!(bi2Next < bi2));
     MOZ_RELEASE_ASSERT(!(bi2Next <= bi2));
 
     // Push `3` through EntryReserver and then EntryWriter, check writer output
     // is returned to the initial caller.
-    auto put3 = rb.Put([&](BlocksRingBuffer::EntryReserver aER) {
-      return aER.Reserve(
+    auto put3 = rb.Put([&](Maybe<BlocksRingBuffer::EntryReserver>&& aER) {
+      MOZ_RELEASE_ASSERT(aER.isSome());
+      return aER->Reserve(
           sizeof(uint32_t), [&](BlocksRingBuffer::EntryWriter aEW) {
             aEW.WriteObject(uint32_t(3));
             return float(ExtractBlockIndex(aEW.CurrentBlockIndex()));
           });
     });
     static_assert(std::is_same<decltype(put3), float>::value,
                   "Expect float as returned by callback.");
     MOZ_RELEASE_ASSERT(put3 == 11.0);
@@ -663,18 +665,19 @@ void TestBlocksRingBufferAPI() {
     rb.ReadEach([&](BlocksRingBuffer::EntryReader& aReader) {
       MOZ_RELEASE_ASSERT(aReader.ReadObject<uint32_t>() == ++count);
     });
     MOZ_RELEASE_ASSERT(count == 4);
 
     // Push 5 through EntryReserver then EntryWriter, no returns.
     // This will destroy the second entry.
     // Check that the EntryWriter can access bi4 but not bi2.
-    auto bi5_6 = rb.Put([&](BlocksRingBuffer::EntryReserver aER) {
-      return aER.Reserve(
+    auto bi5_6 = rb.Put([&](Maybe<BlocksRingBuffer::EntryReserver>&& aER) {
+      MOZ_RELEASE_ASSERT(aER.isSome());
+      return aER->Reserve(
           sizeof(uint32_t), [&](BlocksRingBuffer::EntryWriter aEW) {
             aEW.WriteObject(uint32_t(5));
             MOZ_RELEASE_ASSERT(aEW.GetEntryAt(bi2).isNothing());
             MOZ_RELEASE_ASSERT(aEW.GetEntryAt(bi4).isSome());
             MOZ_RELEASE_ASSERT(aEW.GetEntryAt(bi4)->CurrentBlockIndex() == bi4);
             MOZ_RELEASE_ASSERT(aEW.GetEntryAt(bi4)->ReadObject<uint32_t>() ==
                                4);
             return MakePair(aEW.CurrentBlockIndex(), aEW.BlockEndIndex());
@@ -769,16 +772,227 @@ void TestBlocksRingBufferAPI() {
   }
   for (size_t i = MBSize * 2; i < MBSize * 3; ++i) {
     MOZ_RELEASE_ASSERT(buffer[i] == uint8_t('A' + i));
   }
 
   printf("TestBlocksRingBufferAPI done\n");
 }
 
+void TestBlocksRingBufferUnderlyingBufferChanges() {
+  printf("TestBlocksRingBufferUnderlyingBufferChanges...\n");
+
+  // Out-of-session BlocksRingBuffer to start with.
+  BlocksRingBuffer rb;
+
+  // Block index to read at. Initially "null", but may be changed below.
+  BlocksRingBuffer::BlockIndex bi;
+
+  // Test all rb APIs when rb is out-of-session and therefore doesn't have an
+  // underlying buffer.
+  auto testOutOfSession = [&]() {
+    MOZ_RELEASE_ASSERT(rb.BufferLength().isNothing());
+    BlocksRingBuffer::State state = rb.GetState();
+    // When out-of-session, range start and ends are the same, and there are no
+    // pushed&cleared blocks.
+    MOZ_RELEASE_ASSERT(state.mRangeStart == state.mRangeEnd);
+    MOZ_RELEASE_ASSERT(state.mPushedBlockCount == 0);
+    MOZ_RELEASE_ASSERT(state.mClearedBlockCount == 0);
+    // `Put()` functions run the callback with `Nothing`.
+    int32_t ran = 0;
+    rb.Put([&](Maybe<BlocksRingBuffer::EntryReserver>&& aMaybeEntryReserver) {
+      MOZ_RELEASE_ASSERT(aMaybeEntryReserver.isNothing());
+      ++ran;
+    });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    ran = 0;
+    rb.Put(1, [&](Maybe<BlocksRingBuffer::EntryWriter>&& aMaybeEntryWriter) {
+      MOZ_RELEASE_ASSERT(aMaybeEntryWriter.isNothing());
+      ++ran;
+    });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    // `PutFrom` won't do anything, and returns the null BlockIndex.
+    MOZ_RELEASE_ASSERT(rb.PutFrom(&ran, sizeof(ran)) ==
+                       BlocksRingBuffer::BlockIndex{});
+    MOZ_RELEASE_ASSERT(rb.PutObject(ran) == BlocksRingBuffer::BlockIndex{});
+    // `Read()` functions run the callback with `Nothing`.
+    ran = 0;
+    rb.Read([&](Maybe<BlocksRingBuffer::Reader>&& aMaybeReader) {
+      MOZ_RELEASE_ASSERT(aMaybeReader.isNothing());
+      ++ran;
+    });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    ran = 0;
+    rb.ReadAt(BlocksRingBuffer::BlockIndex{},
+              [&](Maybe<BlocksRingBuffer::EntryReader>&& aMaybeEntryReader) {
+                MOZ_RELEASE_ASSERT(aMaybeEntryReader.isNothing());
+                ++ran;
+              });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    ran = 0;
+    rb.ReadAt(bi,
+              [&](Maybe<BlocksRingBuffer::EntryReader>&& aMaybeEntryReader) {
+                MOZ_RELEASE_ASSERT(aMaybeEntryReader.isNothing());
+                ++ran;
+              });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    // `ReadEach` shouldn't run the callback (nothing to read).
+    rb.ReadEach([](auto&&) { MOZ_RELEASE_ASSERT(false); });
+  };
+
+  // As `testOutOfSession()` attempts to modify the buffer, we run it twice to
+  // make sure one run doesn't influence the next one.
+  testOutOfSession();
+  testOutOfSession();
+
+  rb.ClearBefore(bi);
+  testOutOfSession();
+  testOutOfSession();
+
+  rb.Clear();
+  testOutOfSession();
+  testOutOfSession();
+
+  rb.Reset();
+  testOutOfSession();
+  testOutOfSession();
+
+  constexpr uint32_t MBSize = 32;
+
+  rb.Set(MakePowerOfTwo<BlocksRingBuffer::Length, MBSize>());
+
+  constexpr bool EMPTY = true;
+  constexpr bool NOT_EMPTY = false;
+  // Test all rb APIs when rb has an underlying buffer.
+  auto testInSession = [&](bool aExpectEmpty) {
+    MOZ_RELEASE_ASSERT(rb.BufferLength().isSome());
+    BlocksRingBuffer::State state = rb.GetState();
+    if (aExpectEmpty) {
+      MOZ_RELEASE_ASSERT(state.mRangeStart == state.mRangeEnd);
+      MOZ_RELEASE_ASSERT(state.mPushedBlockCount == 0);
+      MOZ_RELEASE_ASSERT(state.mClearedBlockCount == 0);
+    } else {
+      MOZ_RELEASE_ASSERT(state.mRangeStart < state.mRangeEnd);
+      MOZ_RELEASE_ASSERT(state.mPushedBlockCount > 0);
+      MOZ_RELEASE_ASSERT(state.mClearedBlockCount <= state.mPushedBlockCount);
+    }
+    int32_t ran = 0;
+    rb.Put([&](Maybe<BlocksRingBuffer::EntryReserver>&& aMaybeEntryReserver) {
+      MOZ_RELEASE_ASSERT(aMaybeEntryReserver.isSome());
+      ++ran;
+    });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    ran = 0;
+    // The following three `Put...` will write three int32_t of value 1.
+    bi = rb.Put(sizeof(ran),
+                [&](Maybe<BlocksRingBuffer::EntryWriter>&& aMaybeEntryWriter) {
+                  MOZ_RELEASE_ASSERT(aMaybeEntryWriter.isSome());
+                  ++ran;
+                  aMaybeEntryWriter->WriteObject(ran);
+                  return aMaybeEntryWriter->CurrentBlockIndex();
+                });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    MOZ_RELEASE_ASSERT(rb.PutFrom(&ran, sizeof(ran)) !=
+                       BlocksRingBuffer::BlockIndex{});
+    MOZ_RELEASE_ASSERT(rb.PutObject(ran) != BlocksRingBuffer::BlockIndex{});
+    ran = 0;
+    rb.Read([&](Maybe<BlocksRingBuffer::Reader>&& aMaybeReader) {
+      MOZ_RELEASE_ASSERT(aMaybeReader.isSome());
+      ++ran;
+    });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    ran = 0;
+    rb.ReadEach([&](BlocksRingBuffer::EntryReader& aEntryReader) {
+      MOZ_RELEASE_ASSERT(aEntryReader.RemainingBytes() == sizeof(ran));
+      MOZ_RELEASE_ASSERT(aEntryReader.ReadObject<decltype(ran)>() == 1);
+      ++ran;
+    });
+    MOZ_RELEASE_ASSERT(ran >= 3);
+    ran = 0;
+    rb.ReadAt(BlocksRingBuffer::BlockIndex{},
+              [&](Maybe<BlocksRingBuffer::EntryReader>&& aMaybeEntryReader) {
+                MOZ_RELEASE_ASSERT(aMaybeEntryReader.isNothing());
+                ++ran;
+              });
+    MOZ_RELEASE_ASSERT(ran == 1);
+    ran = 0;
+    rb.ReadAt(bi,
+              [&](Maybe<BlocksRingBuffer::EntryReader>&& aMaybeEntryReader) {
+                MOZ_RELEASE_ASSERT(aMaybeEntryReader.isNothing() == !bi);
+                ++ran;
+              });
+    MOZ_RELEASE_ASSERT(ran == 1);
+  };
+
+  testInSession(EMPTY);
+  testInSession(NOT_EMPTY);
+
+  rb.Set(MakePowerOfTwo<BlocksRingBuffer::Length, 32>());
+  MOZ_RELEASE_ASSERT(rb.BufferLength().isSome());
+  rb.ReadEach([](auto&&) { MOZ_RELEASE_ASSERT(false); });
+
+  testInSession(EMPTY);
+  testInSession(NOT_EMPTY);
+
+  rb.Reset();
+  testOutOfSession();
+  testOutOfSession();
+
+  uint8_t buffer[MBSize * 3];
+  for (size_t i = 0; i < MBSize * 3; ++i) {
+    buffer[i] = uint8_t('A' + i);
+  }
+
+  rb.Set(&buffer[MBSize], MakePowerOfTwo<BlocksRingBuffer::Length, MBSize>());
+  MOZ_RELEASE_ASSERT(rb.BufferLength().isSome());
+  rb.ReadEach([](auto&&) { MOZ_RELEASE_ASSERT(false); });
+
+  testInSession(EMPTY);
+  testInSession(NOT_EMPTY);
+
+  rb.Reset();
+  testOutOfSession();
+  testOutOfSession();
+
+  int cleared = 0;
+  rb.Set(&buffer[MBSize], MakePowerOfTwo<BlocksRingBuffer::Length, MBSize>(),
+         [&](auto&&) { ++cleared; });
+  MOZ_RELEASE_ASSERT(rb.BufferLength().isSome());
+  rb.ReadEach([](auto&&) { MOZ_RELEASE_ASSERT(false); });
+
+  testInSession(EMPTY);
+  testInSession(NOT_EMPTY);
+
+  // Remove the current underlying buffer, this should clear all entries.
+  rb.Reset();
+  // The above should clear all entries (2 tests, three entries each).
+  MOZ_RELEASE_ASSERT(cleared == 2 * 3);
+
+  // Check that only the provided stack-based sub-buffer was modified.
+  uint32_t changed = 0;
+  for (size_t i = MBSize; i < MBSize * 2; ++i) {
+    changed += (buffer[i] == uint8_t('A' + i)) ? 0 : 1;
+  }
+  // Expect at least 75% changes.
+  MOZ_RELEASE_ASSERT(changed >= MBSize * 6 / 8);
+
+  // Everything around the sub-buffer should be unchanged.
+  for (size_t i = 0; i < MBSize; ++i) {
+    MOZ_RELEASE_ASSERT(buffer[i] == uint8_t('A' + i));
+  }
+  for (size_t i = MBSize * 2; i < MBSize * 3; ++i) {
+    MOZ_RELEASE_ASSERT(buffer[i] == uint8_t('A' + i));
+  }
+
+  testOutOfSession();
+  testOutOfSession();
+
+  printf("TestBlocksRingBufferUnderlyingBufferChanges done\n");
+}
+
 void TestBlocksRingBufferThreading() {
   printf("TestBlocksRingBufferThreading...\n");
 
   // Entry destructor will store about-to-be-cleared value in `lastDestroyed`.
   std::atomic<int> lastDestroyed{0};
 
   constexpr uint32_t MBSize = 8192;
   uint8_t buffer[MBSize * 3];
@@ -822,19 +1036,20 @@ void TestBlocksRingBufferThreading() {
     threads[threadNo] = std::thread(
         [&](int aThreadNo) {
           ::SleepMilli(1);
           constexpr int pushCount = 1024;
           for (int push = 0; push < pushCount; ++push) {
             // Reserve as many bytes as the thread number (but at least enough
             // to store an int), and write an increasing int.
             rb.Put(std::max(aThreadNo, int(sizeof(push))),
-                   [&](BlocksRingBuffer::EntryWriter aEW) {
-                     aEW.WriteObject(aThreadNo * 1000000 + push);
-                     aEW += aEW.RemainingBytes();
+                   [&](Maybe<BlocksRingBuffer::EntryWriter>&& aEW) {
+                     MOZ_RELEASE_ASSERT(aEW.isSome());
+                     aEW->WriteObject(aThreadNo * 1000000 + push);
+                     *aEW += aEW->RemainingBytes();
                    });
           }
         },
         threadNo);
   }
 
   // Wait for all writer threads to die.
   for (auto&& thread : threads) {
@@ -897,16 +1112,17 @@ void TestProfiler() {
   // ::SleepMilli(10000);
 
   // Test dependencies.
   TestPowerOfTwoMask();
   TestPowerOfTwo();
   TestLEB128();
   TestModuloBuffer();
   TestBlocksRingBufferAPI();
+  TestBlocksRingBufferUnderlyingBufferChanges();
   TestBlocksRingBufferThreading();
 
   {
     printf("profiler_init()...\n");
     AUTO_BASE_PROFILER_INIT;
 
     MOZ_RELEASE_ASSERT(!baseprofiler::profiler_is_active());
     MOZ_RELEASE_ASSERT(!baseprofiler::profiler_thread_is_being_profiled());
--- a/taskcluster/ci/repo-update/kind.yml
+++ b/taskcluster/ci/repo-update/kind.yml
@@ -48,17 +48,17 @@ jobs:
             platform: linux64/opt
             symbol: pfu
             tier: 1
         worker-type: b-linux
         worker:
             implementation: docker-worker
             os: linux
             docker-image: {in-tree: periodic-updates}
-            max-run-time: 10800  # Takes 2-3 hours
+            max-run-time: 18000  # Takes 4+ hours
             env:
                 PRODUCT: firefox
                 REVIEWERS: "ryanvm"
             command:
                 - /runme.sh
             taskcluster-proxy: true
             artifacts:
                 - name: 'public/build/nsSTSPreloadList.diff'
--- a/testing/geckodriver/src/main.rs
+++ b/testing/geckodriver/src/main.rs
@@ -1,8 +1,10 @@
+#![forbid(unsafe_code)]
+
 extern crate base64;
 extern crate chrono;
 #[macro_use]
 extern crate clap;
 #[macro_use]
 extern crate lazy_static;
 extern crate hyper;
 extern crate marionette as marionette_rs;
--- a/testing/mozbase/rust/mozprofile/src/lib.rs
+++ b/testing/mozbase/rust/mozprofile/src/lib.rs
@@ -1,8 +1,10 @@
+#![forbid(unsafe_code)]
+
 extern crate tempfile;
 
 pub mod preferences;
 pub mod prefreader;
 pub mod profile;
 
 #[cfg(test)]
 mod test {
--- a/testing/mozbase/rust/mozrunner/src/lib.rs
+++ b/testing/mozbase/rust/mozrunner/src/lib.rs
@@ -1,8 +1,10 @@
+#![forbid(unsafe_code)]
+
 #[macro_use]
 extern crate log;
 #[cfg(target_os = "macos")]
 extern crate dirs;
 extern crate mozprofile;
 #[cfg(target_os = "macos")]
 extern crate plist;
 #[cfg(target_os = "windows")]
--- a/testing/mozbase/rust/mozversion/src/lib.rs
+++ b/testing/mozbase/rust/mozversion/src/lib.rs
@@ -1,8 +1,10 @@
+#![forbid(unsafe_code)]
+
 extern crate ini;
 extern crate regex;
 extern crate semver;
 
 use crate::platform::ini_path;
 use ini::Ini;
 use regex::Regex;
 use std::default::Default;
--- a/testing/web-platform/meta/mathml/relations/css-styling/attribute-mapping-001.html.ini
+++ b/testing/web-platform/meta/mathml/relations/css-styling/attribute-mapping-001.html.ini
@@ -18,19 +18,16 @@
     expected: FAIL
 
   [mathsize on the maction element is mapped to CSS font-size]
     expected: FAIL
 
   [mathsize on the mi element is mapped to CSS font-size]
     expected: FAIL
 
-  [mathcolor on the mmultiscripts element is mapped to CSS color]
-    expected: FAIL
-
   [dir on the mover element is mapped to CSS direction]
     expected: FAIL
 
   [mathsize on the mtable element is mapped to CSS font-size]
     expected: FAIL
 
   [mathsize on the mn element is mapped to CSS font-size]
     expected: FAIL
@@ -60,19 +57,16 @@
     expected: FAIL
 
   [mathsize on the none element is mapped to CSS font-size]
     expected: FAIL
 
   [dir on the merror element is mapped to CSS direction]
     expected: FAIL
 
-  [mathcolor on the annotation element is mapped to CSS color]
-    expected: FAIL
-
   [dir on the mfrac element is mapped to CSS direction]
     expected: FAIL
 
   [mathsize on the menclose element is mapped to CSS font-size]
     expected: FAIL
 
   [mathsize on the mroot element is mapped to CSS font-size]
     expected: FAIL
@@ -108,19 +102,16 @@
     expected: FAIL
 
   [dir on the mspace element is mapped to CSS direction]
     expected: FAIL
 
   [dir on the mphantom element is mapped to CSS direction]
     expected: FAIL
 
-  [mathbackground on the annotation element is mapped to CSS background-color]
-    expected: FAIL
-
   [dir on the annotation element is mapped to CSS direction]
     expected: FAIL
 
   [dir on the none element is mapped to CSS direction]
     expected: FAIL
 
   [dir on the mrow element is mapped to CSS direction]
     expected: FAIL
@@ -147,25 +138,19 @@
     expected: FAIL
 
   [mathsize on the mtd element is mapped to CSS font-size]
     expected: FAIL
 
   [mathsize on the merror element is mapped to CSS font-size]
     expected: FAIL
 
-  [mathbackground on the mmultiscripts element is mapped to CSS background-color]
-    expected: FAIL
-
   [dir on the msub element is mapped to CSS direction]
     expected: FAIL
 
-  [mathbackground on the annotation-xml element is mapped to CSS background-color]
-    expected: FAIL
-
   [mathsize on the mmultiscripts element is mapped to CSS font-size]
     expected: FAIL
 
   [dir on the mmultiscripts element is mapped to CSS direction]
     expected: FAIL
 
   [dir on the mi element is mapped to CSS direction]
     expected: FAIL
@@ -174,19 +159,16 @@
     expected: FAIL
 
   [mathsize on the msup element is mapped to CSS font-size]
     expected: FAIL
 
   [mathsize on the mphantom element is mapped to CSS font-size]
     expected: FAIL
 
-  [mathcolor on the annotation-xml element is mapped to CSS color]
-    expected: FAIL
-
   [mathsize on the mo element is mapped to CSS font-size]
     expected: FAIL
 
   [dir on the msubsup element is mapped to CSS direction]
     expected: FAIL
 
   [mathsize on the mtext element is mapped to CSS font-size]
     expected: FAIL
@@ -204,17 +186,11 @@
     expected: FAIL
 
   [dir on the mtable element is mapped to CSS direction]
     expected: FAIL
 
   [dir on the semantics element is mapped to CSS direction]
     expected: FAIL
 
-  [mathcolor on the semantics element is mapped to CSS color]
-    expected: FAIL
-
   [mathsize on the semantics element is mapped to CSS font-size]
     expected: FAIL
 
-  [mathbackground on the semantics element is mapped to CSS background-color]
-    expected: FAIL
-
--- a/testing/webdriver/src/lib.rs
+++ b/testing/webdriver/src/lib.rs
@@ -1,9 +1,10 @@
 #![allow(non_snake_case)]
+#![forbid(unsafe_code)]
 
 extern crate base64;
 extern crate cookie;
 #[macro_use]
 extern crate log;
 extern crate http;
 extern crate regex;
 extern crate serde;
--- a/third_party/rust/cranelift-bforest/.cargo-checksum.json
+++ b/third_party/rust/cranelift-bforest/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"4b2597a03220bf2e539f62dbbdee8758b496c29cc8c509c60e1349b0e933b78d","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"af367c67340fa7f6fb9a35b0aa637dcf303957f7ae7427a5f4f6356801c8bb04","src/lib.rs":"1b23abbfe5850a4cd77ae6ae5dcfc2f678ef36b4032fd7496f2b333c51e63301","src/map.rs":"5d891d62814941e19dfc88ff36538efa3da5479f3f97de8219a6f610c9a1ee32","src/node.rs":"e620c64e78488035f11723b14892c7986c06ad37dc5b115a35a453ff1ae66ca3","src/path.rs":"a86ee1c882c173e8af96fd53a416a0fb485dd3f045ac590ef313a9d9ecf90f56","src/pool.rs":"6090f8c0e0da16ebee0e31bca66392d0075b3aff529d30d4e716fa20cd0aef99","src/set.rs":"b411158f813a310c7a6c337d4ada3bf0a021088c443875dc25233415dcbe0633"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"a342f4338c2dd4e390dd2abf40dbe94a20d1ca85733e2c63c76280b003015544","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"af367c67340fa7f6fb9a35b0aa637dcf303957f7ae7427a5f4f6356801c8bb04","src/lib.rs":"1b23abbfe5850a4cd77ae6ae5dcfc2f678ef36b4032fd7496f2b333c51e63301","src/map.rs":"5d891d62814941e19dfc88ff36538efa3da5479f3f97de8219a6f610c9a1ee32","src/node.rs":"e620c64e78488035f11723b14892c7986c06ad37dc5b115a35a453ff1ae66ca3","src/path.rs":"a86ee1c882c173e8af96fd53a416a0fb485dd3f045ac590ef313a9d9ecf90f56","src/pool.rs":"6090f8c0e0da16ebee0e31bca66392d0075b3aff529d30d4e716fa20cd0aef99","src/set.rs":"b411158f813a310c7a6c337d4ada3bf0a021088c443875dc25233415dcbe0633"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-bforest/Cargo.toml
+++ b/third_party/rust/cranelift-bforest/Cargo.toml
@@ -1,23 +1,23 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-bforest"
-version = "0.37.0"
+version = "0.38.0"
 description = "A forest of B+-trees"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://cranelift.readthedocs.io/"
 repository = "https://github.com/CraneStation/cranelift"
 categories = ["no-std"]
 readme = "README.md"
 keywords = ["btree", "forest", "set", "map"]
 edition = "2018"
 
 [dependencies]
-cranelift-entity = { path = "../cranelift-entity", version = "0.37.0", default-features = false }
+cranelift-entity = { path = "../cranelift-entity", version = "0.38.0", default-features = false }
 
 [features]
 default = ["std"]
 std = ["cranelift-entity/std"]
 core = []
 
 [badges]
 maintenance = { status = "experimental" }
--- a/third_party/rust/cranelift-codegen-meta/.cargo-checksum.json
+++ b/third_party/rust/cranelift-codegen-meta/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"918c996a040b20c30d82c085c666949a1fb3429093fb762af932b07c33030b5d","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/ast.rs":"1c9bf45110e47710ea160f30b3f3b00b531a8d91ff365bae856dd9d44b928253","src/cdsl/cpu_modes.rs":"7c59ae347d6f9c769d6356fe49c8406934153eefa59d9bf37c182474fcbb9715","src/cdsl/encodings.rs":"da6fa16c95fe4a586c2d00ef4ccf79b4b0f64cd8923b3ca5a5a527da1e799a4f","src/cdsl/formats.rs":"811dcf12f6e9c5be72f18543675ff52c3996edd029064bd0643f878d2ceca3bd","src/cdsl/instructions.rs":"f4c778bf55c0e273a694ecbb995bc733799d6c5930a64210ff619f0a4a3fa039","src/cdsl/isa.rs":"9b6030a935f69b07726d239c23a78d654566785f1fed61ccefdaf7d4f0a97d0e","src/cdsl/mod.rs":"c85f62a7d8d6bedc81c673b8d02be01181f79f272dbc0325a76d52e7eec732e8","src/cdsl/operands.rs":"1cda258798d861c4f467783b5c70c1202a57f554861017eead6477af2ee34063","src/cdsl/recipes.rs":"9f50f29f243f2ed8dbca3ef8b2722e9671bc164b2956254a95ed62641315eab7","src/cdsl/regs.rs":"8a92798a92b2c34c5e572e03602454c72a896d31ac301f15f43d17c101d4da6e","src/cdsl/settings.rs":"7da3c5a9df8e47ed6ca7f1d820e28eae45e783f3759f6c55730d2f17d88f1013","src/cdsl/type_inference.rs":"695d4fd2720f43569a52f29998bd92562951f0b4e7844cc0a239640e0c7d0b0f","src/cdsl/types.rs":"0db5637fa86052384950d4725b2ae27082e7a6aefa1ce2d450ab4beb7d7c1405","src/cdsl/typevar.rs":"7249fcd7c3cd3645e8489c73595bd5327bedc499bd7bc87f2a68e241fba329c3","src/cdsl/xform.rs":"64e9b70ef1265c0331ee9d71c1a1f33dba3f6975b1639385c34d68456cda0e0e","src/constant_hash.rs":"66d6f42c1e98cd4dbc95009997dc00256b57c11d2c2d9eac63b33e458c68a56f","src/default_map.rs":"8bbd6f4d9f93ef2126538cda633e6d9c80a5c7dc79b8d5a759c8be1fe2dbc605","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_binemit.rs":"9024619858124b9ad9ecd232683f768beefffd34248da38c2f2e0210ae187267","src/gen_encodings.rs":"024b6ea8086fa1559333245753e909f306adeab7c8dfdc915610877413cba585","src/gen_inst.rs":"9e8c8e62761f4186fdb274c0c99ea03123d454d79371f9f1b20524c3a41631c0","src/gen_legalizer.rs":"094c1ce664637e54a07d6d928e68458cadbc6e8d83ca5f72e207866f61bbe08f","src/gen_registers.rs":"a544a2b91fafe08639e39e50bea0892fda89fe2f6eaf111b2d5f3e98e4d07b86","src/gen_settings.rs":"765ca86fa735342c1875021c34a92cbc39836ba3ad1c12aa615204372a1f7b61","src/gen_types.rs":"3935da6c6a53f9332e06f74bc3a46270656b4d4231ad28ed2648d7b1d2774e90","src/isa/arm32/mod.rs":"a2500871fb270e5624f5561f24fe510f08708cdca2ab1c3163848b67694f7a7a","src/isa/arm64/mod.rs":"dc210e8dc9f179d87d2c5a72d7795a9e34bb30afb91c8053c362d43635629d19","src/isa/mod.rs":"fce60d19dd3c099ebee3ac5ae64a2bee363f13da9ff5a4960d3c1a0bee71d29a","src/isa/riscv/encodings.rs":"8246543bedcc00f407baf140dcffe8632588fe64f87cf098b4445666b31067ec","src/isa/riscv/mod.rs":"895492f0f379cfb02aba1cbb9759dc489bbb4d51ec40759af9f4d71c45c17a86","src/isa/riscv/recipes.rs":"b1fcada01bac0c46376f50c835c1a33b8d96d2cbb77971b9caa73ffb2ad38b89","src/isa/x86/encodings.rs":"c0f28d4d2eea5e81d9f005d4bf9dac30537153dcc51173ab913ae478523ef642","src/isa/x86/instructions.rs":"eea5fa7dd804d435dfdf289fc47f4bcc77db8dfce068d78d0a428155604b9626","src/isa/x86/legalize.rs":"2617153608816928a298e1ef18aa34cb62ccc1aa717d74b89e574881299d27d4","src/isa/x86/mod.rs":"6ed9f102238c1cb9029ec953e37629767438580cf9df8b4f2d1eace75ecd86fc","src/isa/x86/recipes.rs":"7a25de41b3affc092fa4aaaf195e65c77329072a80a6c9aa7c978732bb53f9b7","src/isa/x86/registers.rs":"c0bc60336a8c8b7b4db92dc623e9419a60af14dd6252f0b19e227e46f7166178","src/isa/x86/settings.rs":"4291933922323544f72a56cf5c89427b6d550ac236fb74d617268242396ad3d3","src/lib.rs":"bc458358fd81d092f368b243d07682259dbed7a336b1eed5bcdf5228368289e9","src/shared/entities.rs":"80b8ff57a09c7b2f9dab755abbcc2738317de474776fe1de0d2a581310aa9af8","src/shared/formats.rs":"20908b1048c5e71a185de6b6ded79cdff2c26ddb38ba7b134b7a27f37e8324f3","src/shared/immediates.rs":"804c4c9ffa2fe55d90279ee158aaa6bd6c7f0c604d63d7457a98e82269cec9a7","src/shared/instructions.rs":"178eb2e3817f7f5a1c2ee4498c55c54d2279843c20f6df7480ef055095503e50","src/shared/legalize.rs":"1c32c28f603b11f89e1ba9c4d301b0b8036fd81254d227836202b84573a9a446","src/shared/mod.rs":"f5bb50d8292e46380afdd83a320cb5d6021e1483741e67b1e277053c28f9b943","src/shared/settings.rs":"fd50687cfe558e3d156a22e7445052264ae2327338130e19266970a392275811","src/shared/types.rs":"482c73b5a4cd021a1b1ef6ed609fd0bb56ad70640d65fbaff46db1dd51607de4","src/srcgen.rs":"79fee2f603b33f76f7c9c8b9452c745a363d732c40c0814d84001ff3ef805677","src/unique_table.rs":"90b7203b29241a1ede70f0a3e50d96799e0b41d8f7455170d6ffb127f87f3cc3"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"76748196d091ef2992173dccea5430e0d3559811e28fc60062e55c3176df6eb7","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"b123f056d0d458396679c5f7f2a16d2762af0258fcda4ac14b6655a95e5a0022","src/cdsl/ast.rs":"1c9bf45110e47710ea160f30b3f3b00b531a8d91ff365bae856dd9d44b928253","src/cdsl/cpu_modes.rs":"7c59ae347d6f9c769d6356fe49c8406934153eefa59d9bf37c182474fcbb9715","src/cdsl/encodings.rs":"da6fa16c95fe4a586c2d00ef4ccf79b4b0f64cd8923b3ca5a5a527da1e799a4f","src/cdsl/formats.rs":"811dcf12f6e9c5be72f18543675ff52c3996edd029064bd0643f878d2ceca3bd","src/cdsl/instructions.rs":"f4c778bf55c0e273a694ecbb995bc733799d6c5930a64210ff619f0a4a3fa039","src/cdsl/isa.rs":"9b6030a935f69b07726d239c23a78d654566785f1fed61ccefdaf7d4f0a97d0e","src/cdsl/mod.rs":"c85f62a7d8d6bedc81c673b8d02be01181f79f272dbc0325a76d52e7eec732e8","src/cdsl/operands.rs":"1cda258798d861c4f467783b5c70c1202a57f554861017eead6477af2ee34063","src/cdsl/recipes.rs":"9f50f29f243f2ed8dbca3ef8b2722e9671bc164b2956254a95ed62641315eab7","src/cdsl/regs.rs":"8a92798a92b2c34c5e572e03602454c72a896d31ac301f15f43d17c101d4da6e","src/cdsl/settings.rs":"7da3c5a9df8e47ed6ca7f1d820e28eae45e783f3759f6c55730d2f17d88f1013","src/cdsl/type_inference.rs":"695d4fd2720f43569a52f29998bd92562951f0b4e7844cc0a239640e0c7d0b0f","src/cdsl/types.rs":"0db5637fa86052384950d4725b2ae27082e7a6aefa1ce2d450ab4beb7d7c1405","src/cdsl/typevar.rs":"7249fcd7c3cd3645e8489c73595bd5327bedc499bd7bc87f2a68e241fba329c3","src/cdsl/xform.rs":"64e9b70ef1265c0331ee9d71c1a1f33dba3f6975b1639385c34d68456cda0e0e","src/constant_hash.rs":"66d6f42c1e98cd4dbc95009997dc00256b57c11d2c2d9eac63b33e458c68a56f","src/default_map.rs":"8bbd6f4d9f93ef2126538cda633e6d9c80a5c7dc79b8d5a759c8be1fe2dbc605","src/error.rs":"5110a4e3c1e97396ba02d9f5abbb8af4b586f0cc4d33a5c2473f1718cc4bef05","src/gen_binemit.rs":"9024619858124b9ad9ecd232683f768beefffd34248da38c2f2e0210ae187267","src/gen_encodings.rs":"024b6ea8086fa1559333245753e909f306adeab7c8dfdc915610877413cba585","src/gen_inst.rs":"9e8c8e62761f4186fdb274c0c99ea03123d454d79371f9f1b20524c3a41631c0","src/gen_legalizer.rs":"094c1ce664637e54a07d6d928e68458cadbc6e8d83ca5f72e207866f61bbe08f","src/gen_registers.rs":"a544a2b91fafe08639e39e50bea0892fda89fe2f6eaf111b2d5f3e98e4d07b86","src/gen_settings.rs":"765ca86fa735342c1875021c34a92cbc39836ba3ad1c12aa615204372a1f7b61","src/gen_types.rs":"3935da6c6a53f9332e06f74bc3a46270656b4d4231ad28ed2648d7b1d2774e90","src/isa/arm32/mod.rs":"a2500871fb270e5624f5561f24fe510f08708cdca2ab1c3163848b67694f7a7a","src/isa/arm64/mod.rs":"dc210e8dc9f179d87d2c5a72d7795a9e34bb30afb91c8053c362d43635629d19","src/isa/mod.rs":"fce60d19dd3c099ebee3ac5ae64a2bee363f13da9ff5a4960d3c1a0bee71d29a","src/isa/riscv/encodings.rs":"8246543bedcc00f407baf140dcffe8632588fe64f87cf098b4445666b31067ec","src/isa/riscv/mod.rs":"895492f0f379cfb02aba1cbb9759dc489bbb4d51ec40759af9f4d71c45c17a86","src/isa/riscv/recipes.rs":"b1fcada01bac0c46376f50c835c1a33b8d96d2cbb77971b9caa73ffb2ad38b89","src/isa/x86/encodings.rs":"c0f28d4d2eea5e81d9f005d4bf9dac30537153dcc51173ab913ae478523ef642","src/isa/x86/instructions.rs":"eea5fa7dd804d435dfdf289fc47f4bcc77db8dfce068d78d0a428155604b9626","src/isa/x86/legalize.rs":"2617153608816928a298e1ef18aa34cb62ccc1aa717d74b89e574881299d27d4","src/isa/x86/mod.rs":"6ed9f102238c1cb9029ec953e37629767438580cf9df8b4f2d1eace75ecd86fc","src/isa/x86/recipes.rs":"7a25de41b3affc092fa4aaaf195e65c77329072a80a6c9aa7c978732bb53f9b7","src/isa/x86/registers.rs":"c0bc60336a8c8b7b4db92dc623e9419a60af14dd6252f0b19e227e46f7166178","src/isa/x86/settings.rs":"4291933922323544f72a56cf5c89427b6d550ac236fb74d617268242396ad3d3","src/lib.rs":"bc458358fd81d092f368b243d07682259dbed7a336b1eed5bcdf5228368289e9","src/shared/entities.rs":"80b8ff57a09c7b2f9dab755abbcc2738317de474776fe1de0d2a581310aa9af8","src/shared/formats.rs":"20908b1048c5e71a185de6b6ded79cdff2c26ddb38ba7b134b7a27f37e8324f3","src/shared/immediates.rs":"804c4c9ffa2fe55d90279ee158aaa6bd6c7f0c604d63d7457a98e82269cec9a7","src/shared/instructions.rs":"178eb2e3817f7f5a1c2ee4498c55c54d2279843c20f6df7480ef055095503e50","src/shared/legalize.rs":"1c32c28f603b11f89e1ba9c4d301b0b8036fd81254d227836202b84573a9a446","src/shared/mod.rs":"f5bb50d8292e46380afdd83a320cb5d6021e1483741e67b1e277053c28f9b943","src/shared/settings.rs":"a9e43f2928ffb94e2cff784241bbd5f7e33d778ee55644d72708c1127886bc7f","src/shared/types.rs":"482c73b5a4cd021a1b1ef6ed609fd0bb56ad70640d65fbaff46db1dd51607de4","src/srcgen.rs":"79fee2f603b33f76f7c9c8b9452c745a363d732c40c0814d84001ff3ef805677","src/unique_table.rs":"90b7203b29241a1ede70f0a3e50d96799e0b41d8f7455170d6ffb127f87f3cc3"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-codegen-meta/Cargo.toml
+++ b/third_party/rust/cranelift-codegen-meta/Cargo.toml
@@ -1,20 +1,20 @@
 [package]
 name = "cranelift-codegen-meta"
 authors = ["The Cranelift Project Developers"]
-version = "0.37.0"
+version = "0.38.0"
 description = "Metaprogram for cranelift-codegen code generator library"
 license = "Apache-2.0 WITH LLVM-exception"
 repository = "https://github.com/CraneStation/cranelift"
 readme = "README.md"
 edition = "2018"
 
 [dependencies]
-cranelift-entity = { path = "../../cranelift-entity", version = "0.37.0", default-features = false }
+cranelift-entity = { path = "../../cranelift-entity", version = "0.38.0", default-features = false }
 
 [badges]
 maintenance = { status = "experimental" }
 travis-ci = { repository = "CraneStation/cranelift" }
 
 [features]
 default = ["std"]
 std = ["cranelift-entity/std"]
--- a/third_party/rust/cranelift-codegen-meta/src/shared/settings.rs
+++ b/third_party/rust/cranelift-codegen-meta/src/shared/settings.rs
@@ -89,16 +89,39 @@ pub fn define() -> SettingGroup {
     settings.add_bool(
         "enable_atomics",
         "Enable the use of atomic instructions",
         true,
     );
 
     // Settings specific to the `baldrdash` calling convention.
 
+    settings.add_enum(
+        "libcall_call_conv",
+        r#"
+            Defines the calling convention to use for LibCalls call expansion,
+            since it may be different from the ISA default calling convention.
+
+            The default value is to use the same calling convention as the ISA
+            default calling convention.
+
+            This list should be kept in sync with the list of calling
+            conventions available in isa/call_conv.rs.
+        "#,
+        vec![
+            "isa_default",
+            "fast",
+            "cold",
+            "system_v",
+            "windows_fastcall",
+            "baldrdash",
+            "probestack",
+        ],
+    );
+
     settings.add_num(
         "baldrdash_prologue_words",
         r#"
             Number of pointer-sized words pushed by the baldrdash prologue.
 
             Functions with the `baldrdash` calling convention don't generate their
             own prologue and epilogue. They depend on externally generated code
             that pushes a fixed number of words in the prologue and restores them
--- a/third_party/rust/cranelift-codegen/.cargo-checksum.json
+++ b/third_party/rust/cranelift-codegen/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"7af208983b21f3aed799ac6b16318e88362a6a2ea0c5c7e17f92af96387170fd","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"e5127227a7db4a8aa92fa6613ed71801025790e696bb41b0323fb7f3c6f7495a","build.rs":"8587e8557a046dc858f7662f63f1fa54e57ff92ed87b30fbcdfce6feafda9d60","src/abi.rs":"76ee030cf0780fe63ccbf855b1161f215e3e2991cb9abe71ca9aff25e5f1dbc2","src/binemit/memorysink.rs":"3ac1d77f178dabe3b2201735f9c7aa577fcc680424ad9ed435736b76fe583dfa","src/binemit/mod.rs":"6a4b1600ae79c6e7917517bdd09626a1cc24b516fb390ab0a59377c376491a1c","src/binemit/relaxation.rs":"697c4041b7e9605dd09b93be389cc18d54666ce16637383f2916a35788df32c9","src/binemit/shrink.rs":"79a2dcca2633c8449d49bf7cfb1e2fe14b46753a4d52ef412078c53d7742fe86","src/bitset.rs":"d57a79058a31b094b4bbe9d34876a5543286df1e08b5ceadfd05a9efc5a3b1ce","src/cfg_printer.rs":"a84dee96d8c6f4775b8ba022243cf1c0bcf847c6d7ec532698cfd331655aa453","src/constant_hash.rs":"77eb898f7b0a4ae3b8165a03c25fcd23ce9c1d0cd32c793fa6cb31666ce27eb1","src/context.rs":"cf97e03ee2a5eaba6dd84208b937664a825e778aa49da34f5311b1f1c9a70a53","src/cursor.rs":"874d1c7ef6e114d6ae47e0a1f92da7c5cec2e555597558042a3b214da7bc5fe4","src/dbg.rs":"1898d94cff0975815eb348651702e95c8f2f63886501d3b7043ee75668480472","src/dce.rs":"d8ab7c0cac0416f9d75afdacc60ba02e532361ec927c3f8bd171b1a53ec8b31a","src/divconst_magic_numbers.rs":"09691389c9055bef7a0cfd5d77fbfeba4be468d05d981d4de088516a94b8b28e","src/dominator_tree.rs":"7ee4114026011b11d49e48c5a9202970bafe3b22c2074f7c1390b98ebb2edb7a","src/flowgraph.rs":"bf520026c32c5454554d0b078b64a78bd44f3c0f4f198eddf71bcfd78cc963a3","src/fx.rs":"8a5d07487906d8316a179e826fcd817a92a4860686256a6fd9d78ba47c63f330","src/ir/builder.rs":"6f111f2a65dfe27633dcec918f74b767277131a72cd94b7bec2e1ef5c4308d5b","src/ir/condcodes.rs":"5247c8d849e1372d2a22379c33a4a88226ec6187be18ca6f2c4e0f315d812aa8","src/ir/dfg.rs":"632f7bec7d2ac63a4f6bb4c5fb4141b9845d6172e82c40c023a733606935549b","src/ir/entities.rs":"5da0d988588d36c0997c7f6208d37b90be5a36816a7a8df46306b244e0b97219","src/ir/extfunc.rs":"9806734eeb480724481128d8c1de78a3b1f80f1214c20f24131196a0df137872","src/ir/extname.rs":"ed2c0b52cdaecc7f0ba9a894ef9fffe139e09b520e43dcd6f0c887a3d41a31ac","src/ir/function.rs":"397fd1135287302b8945b534c8b8745ff428bf0174e365ca4886ed0006235ab5","src/ir/globalvalue.rs":"906d29f3d4190b811f66fc549c4e0408bdd2807eac98e33c0b3d5c2b876acc02","src/ir/heap.rs":"a59d3e5901412b53c0b53a8cdf10765ff5921de9c410ae9acea226c89827df3c","src/ir/immediates.rs":"2b2ed4df22ccddbec85be96efea8f5afd21dafe6cd90f08b25aa7746529be06c","src/ir/instructions.rs":"3c9ad9ea150ed4526f6ae68bdf1afcdc0ccec7aaa535c7860005343ee5d83309","src/ir/jumptable.rs":"7764abc9aa027a5a89d22059b360372bd9a19686887c5a7830f7637d6f188e1e","src/ir/layout.rs":"bb45eefde16ac9423f637dfcc2796ae7b955a97f38a55f23a19cc45da5decce1","src/ir/libcall.rs":"d39c0a5f5584fc4f682dbe21413bb42da5c9fa20b02092b4c8c60b7ce3b14610","src/ir/memflags.rs":"dbcf3798ab66dc764b73fb7f139a621c54cc6bcc683f1f70a33ed7e8c3486bfd","src/ir/mod.rs":"bb92d69a52e75e9fec4060ed33239d9194d6ff1514d37ec51f5e64223810ecd0","src/ir/progpoint.rs":"49433f22bd6ff3a96ad0733ff612f3617b312e4492b6b663187141966f6aa701","src/ir/sourceloc.rs":"e1442572958b138f7637a14b662200ea9f729a513b77108be2a39745d8c2a0d5","src/ir/stackslot.rs":"effb0d676ef16c321e25da001c0f0ccfe3f57a15873680c4e358c0a19a60cf06","src/ir/table.rs":"dcc3b663a989b2b084402b08dc9a0e928dbd052e194a46a1886cc6f0cf1a5f2c","src/ir/trapcode.rs":"59e223193617b8c1043ddd3a907c6131f2987e8fe0965ebfd9f7c056c064b7c5","src/ir/types.rs":"ccf49cfddcdc377e97b46b8a4fcfd7ac5938d0d9eef3c85ff403339b4d134546","src/ir/valueloc.rs":"1bf764fde5ff8140fa2b59f0192086ed17e9ba7018c74719788b74fc5ca05636","src/isa/arm32/abi.rs":"74775c5f1eb95764e46815fa8b31891416a616fdd212972eb77aead43b3345a9","src/isa/arm32/binemit.rs":"52b2f4b3c6d8683ed6963c1f53907ac57b6aab7cc149464c9a12d6875aa3b5c6","src/isa/arm32/enc_tables.rs":"e94b12af802de59484cab1124d2ac8a88d08433f6e1a476724ed0403f4b5967f","src/isa/arm32/mod.rs":"239eb819224af2c2edaa44355f5491cbab780b7427289a607162f5e0df500da8","src/isa/arm32/registers.rs":"254b568a02480f46bb4967a24a438390231014258f0c159f0a41dbafe8e66d56","src/isa/arm32/settings.rs":"2314460f885c24f9571d640f9737a8e0b7d20ca02bcda1127f878fd3891c0529","src/isa/arm64/abi.rs":"52353ed2e2133dacddaad70a876ecebb9c179c19b911ffa823b5b89d3ee7a17c","src/isa/arm64/binemit.rs":"4465cceb68d03ae4d0fdf188c9b86870fb57b3617d67f0bb7d476e5afb581e81","src/isa/arm64/enc_tables.rs":"73fedf7da610a982e37c76e5211dbce880f77841b71c678b0dab2b9e7217cb7c","src/isa/arm64/mod.rs":"46bc796d3420b252b7af843d60e16e0f51de524b0842c6e6fe1adcbadbb70752","src/isa/arm64/registers.rs":"308cfcfd9ff2191d7656e7350bb36e41803664eb86ae490fb4b4d3549b25b6a2","src/isa/arm64/settings.rs":"5405ce3560b7ba0705ef525c706eb9f1593e901e1767b837c012084397639042","src/isa/call_conv.rs":"833ac811ff78ab8d3a5052165e76c51c6da7686020d95462c18074750fb790ed","src/isa/constraints.rs":"bddb5c68e56b122a53d8be215e41d22ccf8c4563630b1486e6eb31c0d3337565","src/isa/enc_tables.rs":"382e714f9500afc292c563cb66d4c963d6787e58f197b1db242db7a099c22b9a","src/isa/encoding.rs":"7ea5b4400530172f96e263561682886ea6c67e706398d44a83933ef7f0ac98a5","src/isa/mod.rs":"d9019b94d415113c782202a48d6c79d333fa6591539771cdc19e6dbee0047041","src/isa/registers.rs":"2050f34d87dd6e8a3d1cefc6248383ac5a888ec9b6766359edd883714ef03bda","src/isa/riscv/abi.rs":"36557b91ad16a1344c80fbb16a62b46eac88500d76cb9ebcd4eae224dd67b2de","src/isa/riscv/binemit.rs":"0bd76b005b53b71bdb59057a20671fbcd8ab1c37d767bfd4ab0a92d05e192d9a","src/isa/riscv/enc_tables.rs":"8491f2082b24c7dedeb7c36cfd913bf9aeaa0a4c8fc754166e9285f4ae002f40","src/isa/riscv/mod.rs":"44932e33e3a6090bebe3c77e0237378d7808c61a331a7cc84ea22ec2f7f52ccb","src/isa/riscv/registers.rs":"666c2abe1a93db5f1573d1603db6c13c37f3fc877c0c93b64d1b971921bfa950","src/isa/riscv/settings.rs":"f6362769e9bc5be0c12b091e414ce924b0d2053b05b0ae88fef118cb8c68761e","src/isa/stack.rs":"5d30e2e19662ff3b3a641888bbb29640094ccd51b9a73368a056352afee46320","src/isa/x86/abi.rs":"3a3d68d3a96896ac61d674d39443e2ae4fa8eccc7d8b9ed348e047ab66b65d47","src/isa/x86/binemit.rs":"9b7a3a5aacbf382145ac39c2bfd767bdfa92a8d0447a84dcc19672f1db34d047","src/isa/x86/enc_tables.rs":"b67d453834e0fdcc2885dfe359502755514c9190a8699bae2c4b5026f1ab4017","src/isa/x86/mod.rs":"9317837cec64abcf0128184617e7834152cd92825054c4905db65780c85c5902","src/isa/x86/registers.rs":"bed70bbe1f56f3ef03ea7cd1bea68eb911913cb4c8b93167e044dfc639f7f461","src/isa/x86/settings.rs":"d3e403db3507830f79bcc976c17340b57052cf1b50877fcf1a79549f2a054458","src/iterators.rs":"f85f52d3fa707a0eb974c92215b3e976923ce8f9481219f7812e0f2869c2bd37","src/legalizer/boundary.rs":"70a6819cbb116f07d0a09f8e5b0e2fe91145f423ad02e10225f8879ff4e61351","src/legalizer/call.rs":"be6074c64c1a00e5e81159dd94c8401fef62205b22c15e07e0c56cf922554d00","src/legalizer/globalvalue.rs":"59ab09a0faf593c7f228500b3fd3bacfc0412bdcb2864ec3621aa65adc4a5693","src/legalizer/heap.rs":"b83dc83a5876b024db058023692f71d8f910f6b212f34200e9bcf61a19daeb8e","src/legalizer/libcall.rs":"5c5a79709322a6562496c30af775ad255b27af67f7541fc1615f7deb891f8cff","src/legalizer/mod.rs":"aef4a458f3ab26239e8ac18c0c20791875fbd21e02085fa130be057e5f899a68","src/legalizer/split.rs":"13fe4d2cecea166ecdc1ebb11f5254374ee170518f1a61de7ac0a921bc8fb25d","src/legalizer/table.rs":"c36d03525312e3191aba8ee00c26a87c1ea200f9a9a0370f0cc84eeacff71786","src/lib.rs":"1e3a026f3ab6a1d133722227f1f9b8d2cd214e576e31c6c68d252326984dafc5","src/licm.rs":"1c14deeb4c86ef7bde39b16758825a774156609359724583ff8788be658abd8e","src/loop_analysis.rs":"58fc3cc0e700f05e3131ff1b16ff975d4f32a68c790f095d8445bd300356d3c0","src/nan_canonicalization.rs":"9619bb5554791bd1be75ecd98564d6c9f5b65132bc07c5c4d8c210cd79b66f82","src/partition_slice.rs":"bc13504e7658aab565918d965a0b67e941eb572e583870571bc6dbb2b9aad272","src/postopt.rs":"d4c487f0299fb905bb5a5822e38dea6f70afa4d4577288988b5bec8660bc1ba0","src/predicates.rs":"2d62e2f25a6c4327e75716a5f3508edf810c4957d0b20d855ed5a865359c8dfb","src/print_errors.rs":"3fbd77a01b5404a4bbcf2970e44299ad33948c71f4014d92026415daa8325314","src/ref_slice.rs":"421a61323c11858a596d50220487f399e1bcedeff0e8d1b716dd4b3531eb01a5","src/regalloc/affinity.rs":"19cec5495c8bddc1fcc8f3c8659d527a5a3ea373ffcc130e3fbd5b462ef15930","src/regalloc/coalescing.rs":"d83be1c1067eb56f2f8f16380450a327d6495b4c6f1f489ded68271c055aec07","src/regalloc/coloring.rs":"cd64fdf547e1c1a1ddcdf345d27b4ec6dfeaf6d34f69cab70082fb2b47c8e860","src/regalloc/context.rs":"e08cbd0dabaee5d9f78e8c6e3750ef61489c6517da7521043cad8dbb23f31581","src/regalloc/diversion.rs":"d46d733f6d00a8f536d5c7c8b8fc6f348c3d0605dd0ee77e1d8359367ba53347","src/regalloc/live_value_tracker.rs":"28823003dc72e8a4702776a8ab5ffd878712700a272b64376b0de2022e0ee31a","src/regalloc/liveness.rs":"a59673fda65d1e3c0e5b3f4468686d05a389c877bee7b10323264595c3c54677","src/regalloc/liverange.rs":"7a28454e5f70d570db439b966a01ead759b65eb65c5845f9c58bf2f230a5f2ab","src/regalloc/mod.rs":"6254df639f9289fd578e01b7dca99bc9c9e3c6680c6d031405e8df8d0cff31ad","src/regalloc/pressure.rs":"04738e95418f0f858885dfc45183efc3dfb59c8d2ad2fd88bbd9a73a62907730","src/regalloc/register_set.rs":"547c6317e2bee7bc6b68a17667fdb96754f54d90c8f623109dae6c586d306e80","src/regalloc/reload.rs":"ccccb716a694b53103bd4d55efb2323e788c1127469233c17b648fa06baa67b1","src/regalloc/solver.rs":"43eebe2ae7f7e8b5510d161428152503ec37661f5083a36d925b4a247e1bd28a","src/regalloc/spilling.rs":"dff4af64409c9a1db7697423576826c3920c26c892fbdde89ee31c680d672e03","src/regalloc/virtregs.rs":"e5c8da6860ba9495f9396621530347e1dd6fc5b2fae2eb23c171ea77429356f1","src/result.rs":"c10354d615f93caa446c3c8c49d6ba3af762816af470f9c4accf04315cce9753","src/scoped_hash_map.rs":"5afafb3a4039094c3a2aad1582354220d21f399aa50046e7f4a1259e1976597e","src/settings.rs":"8aee9565ec271343c8fa19f2fabb0678d3064058af564d8672919acff1afd661","src/simple_gvn.rs":"c8feb380d4831badc59aa1e65efeafa6702711585817fe5f6b31de6b265fac24","src/simple_preopt.rs":"a06c9fe68659bd2db997d1afde9548a809a42fd7c91d3eb17dedbd82003b7d34","src/stack_layout.rs":"c5de271e296fc424f1a30017620bc88500369c8e553fef6e95beccb9c6640e7c","src/timing.rs":"a6808943eec68f5d3ff32132d40c07c142e6aa8073473561573a013978883e4f","src/topo_order.rs":"b01ed68a7300691f41ac434e58a5267b10a8b4a7056d65035e24aa8a6722122a","src/unreachable_code.rs":"40cc71a02887ee4065c76ce96dda0a363a8cc134ec784fe5ed1f276db76596ce","src/value_label.rs":"40d7cb899793cd084647b9d49c543ab5e24f44143b98d05c9900c732cf58c655","src/verifier/cssa.rs":"e3e1d77b763c0ba82d3b59ab5b4667fd3152d5a08be50b58b0c82f86376bb062","src/verifier/flags.rs":"cac8ba7ed5fe621eaa425112b931423cb5f3523d580fcf0b7536deb252e96186","src/verifier/liveness.rs":"ac3413b464ee8b5aa5928bee724799b9a1e0cbbdce433c819b9d870483ed070a","src/verifier/locations.rs":"04635edc12bc741a23c9318611aac4abcb42f01effbebee6d858f108f9393a44","src/verifier/mod.rs":"95bf36674a3d800b0231487025f7588d14f30740fe071a057a9d107fb219c543","src/write.rs":"e067ac97dffcace67545525c631d6df6930e96700019a1e4723f98e31333fc9a"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"129b52a1c7e542bde0857030c6eb997ae92b33d12f1461a3c45c0c17f03ddfb1","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"e5127227a7db4a8aa92fa6613ed71801025790e696bb41b0323fb7f3c6f7495a","build.rs":"8587e8557a046dc858f7662f63f1fa54e57ff92ed87b30fbcdfce6feafda9d60","src/abi.rs":"76ee030cf0780fe63ccbf855b1161f215e3e2991cb9abe71ca9aff25e5f1dbc2","src/binemit/memorysink.rs":"3ac1d77f178dabe3b2201735f9c7aa577fcc680424ad9ed435736b76fe583dfa","src/binemit/mod.rs":"6a4b1600ae79c6e7917517bdd09626a1cc24b516fb390ab0a59377c376491a1c","src/binemit/relaxation.rs":"538cb74550d5b474feb210735a287f5285c3dab6903683448eb014fa4eb6fd0b","src/binemit/shrink.rs":"79a2dcca2633c8449d49bf7cfb1e2fe14b46753a4d52ef412078c53d7742fe86","src/bitset.rs":"d57a79058a31b094b4bbe9d34876a5543286df1e08b5ceadfd05a9efc5a3b1ce","src/cfg_printer.rs":"a84dee96d8c6f4775b8ba022243cf1c0bcf847c6d7ec532698cfd331655aa453","src/constant_hash.rs":"77eb898f7b0a4ae3b8165a03c25fcd23ce9c1d0cd32c793fa6cb31666ce27eb1","src/context.rs":"a95115c52dcc479e5cefa4e06166b3a7ed2a7acecc987f46f7f8e8c577d7b184","src/cursor.rs":"874d1c7ef6e114d6ae47e0a1f92da7c5cec2e555597558042a3b214da7bc5fe4","src/dbg.rs":"1898d94cff0975815eb348651702e95c8f2f63886501d3b7043ee75668480472","src/dce.rs":"d8ab7c0cac0416f9d75afdacc60ba02e532361ec927c3f8bd171b1a53ec8b31a","src/divconst_magic_numbers.rs":"09691389c9055bef7a0cfd5d77fbfeba4be468d05d981d4de088516a94b8b28e","src/dominator_tree.rs":"7ee4114026011b11d49e48c5a9202970bafe3b22c2074f7c1390b98ebb2edb7a","src/flowgraph.rs":"c975c05949017f0b2efeff85bf82017b6a5779ed92e25f13742aeea76ca95c52","src/fx.rs":"8a5d07487906d8316a179e826fcd817a92a4860686256a6fd9d78ba47c63f330","src/ir/builder.rs":"6f111f2a65dfe27633dcec918f74b767277131a72cd94b7bec2e1ef5c4308d5b","src/ir/condcodes.rs":"5247c8d849e1372d2a22379c33a4a88226ec6187be18ca6f2c4e0f315d812aa8","src/ir/dfg.rs":"632f7bec7d2ac63a4f6bb4c5fb4141b9845d6172e82c40c023a733606935549b","src/ir/entities.rs":"5da0d988588d36c0997c7f6208d37b90be5a36816a7a8df46306b244e0b97219","src/ir/extfunc.rs":"9806734eeb480724481128d8c1de78a3b1f80f1214c20f24131196a0df137872","src/ir/extname.rs":"ed2c0b52cdaecc7f0ba9a894ef9fffe139e09b520e43dcd6f0c887a3d41a31ac","src/ir/function.rs":"68b1ed2a262c691d4459501feb6358e6cb96663b32796c67e94a3f14a146fdd0","src/ir/globalvalue.rs":"906d29f3d4190b811f66fc549c4e0408bdd2807eac98e33c0b3d5c2b876acc02","src/ir/heap.rs":"a59d3e5901412b53c0b53a8cdf10765ff5921de9c410ae9acea226c89827df3c","src/ir/immediates.rs":"2b2ed4df22ccddbec85be96efea8f5afd21dafe6cd90f08b25aa7746529be06c","src/ir/instructions.rs":"3c9ad9ea150ed4526f6ae68bdf1afcdc0ccec7aaa535c7860005343ee5d83309","src/ir/jumptable.rs":"7764abc9aa027a5a89d22059b360372bd9a19686887c5a7830f7637d6f188e1e","src/ir/layout.rs":"bb45eefde16ac9423f637dfcc2796ae7b955a97f38a55f23a19cc45da5decce1","src/ir/libcall.rs":"4702f2b4ab6a6fd7a3319ca9f1df63f328f272dc4905667d29adb9425e944653","src/ir/memflags.rs":"dbcf3798ab66dc764b73fb7f139a621c54cc6bcc683f1f70a33ed7e8c3486bfd","src/ir/mod.rs":"bb92d69a52e75e9fec4060ed33239d9194d6ff1514d37ec51f5e64223810ecd0","src/ir/progpoint.rs":"49433f22bd6ff3a96ad0733ff612f3617b312e4492b6b663187141966f6aa701","src/ir/sourceloc.rs":"e1442572958b138f7637a14b662200ea9f729a513b77108be2a39745d8c2a0d5","src/ir/stackslot.rs":"effb0d676ef16c321e25da001c0f0ccfe3f57a15873680c4e358c0a19a60cf06","src/ir/table.rs":"dcc3b663a989b2b084402b08dc9a0e928dbd052e194a46a1886cc6f0cf1a5f2c","src/ir/trapcode.rs":"59e223193617b8c1043ddd3a907c6131f2987e8fe0965ebfd9f7c056c064b7c5","src/ir/types.rs":"ccf49cfddcdc377e97b46b8a4fcfd7ac5938d0d9eef3c85ff403339b4d134546","src/ir/valueloc.rs":"1bf764fde5ff8140fa2b59f0192086ed17e9ba7018c74719788b74fc5ca05636","src/isa/arm32/abi.rs":"74775c5f1eb95764e46815fa8b31891416a616fdd212972eb77aead43b3345a9","src/isa/arm32/binemit.rs":"52b2f4b3c6d8683ed6963c1f53907ac57b6aab7cc149464c9a12d6875aa3b5c6","src/isa/arm32/enc_tables.rs":"e94b12af802de59484cab1124d2ac8a88d08433f6e1a476724ed0403f4b5967f","src/isa/arm32/mod.rs":"239eb819224af2c2edaa44355f5491cbab780b7427289a607162f5e0df500da8","src/isa/arm32/registers.rs":"254b568a02480f46bb4967a24a438390231014258f0c159f0a41dbafe8e66d56","src/isa/arm32/settings.rs":"2314460f885c24f9571d640f9737a8e0b7d20ca02bcda1127f878fd3891c0529","src/isa/arm64/abi.rs":"52353ed2e2133dacddaad70a876ecebb9c179c19b911ffa823b5b89d3ee7a17c","src/isa/arm64/binemit.rs":"4465cceb68d03ae4d0fdf188c9b86870fb57b3617d67f0bb7d476e5afb581e81","src/isa/arm64/enc_tables.rs":"73fedf7da610a982e37c76e5211dbce880f77841b71c678b0dab2b9e7217cb7c","src/isa/arm64/mod.rs":"46bc796d3420b252b7af843d60e16e0f51de524b0842c6e6fe1adcbadbb70752","src/isa/arm64/registers.rs":"308cfcfd9ff2191d7656e7350bb36e41803664eb86ae490fb4b4d3549b25b6a2","src/isa/arm64/settings.rs":"5405ce3560b7ba0705ef525c706eb9f1593e901e1767b837c012084397639042","src/isa/call_conv.rs":"cda0a77e318c8f5f55f8b44459300502141bc1b3f0c8127d94c5ecce9585da4a","src/isa/constraints.rs":"bddb5c68e56b122a53d8be215e41d22ccf8c4563630b1486e6eb31c0d3337565","src/isa/enc_tables.rs":"382e714f9500afc292c563cb66d4c963d6787e58f197b1db242db7a099c22b9a","src/isa/encoding.rs":"7ea5b4400530172f96e263561682886ea6c67e706398d44a83933ef7f0ac98a5","src/isa/mod.rs":"d9019b94d415113c782202a48d6c79d333fa6591539771cdc19e6dbee0047041","src/isa/registers.rs":"2050f34d87dd6e8a3d1cefc6248383ac5a888ec9b6766359edd883714ef03bda","src/isa/riscv/abi.rs":"36557b91ad16a1344c80fbb16a62b46eac88500d76cb9ebcd4eae224dd67b2de","src/isa/riscv/binemit.rs":"0bd76b005b53b71bdb59057a20671fbcd8ab1c37d767bfd4ab0a92d05e192d9a","src/isa/riscv/enc_tables.rs":"8491f2082b24c7dedeb7c36cfd913bf9aeaa0a4c8fc754166e9285f4ae002f40","src/isa/riscv/mod.rs":"44932e33e3a6090bebe3c77e0237378d7808c61a331a7cc84ea22ec2f7f52ccb","src/isa/riscv/registers.rs":"666c2abe1a93db5f1573d1603db6c13c37f3fc877c0c93b64d1b971921bfa950","src/isa/riscv/settings.rs":"f6362769e9bc5be0c12b091e414ce924b0d2053b05b0ae88fef118cb8c68761e","src/isa/stack.rs":"5d30e2e19662ff3b3a641888bbb29640094ccd51b9a73368a056352afee46320","src/isa/x86/abi.rs":"3a3d68d3a96896ac61d674d39443e2ae4fa8eccc7d8b9ed348e047ab66b65d47","src/isa/x86/binemit.rs":"9b7a3a5aacbf382145ac39c2bfd767bdfa92a8d0447a84dcc19672f1db34d047","src/isa/x86/enc_tables.rs":"b67d453834e0fdcc2885dfe359502755514c9190a8699bae2c4b5026f1ab4017","src/isa/x86/mod.rs":"9317837cec64abcf0128184617e7834152cd92825054c4905db65780c85c5902","src/isa/x86/registers.rs":"bed70bbe1f56f3ef03ea7cd1bea68eb911913cb4c8b93167e044dfc639f7f461","src/isa/x86/settings.rs":"d3e403db3507830f79bcc976c17340b57052cf1b50877fcf1a79549f2a054458","src/iterators.rs":"f85f52d3fa707a0eb974c92215b3e976923ce8f9481219f7812e0f2869c2bd37","src/legalizer/boundary.rs":"70a6819cbb116f07d0a09f8e5b0e2fe91145f423ad02e10225f8879ff4e61351","src/legalizer/call.rs":"be6074c64c1a00e5e81159dd94c8401fef62205b22c15e07e0c56cf922554d00","src/legalizer/globalvalue.rs":"59ab09a0faf593c7f228500b3fd3bacfc0412bdcb2864ec3621aa65adc4a5693","src/legalizer/heap.rs":"b83dc83a5876b024db058023692f71d8f910f6b212f34200e9bcf61a19daeb8e","src/legalizer/libcall.rs":"4869925d75b3f05128a06ee55a2fae94b23b80186ee8c5493de343fffac7b49d","src/legalizer/mod.rs":"aef4a458f3ab26239e8ac18c0c20791875fbd21e02085fa130be057e5f899a68","src/legalizer/split.rs":"13fe4d2cecea166ecdc1ebb11f5254374ee170518f1a61de7ac0a921bc8fb25d","src/legalizer/table.rs":"c36d03525312e3191aba8ee00c26a87c1ea200f9a9a0370f0cc84eeacff71786","src/lib.rs":"1e3a026f3ab6a1d133722227f1f9b8d2cd214e576e31c6c68d252326984dafc5","src/licm.rs":"07534926e2986c01e3b76a2faf2336f5b96411f06d93d44d74e991f76265e29d","src/loop_analysis.rs":"58fc3cc0e700f05e3131ff1b16ff975d4f32a68c790f095d8445bd300356d3c0","src/nan_canonicalization.rs":"9619bb5554791bd1be75ecd98564d6c9f5b65132bc07c5c4d8c210cd79b66f82","src/partition_slice.rs":"bc13504e7658aab565918d965a0b67e941eb572e583870571bc6dbb2b9aad272","src/postopt.rs":"d4c487f0299fb905bb5a5822e38dea6f70afa4d4577288988b5bec8660bc1ba0","src/predicates.rs":"2d62e2f25a6c4327e75716a5f3508edf810c4957d0b20d855ed5a865359c8dfb","src/print_errors.rs":"3fbd77a01b5404a4bbcf2970e44299ad33948c71f4014d92026415daa8325314","src/ref_slice.rs":"421a61323c11858a596d50220487f399e1bcedeff0e8d1b716dd4b3531eb01a5","src/regalloc/affinity.rs":"19cec5495c8bddc1fcc8f3c8659d527a5a3ea373ffcc130e3fbd5b462ef15930","src/regalloc/coalescing.rs":"d83be1c1067eb56f2f8f16380450a327d6495b4c6f1f489ded68271c055aec07","src/regalloc/coloring.rs":"cd64fdf547e1c1a1ddcdf345d27b4ec6dfeaf6d34f69cab70082fb2b47c8e860","src/regalloc/context.rs":"e08cbd0dabaee5d9f78e8c6e3750ef61489c6517da7521043cad8dbb23f31581","src/regalloc/diversion.rs":"d46d733f6d00a8f536d5c7c8b8fc6f348c3d0605dd0ee77e1d8359367ba53347","src/regalloc/live_value_tracker.rs":"28823003dc72e8a4702776a8ab5ffd878712700a272b64376b0de2022e0ee31a","src/regalloc/liveness.rs":"a59673fda65d1e3c0e5b3f4468686d05a389c877bee7b10323264595c3c54677","src/regalloc/liverange.rs":"7a28454e5f70d570db439b966a01ead759b65eb65c5845f9c58bf2f230a5f2ab","src/regalloc/mod.rs":"6254df639f9289fd578e01b7dca99bc9c9e3c6680c6d031405e8df8d0cff31ad","src/regalloc/pressure.rs":"04738e95418f0f858885dfc45183efc3dfb59c8d2ad2fd88bbd9a73a62907730","src/regalloc/register_set.rs":"547c6317e2bee7bc6b68a17667fdb96754f54d90c8f623109dae6c586d306e80","src/regalloc/reload.rs":"ccccb716a694b53103bd4d55efb2323e788c1127469233c17b648fa06baa67b1","src/regalloc/solver.rs":"43eebe2ae7f7e8b5510d161428152503ec37661f5083a36d925b4a247e1bd28a","src/regalloc/spilling.rs":"dff4af64409c9a1db7697423576826c3920c26c892fbdde89ee31c680d672e03","src/regalloc/virtregs.rs":"e5c8da6860ba9495f9396621530347e1dd6fc5b2fae2eb23c171ea77429356f1","src/result.rs":"c10354d615f93caa446c3c8c49d6ba3af762816af470f9c4accf04315cce9753","src/scoped_hash_map.rs":"5afafb3a4039094c3a2aad1582354220d21f399aa50046e7f4a1259e1976597e","src/settings.rs":"0524624cde4d689743daf360af43a6f8a0ba69bf136253a01a4d8f4555b1bdab","src/simple_gvn.rs":"c8feb380d4831badc59aa1e65efeafa6702711585817fe5f6b31de6b265fac24","src/simple_preopt.rs":"a06c9fe68659bd2db997d1afde9548a809a42fd7c91d3eb17dedbd82003b7d34","src/stack_layout.rs":"c5de271e296fc424f1a30017620bc88500369c8e553fef6e95beccb9c6640e7c","src/timing.rs":"a6808943eec68f5d3ff32132d40c07c142e6aa8073473561573a013978883e4f","src/topo_order.rs":"b01ed68a7300691f41ac434e58a5267b10a8b4a7056d65035e24aa8a6722122a","src/unreachable_code.rs":"40cc71a02887ee4065c76ce96dda0a363a8cc134ec784fe5ed1f276db76596ce","src/value_label.rs":"40d7cb899793cd084647b9d49c543ab5e24f44143b98d05c9900c732cf58c655","src/verifier/cssa.rs":"e3e1d77b763c0ba82d3b59ab5b4667fd3152d5a08be50b58b0c82f86376bb062","src/verifier/flags.rs":"cac8ba7ed5fe621eaa425112b931423cb5f3523d580fcf0b7536deb252e96186","src/verifier/liveness.rs":"ac3413b464ee8b5aa5928bee724799b9a1e0cbbdce433c819b9d870483ed070a","src/verifier/locations.rs":"04635edc12bc741a23c9318611aac4abcb42f01effbebee6d858f108f9393a44","src/verifier/mod.rs":"95bf36674a3d800b0231487025f7588d14f30740fe071a057a9d107fb219c543","src/write.rs":"e067ac97dffcace67545525c631d6df6930e96700019a1e4723f98e31333fc9a"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-codegen/Cargo.toml
+++ b/third_party/rust/cranelift-codegen/Cargo.toml
@@ -1,38 +1,38 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-codegen"
-version = "0.37.0"
+version = "0.38.0"
 description = "Low-level code generator library"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://cranelift.readthedocs.io/"
 repository = "https://github.com/CraneStation/cranelift"
 categories = ["no-std"]
 readme = "README.md"
 keywords = ["compile", "compiler", "jit"]
 build = "build.rs"
 edition = "2018"
 
 [dependencies]
-cranelift-entity = { path = "../cranelift-entity", version = "0.37.0", default-features = false }
-cranelift-bforest = { path = "../cranelift-bforest", version = "0.37.0", default-features = false }
+cranelift-entity = { path = "../cranelift-entity", version = "0.38.0", default-features = false }
+cranelift-bforest = { path = "../cranelift-bforest", version = "0.38.0", default-features = false }
 failure = { version = "0.1.1", default-features = false, features = ["derive"] }
 failure_derive = { version = "0.1.1", default-features = false }
 hashmap_core = { version = "0.1.9", optional = true }
 target-lexicon = { version = "0.4.0", default-features = false }
 log = { version = "0.4.6", default-features = false }
 serde = { version = "1.0.94", features = ["derive"], optional = true }
 # It is a goal of the cranelift-codegen crate to have minimal external dependencies.
 # Please don't add any unless they are essential to the task of creating binary
 # machine code. Integration tests that need external dependencies can be
 # accomodated in `tests`.
 
 [build-dependencies]
-cranelift-codegen-meta = { path = "meta", version = "0.37.0", default-features = false }
+cranelift-codegen-meta = { path = "meta", version = "0.38.0", default-features = false }
 
 [features]
 default = ["std", "x86", "arm32", "arm64", "riscv"]
 
 # The "std" feature enables use of libstd. The "core" feature enables use
 # of some minimal std-like replacement libraries. At least one of these two
 # features need to be enabled.
 std = [
--- a/third_party/rust/cranelift-codegen/src/binemit/relaxation.rs
+++ b/third_party/rust/cranelift-codegen/src/binemit/relaxation.rs
@@ -24,37 +24,47 @@
 //! ```clif
 //!     brnz v1, ebb23
 //!     jump ebb17
 //! ebb23:
 //! ```
 
 use crate::binemit::{CodeInfo, CodeOffset};
 use crate::cursor::{Cursor, FuncCursor};
-use crate::ir::{Function, InstructionData, Opcode};
+use crate::dominator_tree::DominatorTree;
+use crate::flowgraph::ControlFlowGraph;
+use crate::ir::{Ebb, Function, Inst, InstructionData, Opcode, Value, ValueList};
 use crate::isa::{EncInfo, TargetIsa};
 use crate::iterators::IteratorExtras;
 use crate::regalloc::RegDiversions;
 use crate::timing;
 use crate::CodegenResult;
 use log::debug;
 
 /// Relax branches and compute the final layout of EBB headers in `func`.
 ///
 /// Fill in the `func.offsets` table so the function is ready for binary emission.
-pub fn relax_branches(func: &mut Function, isa: &dyn TargetIsa) -> CodegenResult<CodeInfo> {
+pub fn relax_branches(
+    func: &mut Function,
+    cfg: &mut ControlFlowGraph,
+    domtree: &mut DominatorTree,
+    isa: &dyn TargetIsa,
+) -> CodegenResult<CodeInfo> {
     let _tt = timing::relax_branches();
 
     let encinfo = isa.encoding_info();
 
     // Clear all offsets so we can recognize EBBs that haven't been visited yet.
     func.offsets.clear();
     func.offsets.resize(func.dfg.num_ebbs());
 
-    // Start by inserting fall through instructions.
+    // Start by removing redundant jumps.
+    fold_redundant_jumps(func, cfg, domtree);
+
+    // Convert jumps to fallthrough instructions where possible.
     fallthroughs(func);
 
     let mut offset = 0;
     let mut divert = RegDiversions::new();
 
     // First, compute initial offsets for every EBB.
     {
         let mut cur = FuncCursor::new(func);
@@ -74,17 +84,16 @@ pub fn relax_branches(func: &mut Functio
     while go_again {
         go_again = false;
         offset = 0;
 
         // Visit all instructions in layout order.
         let mut cur = FuncCursor::new(func);
         while let Some(ebb) = cur.next_ebb() {
             divert.clear();
-
             // Record the offset for `ebb` and make sure we iterate until offsets are stable.
             if cur.func.offsets[ebb] != offset {
                 cur.func.offsets[ebb] = offset;
                 go_again = true;
             }
 
             while let Some(inst) = cur.next_inst() {
                 divert.apply(&cur.func.dfg[inst]);
@@ -129,16 +138,141 @@ pub fn relax_branches(func: &mut Functio
     Ok(CodeInfo {
         code_size,
         jumptables_size,
         rodata_size,
         total_size: offset,
     })
 }
 
+/// Folds an instruction if it is a redundant jump.
+/// Returns whether folding was performed (which invalidates the CFG).
+fn try_fold_redundant_jump(
+    func: &mut Function,
+    cfg: &mut ControlFlowGraph,
+    ebb: Ebb,
+    first_inst: Inst,
+) -> bool {
+    let first_dest = match func.dfg[first_inst].branch_destination() {
+        Some(ebb) => ebb, // The instruction was a single-target branch.
+        None => {
+            return false; // The instruction was either multi-target or not a branch.
+        }
+    };
+
+    // Look at the first instruction of the first branch's destination.
+    // If it is an unconditional branch, maybe the second jump can be bypassed.
+    let second_inst = func.layout.first_inst(first_dest).expect("Instructions");
+    if func.dfg[second_inst].opcode() != Opcode::Jump {
+        return false;
+    }
+
+    // Now we need to fix up first_inst's ebb parameters to match second_inst's,
+    // without changing the branch-specific arguments.
+    //
+    // The intermediary block is allowed to reference any SSA value that dominates it,
+    // but that SSA value may not necessarily also dominate the instruction that's
+    // being patched.
+
+    // Get the arguments and parameters passed by the first branch.
+    let num_fixed = func.dfg[first_inst]
+        .opcode()
+        .constraints()
+        .num_fixed_value_arguments();
+    let (first_args, first_params) = func.dfg[first_inst]
+        .arguments(&func.dfg.value_lists)
+        .split_at(num_fixed);
+
+    // Get the parameters passed by the second jump.
+    let num_fixed = func.dfg[second_inst]
+        .opcode()
+        .constraints()
+        .num_fixed_value_arguments();
+    let (_, second_params) = func.dfg[second_inst]
+        .arguments(&func.dfg.value_lists)
+        .split_at(num_fixed);
+    let mut second_params = second_params.to_vec(); // Clone for rewriting below.
+
+    // For each parameter passed by the second jump, if any of those parameters
+    // was a block parameter, rewrite it to refer to the value that the first jump
+    // passed in its parameters. Otherwise, make sure it dominates first_inst.
+    //
+    // For example: if we `ebb0: jump ebb1(v1)` to `ebb1(v2): jump ebb2(v2)`,
+    // we want to rewrite the original jump to `jump ebb2(v1)`.
+    let ebb_params: &[Value] = func.dfg.ebb_params(first_dest);
+    debug_assert!(ebb_params.len() == first_params.len());
+
+    for value in second_params.iter_mut() {
+        if let Some((n, _)) = ebb_params.iter().enumerate().find(|(_, &p)| p == *value) {
+            // This value was the Nth parameter passed to the second_inst's ebb.
+            // Rewrite it as the Nth parameter passed by first_inst.
+            *value = first_params[n];
+        }
+    }
+
+    // Build a value list of first_args (unchanged) followed by second_params (rewritten).
+    let arguments_vec: std::vec::Vec<_> = first_args
+        .iter()
+        .chain(second_params.iter())
+        .map(|x| *x)
+        .collect();
+    let value_list = ValueList::from_slice(&arguments_vec, &mut func.dfg.value_lists);
+
+    func.dfg[first_inst].take_value_list(); // Drop the current list.
+    func.dfg[first_inst].put_value_list(value_list); // Put the new list.
+
+    // Bypass the second jump.
+    // This can disconnect the Ebb containing `second_inst`, to be cleaned up later.
+    let second_dest = func.dfg[second_inst].branch_destination().expect("Dest");
+    func.change_branch_destination(first_inst, second_dest);
+    cfg.recompute_ebb(func, ebb);
+
+    // The previously-intermediary Ebb may now be unreachable. Update CFG.
+    if cfg.pred_iter(first_dest).count() == 0 {
+        // Remove all instructions from that ebb.
+        while let Some(inst) = func.layout.first_inst(first_dest) {
+            func.layout.remove_inst(inst);
+        }
+
+        // Remove the block...
+        cfg.recompute_ebb(func, first_dest); // ...from predecessor lists.
+        func.layout.remove_ebb(first_dest); // ...from the layout.
+    }
+
+    return true;
+}
+
+/// Redirects `jump` instructions that point to other `jump` instructions to the final destination.
+/// This transformation may orphan some blocks.
+fn fold_redundant_jumps(
+    func: &mut Function,
+    cfg: &mut ControlFlowGraph,
+    domtree: &mut DominatorTree,
+) {
+    let mut folded = false;
+
+    // Postorder iteration guarantees that a chain of jumps is visited from
+    // the end of the chain to the start of the chain.
+    for &ebb in domtree.cfg_postorder() {
+        // Only proceed if the first terminator instruction is a single-target branch.
+        let first_inst = func.layout.last_inst(ebb).expect("Ebb has no terminator");
+        folded |= try_fold_redundant_jump(func, cfg, ebb, first_inst);
+
+        // Also try the previous instruction.
+        if let Some(prev_inst) = func.layout.prev_inst(first_inst) {
+            folded |= try_fold_redundant_jump(func, cfg, ebb, prev_inst);
+        }
+    }
+
+    // Folding jumps invalidates the dominator tree.
+    if folded {
+        domtree.compute(func, cfg);
+    }
+}
+
 /// Convert `jump` instructions to `fallthrough` instructions where possible and verify that any
 /// existing `fallthrough` instructions are correct.
 fn fallthroughs(func: &mut Function) {
     for (ebb, succ) in func.layout.ebbs().adjacent_pairs() {
         let term = func.layout.last_inst(ebb).expect("EBB has no terminator.");
         if let InstructionData::Jump {
             ref mut opcode,
             destination,
--- a/third_party/rust/cranelift-codegen/src/context.rs
+++ b/third_party/rust/cranelift-codegen/src/context.rs
@@ -324,17 +324,17 @@ impl Context {
         self.verify_if(isa)?;
         self.verify_locations_if(isa)?;
         Ok(())
     }
 
     /// Run the branch relaxation pass and return information about the function's code and
     /// read-only data.
     pub fn relax_branches(&mut self, isa: &dyn TargetIsa) -> CodegenResult<CodeInfo> {
-        let info = relax_branches(&mut self.func, isa)?;
+        let info = relax_branches(&mut self.func, &mut self.cfg, &mut self.domtree, isa)?;
         self.verify_if(isa)?;
         self.verify_locations_if(isa)?;
         Ok(info)
     }
 
     /// Builds ranges and location for specified value labels.
     pub fn build_value_labels_ranges(
         &self,
--- a/third_party/rust/cranelift-codegen/src/flowgraph.rs
+++ b/third_party/rust/cranelift-codegen/src/flowgraph.rs
@@ -26,17 +26,17 @@
 use crate::bforest;
 use crate::entity::SecondaryMap;
 use crate::ir::instructions::BranchInfo;
 use crate::ir::{Ebb, Function, Inst};
 use crate::timing;
 use core::mem;
 
 /// A basic block denoted by its enclosing Ebb and last instruction.
-#[derive(PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq)]
 pub struct BasicBlock {
     /// Enclosing Ebb key.
     pub ebb: Ebb,
     /// Last instruction in the basic block.
     pub inst: Inst,
 }
 
 impl BasicBlock {
--- a/third_party/rust/cranelift-codegen/src/ir/function.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/function.rs
@@ -3,29 +3,29 @@
 //! The `Function` struct defined in this module owns all of its extended basic blocks and
 //! instructions.
 
 use crate::binemit::CodeOffset;
 use crate::entity::{PrimaryMap, SecondaryMap};
 use crate::ir;
 use crate::ir::{DataFlowGraph, ExternalName, Layout, Signature};
 use crate::ir::{
-    Ebb, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Heap, HeapData, JumpTable,
+    Ebb, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Heap, HeapData, Inst, JumpTable,
     JumpTableData, SigRef, StackSlot, StackSlotData, Table, TableData,
 };
 use crate::ir::{EbbOffsets, InstEncodings, SourceLocs, StackSlots, ValueLocations};
 use crate::ir::{JumpTableOffsets, JumpTables};
 use crate::isa::{CallConv, EncInfo, Encoding, Legalize, TargetIsa};
 use crate::regalloc::RegDiversions;
 use crate::value_label::ValueLabelsRanges;
 use crate::write::write_function;
 use core::fmt;
 
 #[cfg(feature = "basic-blocks")]
-use crate::ir::{Inst, Opcode};
+use crate::ir::Opcode;
 
 /// A function.
 ///
 /// Functions can be cloned, but it is not a very fast operation.
 /// The clone will have all the same entity numbers as the original.
 #[derive(Clone)]
 pub struct Function {
     /// Name of this function. Mostly used by `.clif` files.
@@ -218,16 +218,25 @@ impl Function {
         isa.encode(&self, &self.dfg[inst], self.dfg.ctrl_typevar(inst))
     }
 
     /// Starts collection of debug information.
     pub fn collect_debug_info(&mut self) {
         self.dfg.collect_debug_info();
     }
 
+    /// Changes the destination of a jump or branch instruction.
+    /// Does nothing if called with a non-jump or non-branch instruction.
+    pub fn change_branch_destination(&mut self, inst: Inst, new_dest: Ebb) {
+        match self.dfg[inst].branch_destination_mut() {
+            None => (),
+            Some(inst_dest) => *inst_dest = new_dest,
+        }
+    }
+
     /// Checks that the specified EBB can be encoded as a basic block.
     ///
     /// On error, returns the first invalid instruction and an error message.
     #[cfg(feature = "basic-blocks")]
     pub fn is_ebb_basic(&self, ebb: Ebb) -> Result<(), (Inst, &'static str)> {
         let dfg = &self.dfg;
         let inst_iter = self.layout.ebb_insts(ebb);
 
--- a/third_party/rust/cranelift-codegen/src/ir/libcall.rs
+++ b/third_party/rust/cranelift-codegen/src/ir/libcall.rs
@@ -103,21 +103,23 @@ impl LibCall {
 }
 
 /// Get a function reference for `libcall` in `func`, following the signature
 /// for `inst`.
 ///
 /// If there is an existing reference, use it, otherwise make a new one.
 pub fn get_libcall_funcref(
     libcall: LibCall,
+    call_conv: CallConv,
     func: &mut Function,
     inst: Inst,
     isa: &dyn TargetIsa,
 ) -> FuncRef {
-    find_funcref(libcall, func).unwrap_or_else(|| make_funcref_for_inst(libcall, func, inst, isa))
+    find_funcref(libcall, func)
+        .unwrap_or_else(|| make_funcref_for_inst(libcall, call_conv, func, inst, isa))
 }
 
 /// Get a function reference for the probestack function in `func`.
 ///
 /// If there is an existing reference, use it, otherwise make a new one.
 pub fn get_probestack_funcref(
     func: &mut Function,
     reg_type: Type,
@@ -159,28 +161,37 @@ fn make_funcref_for_probestack(
         sig.returns.push(rax);
     }
     make_funcref(LibCall::Probestack, func, sig, isa)
 }
 
 /// Create a funcref for `libcall` with a signature matching `inst`.
 fn make_funcref_for_inst(
     libcall: LibCall,
+    call_conv: CallConv,
     func: &mut Function,
     inst: Inst,
     isa: &dyn TargetIsa,
 ) -> FuncRef {
-    let mut sig = Signature::new(isa.default_call_conv());
+    let mut sig = Signature::new(call_conv);
     for &v in func.dfg.inst_args(inst) {
         sig.params.push(AbiParam::new(func.dfg.value_type(v)));
     }
     for &v in func.dfg.inst_results(inst) {
         sig.returns.push(AbiParam::new(func.dfg.value_type(v)));
     }
 
+    if call_conv == CallConv::Baldrdash {
+        // Adds the special VMContext parameter to the signature.
+        sig.params.push(AbiParam::special(
+            isa.pointer_type(),
+            ArgumentPurpose::VMContext,
+        ));
+    }
+
     make_funcref(libcall, func, sig, isa)
 }
 
 /// Create a funcref for `libcall`.
 fn make_funcref(
     libcall: LibCall,
     func: &mut Function,
     sig: Signature,
--- a/third_party/rust/cranelift-codegen/src/isa/call_conv.rs
+++ b/third_party/rust/cranelift-codegen/src/isa/call_conv.rs
@@ -1,8 +1,10 @@
+use crate::isa::TargetIsa;
+use crate::settings::LibcallCallConv;
 use core::fmt;
 use core::str;
 use target_lexicon::{CallingConvention, Triple};
 
 /// Calling convention identifiers.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum CallConv {
     /// Best performance, not ABI-stable
@@ -24,16 +26,29 @@ impl CallConv {
     pub fn triple_default(triple: &Triple) -> Self {
         match triple.default_calling_convention() {
             // Default to System V for unknown targets because most everything
             // uses System V.
             Ok(CallingConvention::SystemV) | Err(()) => CallConv::SystemV,
             Ok(CallingConvention::WindowsFastcall) => CallConv::WindowsFastcall,
         }
     }
+
+    /// Returns the calling convention used for libcalls for the given ISA.
+    pub fn for_libcall(isa: &dyn TargetIsa) -> Self {
+        match isa.flags().libcall_call_conv() {
+            LibcallCallConv::IsaDefault => isa.default_call_conv(),
+            LibcallCallConv::Fast => CallConv::Fast,
+            LibcallCallConv::Cold => CallConv::Cold,
+            LibcallCallConv::SystemV => CallConv::SystemV,
+            LibcallCallConv::WindowsFastcall => CallConv::WindowsFastcall,
+            LibcallCallConv::Baldrdash => CallConv::Baldrdash,
+            LibcallCallConv::Probestack => CallConv::Probestack,
+        }
+    }
 }
 
 impl fmt::Display for CallConv {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         f.write_str(match *self {
             CallConv::Fast => "fast",
             CallConv::Cold => "cold",
             CallConv::SystemV => "system_v",
--- a/third_party/rust/cranelift-codegen/src/legalizer/libcall.rs
+++ b/third_party/rust/cranelift-codegen/src/legalizer/libcall.rs
@@ -1,30 +1,39 @@
 //! Expanding instructions as runtime library calls.
 
 use crate::ir;
 use crate::ir::{get_libcall_funcref, InstBuilder};
-use crate::isa::TargetIsa;
+use crate::isa::{CallConv, TargetIsa};
 use crate::legalizer::boundary::legalize_libcall_signature;
 use std::vec::Vec;
 
 /// Try to expand `inst` as a library call, returning true is successful.
 pub fn expand_as_libcall(inst: ir::Inst, func: &mut ir::Function, isa: &dyn TargetIsa) -> bool {
     // Does the opcode/ctrl_type combo even have a well-known runtime library name.
     let libcall = match ir::LibCall::for_inst(func.dfg[inst].opcode(), func.dfg.ctrl_typevar(inst))
     {
         Some(lc) => lc,
         None => return false,
     };
 
     // Now we convert `inst` to a call. First save the arguments.
     let mut args = Vec::new();
     args.extend_from_slice(func.dfg.inst_args(inst));
+
+    let call_conv = CallConv::for_libcall(isa);
+    if call_conv == CallConv::Baldrdash {
+        let vmctx = func
+            .special_param(ir::ArgumentPurpose::VMContext)
+            .expect("Missing vmctx parameter for baldrdash libcall");
+        args.push(vmctx);
+    }
+
     // The replace builder will preserve the instruction result values.
-    let funcref = get_libcall_funcref(libcall, func, inst, isa);
+    let funcref = get_libcall_funcref(libcall, call_conv, func, inst, isa);
     func.dfg.replace(inst).call(funcref, &args);
 
     // Ask the ISA to legalize the signature.
     let fn_data = &func.dfg.ext_funcs[funcref];
     let sig_data = &mut func.dfg.signatures[fn_data.signature];
     legalize_libcall_signature(sig_data, isa);
 
     true
--- a/third_party/rust/cranelift-codegen/src/licm.rs
+++ b/third_party/rust/cranelift-codegen/src/licm.rs
@@ -83,17 +83,17 @@ fn create_pre_header(
         pre_header_args_value.push(func.dfg.append_ebb_param(pre_header, typ), pool);
     }
     for BasicBlock {
         inst: last_inst, ..
     } in cfg.pred_iter(header)
     {
         // We only follow normal edges (not the back edges)
         if !domtree.dominates(header, last_inst, &func.layout) {
-            change_branch_jump_destination(last_inst, pre_header, func);
+            func.change_branch_destination(last_inst, pre_header);
         }
     }
     {
         let mut pos = EncCursor::new(func, isa).at_top(header);
         // Inserts the pre-header at the right place in the layout.
         pos.insert_ebb(pre_header);
         pos.next_inst();
         pos.ins().jump(header, pre_header_args_value.as_slice(pool));
@@ -131,25 +131,16 @@ fn has_pre_header(
                 return None;
             }
             result = Some((pred_ebb, branch_inst));
         }
     }
     result
 }
 
-// Change the destination of a jump or branch instruction. Does nothing if called with a non-jump
-// or non-branch instruction.
-fn change_branch_jump_destination(inst: Inst, new_ebb: Ebb, func: &mut Function) {
-    match func.dfg[inst].branch_destination_mut() {
-        None => (),
-        Some(instruction_dest) => *instruction_dest = new_ebb,
-    }
-}
-
 /// Test whether the given opcode is unsafe to even consider for LICM.
 fn trivially_unsafe_for_licm(opcode: Opcode) -> bool {
     opcode.can_store()
         || opcode.is_call()
         || opcode.is_branch()
         || opcode.is_terminator()
         || opcode.is_return()
         || opcode.can_trap()
--- a/third_party/rust/cranelift-codegen/src/settings.rs
+++ b/third_party/rust/cranelift-codegen/src/settings.rs
@@ -374,16 +374,17 @@ mod tests {
     #[test]
     fn display_default() {
         let b = builder();
         let f = Flags::new(b);
         assert_eq!(
             f.to_string(),
             "[shared]\n\
              opt_level = \"default\"\n\
+             libcall_call_conv = \"isa_default\"\n\
              baldrdash_prologue_words = 0\n\
              probestack_size_log2 = 12\n\
              enable_verifier = true\n\
              is_pic = false\n\
              colocated_libcalls = false\n\
              avoid_div_traps = false\n\
              enable_float = true\n\
              enable_nan_canonicalization = false\n\
--- a/third_party/rust/cranelift-entity/.cargo-checksum.json
+++ b/third_party/rust/cranelift-entity/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"9dadff09b5be93836c6fb489b54f31b94a27f5db28b4c72db755506da7623453","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"96ceffbfd88fb06e3b41aa4d3087cffbbf8441d04eba7ab09662a72ab600a321","src/boxed_slice.rs":"687428ee0442013c0d5962dd78d0964830233bc4cb19aa530d30da0f1dc437a9","src/iter.rs":"4a4d3309fe9aad14fd7702f02459f4277b4ddb50dba700e58dcc75665ffebfb3","src/keys.rs":"b8c2fba26dee15bf3d1880bb2b41e8d66fe1428d242ee6d9fd30ee94bbd0407d","src/lib.rs":"f83cdc6c4a2cd0d75e85c355ee2c8b19b25194c86468c2285bde1f725656062f","src/list.rs":"fc3decc81bcef92e106aae53e586a0ef21d70916fa53a48f7b813c5da44b8dc2","src/map.rs":"82a96216450dcd0843d3b8511737d90f4ab1563630a523b38fe25c9dfdf802df","src/packed_option.rs":"9d47f5b8302ee685c096817e376144e363507d1c77ef562d3ae4dbddae568195","src/primary.rs":"ae42c8b54ba05e555de63048f2a7e620c74513a1de5d99b8bc7f61403c1b806c","src/set.rs":"ec0ff7a9ee674c90ff9d06ea1fd4ab05039369146c2d259f476c6f612417933f","src/sparse.rs":"cf345a81d69a5dddaed4778b6aaaf06c70da2c1fd4cd21e366ed6ca5906ffdab"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"382f3bb3c020a5067262f7b021c829fb2f6c36e730ae2e9fa6cbd21080f23c49","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"96ceffbfd88fb06e3b41aa4d3087cffbbf8441d04eba7ab09662a72ab600a321","src/boxed_slice.rs":"687428ee0442013c0d5962dd78d0964830233bc4cb19aa530d30da0f1dc437a9","src/iter.rs":"4a4d3309fe9aad14fd7702f02459f4277b4ddb50dba700e58dcc75665ffebfb3","src/keys.rs":"b8c2fba26dee15bf3d1880bb2b41e8d66fe1428d242ee6d9fd30ee94bbd0407d","src/lib.rs":"f83cdc6c4a2cd0d75e85c355ee2c8b19b25194c86468c2285bde1f725656062f","src/list.rs":"fc3decc81bcef92e106aae53e586a0ef21d70916fa53a48f7b813c5da44b8dc2","src/map.rs":"82a96216450dcd0843d3b8511737d90f4ab1563630a523b38fe25c9dfdf802df","src/packed_option.rs":"9d47f5b8302ee685c096817e376144e363507d1c77ef562d3ae4dbddae568195","src/primary.rs":"ae42c8b54ba05e555de63048f2a7e620c74513a1de5d99b8bc7f61403c1b806c","src/set.rs":"ec0ff7a9ee674c90ff9d06ea1fd4ab05039369146c2d259f476c6f612417933f","src/sparse.rs":"cf345a81d69a5dddaed4778b6aaaf06c70da2c1fd4cd21e366ed6ca5906ffdab"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-entity/Cargo.toml
+++ b/third_party/rust/cranelift-entity/Cargo.toml
@@ -1,12 +1,12 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-entity"
-version = "0.37.0"
+version = "0.38.0"
 description = "Data structures using entity references as mapping keys"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://cranelift.readthedocs.io/"
 repository = "https://github.com/CraneStation/cranelift"
 categories = ["no-std"]
 readme = "README.md"
 keywords = ["entity", "set", "map"]
 edition = "2018"
--- a/third_party/rust/cranelift-frontend/.cargo-checksum.json
+++ b/third_party/rust/cranelift-frontend/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"552d38e26cb33dadf07b3482b7f66dc52715af9fc59fae6f9a873b15a24667a6","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"dea43e8044284df50f8b8772e9b48ba8b109b45c74111ff73619775d57ad8d67","src/frontend.rs":"bf8d31db3464e5b2f57875e01b67b9c01875e0d9325a9b69104a582005b81bf3","src/lib.rs":"1cc2e7aaffa45bccea9e59fcc9d9c5d295a9f7adacd6bd55933834e20e969aef","src/ssa.rs":"88cb07071943f3e72a91c91afb58960689b4d9c56352b3bb7cd5d69288066190","src/switch.rs":"b8f337966b540254feb5f979b4a146f5ef69ae199864da6332c9d7513ff3ec8b","src/variable.rs":"f082efaa4b2d3c5eb48f6344149408074e1e15cb581f7a63f549313c7a1037be"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"ad9e89bfc1a2c4649185e9e193b22c81683347a2be9a4d944404b1c30524931a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"dea43e8044284df50f8b8772e9b48ba8b109b45c74111ff73619775d57ad8d67","src/frontend.rs":"bf8d31db3464e5b2f57875e01b67b9c01875e0d9325a9b69104a582005b81bf3","src/lib.rs":"1cc2e7aaffa45bccea9e59fcc9d9c5d295a9f7adacd6bd55933834e20e969aef","src/ssa.rs":"88cb07071943f3e72a91c91afb58960689b4d9c56352b3bb7cd5d69288066190","src/switch.rs":"b8f337966b540254feb5f979b4a146f5ef69ae199864da6332c9d7513ff3ec8b","src/variable.rs":"f082efaa4b2d3c5eb48f6344149408074e1e15cb581f7a63f549313c7a1037be"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-frontend/Cargo.toml
+++ b/third_party/rust/cranelift-frontend/Cargo.toml
@@ -1,22 +1,22 @@
 [package]
 authors = ["The Cranelift Project Developers"]
 name = "cranelift-frontend"
-version = "0.37.0"
+version = "0.38.0"
 description = "Cranelift IR builder helper"
 license = "Apache-2.0 WITH LLVM-exception"
 documentation = "https://cranelift.readthedocs.io/"
 categories = ["no-std"]
 repository = "https://github.com/CraneStation/cranelift"
 readme = "README.md"
 edition = "2018"
 
 [dependencies]
-cranelift-codegen = { path = "../cranelift-codegen", version = "0.37.0", default-features = false }
+cranelift-codegen = { path = "../cranelift-codegen", version = "0.38.0", default-features = false }
 target-lexicon = { version = "0.4.0", default-features = false }
 log = { version = "0.4.6", default-features = false }
 hashmap_core = { version = "0.1.9", optional = true }
 
 [features]
 default = ["std"]
 std = ["cranelift-codegen/std"]
 core = ["hashmap_core", "cranelift-codegen/core"]
--- a/third_party/rust/cranelift-wasm/.cargo-checksum.json
+++ b/third_party/rust/cranelift-wasm/.cargo-checksum.json
@@ -1,1 +1,1 @@
-{"files":{"Cargo.toml":"a1f73e14f6704270180091117795a7b50d655d481db5fc5001b205da6491c2de","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"87679cdb53e8cbec3b1aa45afb2124727c1c059f8bd10363d27daf318a9f9a36","src/code_translator.rs":"c0ab95d87696cc883f706d4704c9daf86331c159c2d0a6bbfe164e431e3db3b4","src/environ/dummy.rs":"ee4d8d4924b4b04027f8af07968d5098ecd72ee62b53622d30334d1a38b227b8","src/environ/mod.rs":"617c147485038dfd797ab0ea71b4cfa9574d95d5d5b1ca362c6b7b6a462cf577","src/environ/spec.rs":"9dc46f050ba05f9e8fb5abea509a0480486caba500deb551339a0f881c070097","src/func_translator.rs":"a218d92155332fea4e3b5025227dd7210b41ee462fb496aff4f7f36682d42e2c","src/lib.rs":"4664114c8f4c174bea6c0385d36f29198fc9b01a8503308c7d75ea094987744b","src/module_translator.rs":"cb19ba62e57939bbb71a4956d4519e414b377e8674c00a02dc62986c3f142430","src/sections_translator.rs":"a36b68aae6317e1f0ed2a7b2b456add0e465382390fe4f82f13777eeb56eff0f","src/state.rs":"fc2a8d468b4b681d9262fdb8762f3300ffce709cb0b2e48f3835a5b9164e7c93","src/translation_utils.rs":"2855e7d370506a533c17cf7cdfe77939f1262d7a2e096b5f42f5658c77d531de","tests/wasm_testsuite.rs":"9b4e008587c61377cf38f9d0e4635418ee38e32a865db8da5dfc6e0fae047436"},"package":null}
\ No newline at end of file
+{"files":{"Cargo.toml":"39f02b362d85a6d87d96cefa4ac5267927e898da34fa2f60102df8e13e156c7a","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","README.md":"87679cdb53e8cbec3b1aa45afb2124727c1c059f8bd10363d27daf318a9f9a36","src/code_translator.rs":"4c17a6a1dd4ef8f19e38f84fe7360a328c2d319d498db8c9ff43f0f4435a4c80","src/environ/dummy.rs":"cca4a867849e82d3fe6793d306d24e89080198e74a58947085edbc12aab2a4ff","src/environ/mod.rs":"b046f5344a1017357c1ee9d661d2193e0247327df293436fa1381a0f45f80584","src/environ/spec.rs":"6fa27ec724328d7e84ed93fafddedf69c1bcf58c0ba32404e160040845fdabc1","src/func_translator.rs":"9cde7a28cd97d0be236ecd023f758c5ffde88ae6bb19d562051a7a55919de6c9","src/lib.rs":"4664114c8f4c174bea6c0385d36f29198fc9b01a8503308c7d75ea094987744b","src/module_translator.rs":"cb19ba62e57939bbb71a4956d4519e414b377e8674c00a02dc62986c3f142430","src/sections_translator.rs":"cab0e57e173cf752a8ada6cff74abb364c3191a9c3341e5c686d750f51c83995","src/state.rs":"fc2a8d468b4b681d9262fdb8762f3300ffce709cb0b2e48f3835a5b9164e7c93","src/translation_utils.rs":"134ab514ba2a21cca88d603419cf233ffa00f671c8552fd3262a8cd85d3bb97d","tests/wasm_testsuite.rs":"9b4e008587c61377cf38f9d0e4635418ee38e32a865db8da5dfc6e0fae047436"},"package":null}
\ No newline at end of file
--- a/third_party/rust/cranelift-wasm/Cargo.toml
+++ b/third_party/rust/cranelift-wasm/Cargo.toml
@@ -1,25 +1,25 @@
 [package]
 name = "cranelift-wasm"
-version = "0.37.0"
+version = "0.38.0"
 authors = ["The Cranelift Project Developers"]
 description = "Translator from WebAssembly to Cranelift IR"
 repository = "https://github.com/CraneStation/cranelift"
 license = "Apache-2.0 WITH LLVM-exception"
 categories = ["no-std", "wasm"]
 readme = "README.md"
 keywords = ["webassembly", "wasm"]
 edition = "2018"
 
 [dependencies]
 wasmparser = { version = "0.32.1", default-features = false }
-cranelift-codegen = { path = "../cranelift-codegen", version = "0.37.0", default-features = false }
-cranelift-entity = { path = "../cranelift-entity", version = "0.37.0", default-features = false }
-cranelift-frontend = { path = "../cranelift-frontend", version = "0.37.0", default-features = false }
+cranelift-codegen = { path = "../cranelift-codegen", version = "0.38.0", default-features = false }
+cranelift-entity = { path = "../cranelift-entity", version = "0.38.0", default-features = false }
+cranelift-frontend = { path = "../cranelift-frontend", version = "0.38.0", default-features = false }
 hashmap_core = { version = "0.1.9", optional = true }
 failure = { version = "0.1.1", default-features = false, features = ["derive"] }
 failure_derive = { version = "0.1.1", default-features = false }
 log = { version = "0.4.6", default-features = false }
 serde = { version = "1.0.94", features = ["derive"], optional = true }
 
 [dev-dependencies]
 wabt = "0.7.0"
--- a/third_party/rust/cranelift-wasm/src/code_translator.rs
+++ b/third_party/rust/cranelift-wasm/src/code_translator.rs
@@ -18,22 +18,23 @@
 //! - the `get_global` et `set_global` instructions depends on how the globals are implemented;
 //! - `memory.size` and `memory.grow` are runtime functions;
 //! - `call_indirect` has to translate the function index into the address of where this
 //!    is;
 //!
 //! That is why `translate_function_body` takes an object having the `WasmRuntime` trait as
 //! argument.
 use super::{hash_map, HashMap};
-use crate::environ::{FuncEnvironment, GlobalVariable, ReturnMode, WasmError, WasmResult};
+use crate::environ::{FuncEnvironment, GlobalVariable, ReturnMode, WasmResult};
 use crate::state::{ControlStackFrame, TranslationState};
 use crate::translation_utils::{
     blocktype_to_type, f32_translation, f64_translation, num_return_values,
 };
 use crate::translation_utils::{FuncIndex, MemoryIndex, SignatureIndex, TableIndex};
+use crate::wasm_unsupported;
 use core::{i32, u32};
 use cranelift_codegen::ir::condcodes::{FloatCC, IntCC};
 use cranelift_codegen::ir::types::*;
 use cranelift_codegen::ir::{self, InstBuilder, JumpTableData, MemFlags, ValueLabel};
 use cranelift_codegen::packed_option::ReservedValue;
 use cranelift_frontend::{FunctionBuilder, Variable};
 use wasmparser::{MemoryImmediate, Operator};
 
@@ -894,33 +895,33 @@ pub fn translate_operator<FE: FuncEnviro
         | Operator::I64AtomicRmw32UXchg { .. }
         | Operator::I32AtomicRmwCmpxchg { .. }
         | Operator::I64AtomicRmwCmpxchg { .. }
         | Operator::I32AtomicRmw8UCmpxchg { .. }
         | Operator::I32AtomicRmw16UCmpxchg { .. }
         | Operator::I64AtomicRmw8UCmpxchg { .. }
         | Operator::I64AtomicRmw16UCmpxchg { .. }
         | Operator::I64AtomicRmw32UCmpxchg { .. } => {
-            return Err(WasmError::Unsupported("proposed thread operators"));
+            wasm_unsupported!("proposed thread operator {:?}", op);
         }
         Operator::RefNull | Operator::RefIsNull { .. } => {
-            return Err(WasmError::Unsupported("proposed reference-type operators"));
+            wasm_unsupported!("proposed reference-type operator {:?}", op);
         }
         Operator::MemoryInit { .. }
         | Operator::DataDrop { .. }
         | Operator::MemoryCopy
         | Operator::MemoryFill
         | Operator::TableInit { .. }
         | Operator::ElemDrop { .. }
         | Operator::TableCopy
         | Operator::TableGet { .. }
         | Operator::TableSet { .. }
         | Operator::TableGrow { .. }
         | Operator::TableSize { .. } => {
-            return Err(WasmError::Unsupported("proposed bulk memory operators"));
+            wasm_unsupported!("proposed bulk memory operator {:?}", op);
         }
         Operator::V128Load { .. }
         | Operator::V128Store { .. }
         | Operator::V128Const { .. }
         | Operator::V8x16Shuffle { .. }
         | Operator::I8x16Splat
         | Operator::I8x16ExtractLaneS { .. }
         | Operator::I8x16ExtractLaneU { .. }
@@ -1054,17 +1055,17 @@ pub fn translate_operator<FE: FuncEnviro
         | Operator::I64x2TruncSF64x2Sat
         | Operator::I64x2TruncUF64x2Sat
         | Operator::F32x4ConvertSI32x4
         | Operator::F32x4ConvertUI32x4
         | Operator::F64x2ConvertSI64x2
         | Operator::F64x2ConvertUI64x2
         | Operator::V8x16Shuffle1
         | Operator::V8x16Shuffle2Imm { .. } => {
-            return Err(WasmError::Unsupported("proposed SIMD operators"));
+            wasm_unsupported!("proposed SIMD operator {:?}", op);
         }
     };
     Ok(())
 }
 
 // Clippy warns us of some fields we are deliberately ignoring
 #[cfg_attr(feature = "cargo-clippy", allow(clippy::unneeded_field_pattern))]
 /// Deals with a Wasm instruction located in an unreachable portion of the code. Most of them
--- a/third_party/rust/cranelift-wasm/src/environ/dummy.rs
+++ b/third_party/rust/cranelift-wasm/src/environ/dummy.rs
@@ -362,121 +362,164 @@ impl<'dummy_environment> FuncEnvironment
     }
 }
 
 impl<'data> ModuleEnvironment<'data> for DummyEnvironment {
     fn target_config(&self) -> TargetFrontendConfig {
         self.info.config
     }
 
-    fn declare_signature(&mut self, sig: ir::Signature) {
+    fn declare_signature(&mut self, sig: ir::Signature) -> WasmResult<()> {
         self.info.signatures.push(sig);
+        Ok(())
     }
 
     fn declare_func_import(
         &mut self,
         sig_index: SignatureIndex,
         module: &'data str,
         field: &'data str,
-    ) {
+    ) -> WasmResult<()> {
         assert_eq!(
             self.info.functions.len(),
             self.info.imported_funcs.len(),
             "Imported functions must be declared first"
         );
         self.info.functions.push(Exportable::new(sig_index));
         self.info
             .imported_funcs
             .push((String::from(module), String::from(field)));
+        Ok(())
     }
 
-    fn declare_func_type(&mut self, sig_index: SignatureIndex) {
+    fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()> {
         self.info.functions.push(Exportable::new(sig_index));
+        Ok(())
     }
 
-    fn declare_global(&mut self, global: Global) {
+    fn declare_global(&mut self, global: Global) -> WasmResult<()> {
         self.info.globals.push(Exportable::new(global));
+        Ok(())
     }
 
-    fn declare_global_import(&mut self, global: Global, module: &'data str, field: &'data str) {
+    fn declare_global_import(
+        &mut self,
+        global: Global,
+        module: &'data str,
+        field: &'data str,
+    ) -> WasmResult<()> {
         self.info.globals.push(Exportable::new(global));
         self.info
             .imported_globals
             .push((String::from(module), String::from(field)));
+        Ok(())
     }
 
-    fn declare_table(&mut self, table: Table) {
+    fn declare_table(&mut self, table: Table) -> WasmResult<()> {
         self.info.tables.push(Exportable::new(table));
+        Ok(())
     }
 
-    fn declare_table_import(&mut self, table: Table, module: &'data str, field: &'data str) {
+    fn declare_table_import(
+        &mut self,
+        table: Table,
+        module: &'data str,
+        field: &'data str,
+    ) -> WasmResult<()> {
         self.info.tables.push(Exportable::new(table));
         self.info
             .imported_tables
             .push((String::from(module), String::from(field)));
+        Ok(())
     }
 
     fn declare_table_elements(
         &mut self,
         _table_index: TableIndex,
         _base: Option<GlobalIndex>,
         _offset: usize,
         _elements: Box<[FuncIndex]>,
-    ) {
+    ) -> WasmResult<()> {
         // We do nothing
+        Ok(())
     }
 
-    fn declare_memory(&mut self, memory: Memory) {
+    fn declare_memory(&mut self, memory: Memory) -> WasmResult<()> {
         self.info.memories.push(Exportable::new(memory));
+        Ok(())
     }
 
-    fn declare_memory_import(&mut self, memory: Memory, module: &'data str, field: &'data str) {
+    fn declare_memory_import(
+        &mut self,
+        memory: Memory,
+        module: &'data str,
+        field: &'data str,
+    ) -> WasmResult<()> {
         self.info.memories.push(Exportable::new(memory));
         self.info
             .imported_memories
             .push((String::from(module), String::from(field)));
+        Ok(())
     }
 
     fn declare_data_initialization(
         &mut self,
         _memory_index: MemoryIndex,
         _base: Option<GlobalIndex>,
         _offset: usize,
         _data: &'data [u8],
-    ) {
+    ) -> WasmResult<()> {
         // We do nothing
+        Ok(())
     }
 
-    fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) {
+    fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()> {
         self.info.functions[func_index]
             .export_names
             .push(String::from(name));
+        Ok(())
     }
 
-    fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str) {
+    fn declare_table_export(
+        &mut self,
+        table_index: TableIndex,
+        name: &'data str,
+    ) -> WasmResult<()> {
         self.info.tables[table_index]
             .export_names
             .push(String::from(name));
+        Ok(())
     }
 
-    fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &'data str) {
+    fn declare_memory_export(
+        &mut self,
+        memory_index: MemoryIndex,
+        name: &'data str,
+    ) -> WasmResult<()> {
         self.info.memories[memory_index]
             .export_names
             .push(String::from(name));
+        Ok(())
     }
 
-    fn declare_global_export(&mut self, global_index: GlobalIndex, name: &'data str) {
+    fn declare_global_export(
+        &mut self,
+        global_index: GlobalIndex,
+        name: &'data str,
+    ) -> WasmResult<()> {
         self.info.globals[global_index]
             .export_names
             .push(String::from(name));
+        Ok(())
     }
 
-    fn declare_start_func(&mut self, func_index: FuncIndex) {
+    fn declare_start_func(&mut self, func_index: FuncIndex) -> WasmResult<()> {
         debug_assert!(self.info.start_func.is_none());
         self.info.start_func = Some(func_index);
+        Ok(())
     }
 
     fn define_function_body(
         &mut self,
         body_bytes: &'data [u8],
         body_offset: usize,
     ) -> WasmResult<()> {
         let func = {
--- a/third_party/rust/cranelift-wasm/src/environ/mod.rs
+++ b/third_party/rust/cranelift-wasm/src/environ/mod.rs
@@ -1,9 +1,10 @@
 //! Support for configurable wasm translation.
 
 mod dummy;
+#[macro_use]
 mod spec;
 
 pub use crate::environ::dummy::DummyEnvironment;
 pub use crate::environ::spec::{
     FuncEnvironment, GlobalVariable, ModuleEnvironment, ReturnMode, WasmError, WasmResult,
 };
--- a/third_party/rust/cranelift-wasm/src/environ/spec.rs
+++ b/third_party/rust/cranelift-wasm/src/environ/spec.rs
@@ -55,32 +55,39 @@ pub enum WasmError {
         /// The bytecode offset where the error occurred.
         offset: usize,
     },
 
     /// A feature used by the WebAssembly code is not supported by the embedding environment.
     ///
     /// Embedding environments may have their own limitations and feature restrictions.
     #[fail(display = "Unsupported feature: {}", _0)]
-    Unsupported(&'static str),
+    Unsupported(std::string::String),
 
     /// An implementation limit was exceeded.
     ///
     /// Cranelift can compile very large and complicated functions, but the [implementation has
     /// limits][limits] that cause compilation to fail when they are exceeded.
     ///
     /// [limits]: https://cranelift.readthedocs.io/en/latest/ir.html#implementation-limits
     #[fail(display = "Implementation limit exceeded")]
     ImplLimitExceeded,
 
     /// Any user-defined error.
     #[fail(display = "User error: {}", _0)]
     User(std::string::String),
 }
 
+/// Return an `Err(WasmError::Unsupported(msg))` where `msg` the string built by calling `format!`
+/// on the arguments to this macro.
+#[macro_export]
+macro_rules! wasm_unsupported {
+    ($($arg:tt)*) => { return Err($crate::environ::WasmError::Unsupported(format!($($arg)*))) }
+}
+
 impl From<BinaryReaderError> for WasmError {
     /// Convert from a `BinaryReaderError` to a `WasmError`.
     fn from(e: BinaryReaderError) -> Self {
         let BinaryReaderError { message, offset } = e;
         WasmError::InvalidWebAssembly { message, offset }
     }
 }
 
@@ -284,123 +291,169 @@ pub trait FuncEnvironment {
 /// [`translate_module`](fn.translate_module.html) function. These methods should not be called
 /// by the user, they are only for `cranelift-wasm` internal use.
 pub trait ModuleEnvironment<'data> {
     /// Get the information needed to produce Cranelift IR for the current target.
     fn target_config(&self) -> TargetFrontendConfig;
 
     /// Provides the number of signatures up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_signatures(&mut self, _num: u32) {}
+    fn reserve_signatures(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Declares a function signature to the environment.
-    fn declare_signature(&mut self, sig: ir::Signature);
+    fn declare_signature(&mut self, sig: ir::Signature) -> WasmResult<()>;
 
     /// Provides the number of imports up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_imports(&mut self, _num: u32) {}
+    fn reserve_imports(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Declares a function import to the environment.
     fn declare_func_import(
         &mut self,
         sig_index: SignatureIndex,
         module: &'data str,
         field: &'data str,
-    );
+    ) -> WasmResult<()>;
 
     /// Declares a table import to the environment.
-    fn declare_table_import(&mut self, table: Table, module: &'data str, field: &'data str);
+    fn declare_table_import(
+        &mut self,
+        table: Table,
+        module: &'data str,
+        field: &'data str,
+    ) -> WasmResult<()>;
 
     /// Declares a memory import to the environment.
-    fn declare_memory_import(&mut self, memory: Memory, module: &'data str, field: &'data str);
+    fn declare_memory_import(
+        &mut self,
+        memory: Memory,
+        module: &'data str,
+        field: &'data str,
+    ) -> WasmResult<()>;
 
     /// Declares a global import to the environment.
-    fn declare_global_import(&mut self, global: Global, module: &'data str, field: &'data str);
+    fn declare_global_import(
+        &mut self,
+        global: Global,
+        module: &'data str,
+        field: &'data str,
+    ) -> WasmResult<()>;
 
     /// Notifies the implementation that all imports have been declared.
-    fn finish_imports(&mut self) {}
+    fn finish_imports(&mut self) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Provides the number of defined functions up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_func_types(&mut self, _num: u32) {}
+    fn reserve_func_types(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Declares the type (signature) of a local function in the module.
-    fn declare_func_type(&mut self, sig_index: SignatureIndex);
+    fn declare_func_type(&mut self, sig_index: SignatureIndex) -> WasmResult<()>;
 
     /// Provides the number of defined tables up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_tables(&mut self, _num: u32) {}
+    fn reserve_tables(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Declares a table to the environment.
-    fn declare_table(&mut self, table: Table);
+    fn declare_table(&mut self, table: Table) -> WasmResult<()>;
 
     /// Provides the number of defined memories up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_memories(&mut self, _num: u32) {}
+    fn reserve_memories(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Declares a memory to the environment
-    fn declare_memory(&mut self, memory: Memory);
+    fn declare_memory(&mut self, memory: Memory) -> WasmResult<()>;
 
     /// Provides the number of defined globals up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_globals(&mut self, _num: u32) {}
+    fn reserve_globals(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Declares a global to the environment.
-    fn declare_global(&mut self, global: Global);
+    fn declare_global(&mut self, global: Global) -> WasmResult<()>;
 
     /// Provides the number of exports up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_exports(&mut self, _num: u32) {}
+    fn reserve_exports(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Declares a function export to the environment.
-    fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str);
+    fn declare_func_export(&mut self, func_index: FuncIndex, name: &'data str) -> WasmResult<()>;
 
     /// Declares a table export to the environment.
-    fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str);
+    fn declare_table_export(&mut self, table_index: TableIndex, name: &'data str)
+        -> WasmResult<()>;
 
     /// Declares a memory export to the environment.
-    fn declare_memory_export(&mut self, memory_index: MemoryIndex, name: &'data str);
+    fn declare_memory_export(
+        &mut self,
+        memory_index: MemoryIndex,
+        name: &'data str,
+    ) -> WasmResult<()>;
 
     /// Declares a global export to the environment.
-    fn declare_global_export(&mut self, global_index: GlobalIndex, name: &'data str);
+    fn declare_global_export(
+        &mut self,
+        global_index: GlobalIndex,
+        name: &'data str,
+    ) -> WasmResult<()>;
 
     /// Notifies the implementation that all exports have been declared.
-    fn finish_exports(&mut self) {}
+    fn finish_exports(&mut self) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Declares the optional start function.
-    fn declare_start_func(&mut self, index: FuncIndex);
+    fn declare_start_func(&mut self, index: FuncIndex) -> WasmResult<()>;
 
     /// Provides the number of element initializers up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_table_elements(&mut self, _num: u32) {}
+    fn reserve_table_elements(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Fills a declared table with references to functions in the module.
     fn declare_table_elements(
         &mut self,
         table_index: TableIndex,
         base: Option<GlobalIndex>,
         offset: usize,
         elements: Box<[FuncIndex]>,
-    );
+    ) -> WasmResult<()>;
 
     /// Provides the contents of a function body.
     ///
     /// Note there's no `reserve_function_bodies` function because the number of
     /// functions is already provided by `reserve_func_types`.
     fn define_function_body(
         &mut self,
         body_bytes: &'data [u8],
         body_offset: usize,
     ) -> WasmResult<()>;
 
     /// Provides the number of data initializers up front. By default this does nothing, but
     /// implementations can use this to preallocate memory if desired.
-    fn reserve_data_initializers(&mut self, _num: u32) {}
+    fn reserve_data_initializers(&mut self, _num: u32) -> WasmResult<()> {
+        Ok(())
+    }
 
     /// Fills a declared memory with bytes at module instantiation.
     fn declare_data_initialization(
         &mut self,
         memory_index: MemoryIndex,
         base: Option<GlobalIndex>,
         offset: usize,
         data: &'data [u8],
-    );
+    ) -> WasmResult<()>;
 }
--- a/third_party/rust/cranelift-wasm/src/func_translator.rs
+++ b/third_party/rust/cranelift-wasm/src/func_translator.rs
@@ -1,18 +1,19 @@
 //! Stand-alone WebAssembly to Cranelift IR translator.
 //!
 //! This module defines the `FuncTranslator` type which can translate a single WebAssembly
 //! function to Cranelift IR guided by a `FuncEnvironment` which provides information about the
 //! WebAssembly module and the runtime environment.
 
 use crate::code_translator::translate_operator;
-use crate::environ::{FuncEnvironment, ReturnMode, WasmError, WasmResult};
+use crate::environ::{FuncEnvironment, ReturnMode, WasmResult};
 use crate::state::{TranslationState, VisibleTranslationState};
 use crate::translation_utils::get_vmctx_value_label;
+use crate::wasm_unsupported;
 use cranelift_codegen::entity::EntityRef;
 use cranelift_codegen::ir::{self, Ebb, InstBuilder, ValueLabel};
 use cranelift_codegen::timing;
 use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable};
 use log::info;
 use wasmparser::{self, BinaryReader};
 
 /// WebAssembly to Cranelift IR function translator.
@@ -171,17 +172,17 @@ fn declare_locals(
 ) -> WasmResult<()> {
     // All locals are initialized to 0.
     use wasmparser::Type::*;
     let zeroval = match wasm_type {
         I32 => builder.ins().iconst(ir::types::I32, 0),
         I64 => builder.ins().iconst(ir::types::I64, 0),
         F32 => builder.ins().f32const(ir::immediates::Ieee32::with_bits(0)),
         F64 => builder.ins().f64const(ir::immediates::Ieee64::with_bits(0)),
-        _ => return Err(WasmError::Unsupported("unsupported local type")),
+        ty => wasm_unsupported!("unsupported local type {:?}", ty),
     };
 
     let ty = builder.func.dfg.value_type(zeroval);
     for _ in 0..count {
         let local = Variable::new(*next_local);
         builder.declare_var(local, ty);
         builder.def_var(local, zeroval);
         builder.set_val_label(zeroval, ValueLabel::new(*next_local));
--- a/third_party/rust/cranelift-wasm/src/sections_translator.rs
+++ b/third_party/rust/cranelift-wasm/src/sections_translator.rs
@@ -2,21 +2,22 @@
 //! WebAssembly module.
 //!
 //! The code of these helper functions is straightforward since they only read metadata
 //! about linear memories, tables, globals, etc. and store them for later use.
 //!
 //! The special case of the initialize expressions for table elements offsets or global variables
 //! is handled, according to the semantics of WebAssembly, to only specific expressions that are
 //! interpreted on the fly.
-use crate::environ::{ModuleEnvironment, WasmError, WasmResult};
+use crate::environ::{ModuleEnvironment, WasmResult};
 use crate::translation_utils::{
     tabletype_to_type, type_to_type, FuncIndex, Global, GlobalIndex, GlobalInit, Memory,
     MemoryIndex, SignatureIndex, Table, TableElementType, TableIndex,
 };
+use crate::wasm_unsupported;
 use core::convert::TryFrom;
 use cranelift_codegen::ir::{self, AbiParam, Signature};
 use cranelift_entity::EntityRef;
 use std::vec::Vec;
 use wasmparser::{
     self, CodeSectionReader, Data, DataKind, DataSectionReader, Element, ElementKind,
     ElementSectionReader, Export, ExportSectionReader, ExternalKind, FuncType,
     FunctionSectionReader, GlobalSectionReader, GlobalType, ImportSectionEntryType,
@@ -24,17 +25,17 @@ use wasmparser::{
     TypeSectionReader,
 };
 
 /// Parses the Type section of the wasm module.
 pub fn parse_type_section(
     types: TypeSectionReader,
     environ: &mut dyn ModuleEnvironment,
 ) -> WasmResult<()> {
-    environ.reserve_signatures(types.get_count());
+    environ.reserve_signatures(types.get_count())?;
 
     for entry in types {
         match entry? {
             FuncType {
                 form: wasmparser::Type::Func,
                 ref params,
                 ref returns,
             } => {
@@ -44,148 +45,152 @@ pub fn parse_type_section(
                         .expect("only numeric types are supported in function signatures");
                     AbiParam::new(cret_arg)
                 }));
                 sig.returns.extend(returns.iter().map(|ty| {
                     let cret_arg: ir::Type = type_to_type(*ty)
                         .expect("only numeric types are supported in function signatures");
                     AbiParam::new(cret_arg)
                 }));
-                environ.declare_signature(sig);
+                environ.declare_signature(sig)?;
             }
-            _ => return Err(WasmError::Unsupported("unsupported type in type section")),
+            ty => wasm_unsupported!("unsupported type in type section: {:?}", ty),
         }
     }
     Ok(())
 }
 
 /// Parses the Import section of the wasm module.
 pub fn parse_import_section<'data>(
     imports: ImportSectionReader<'data>,
     environ: &mut dyn ModuleEnvironment<'data>,
 ) -> WasmResult<()> {
-    environ.reserve_imports(imports.get_count());
+    environ.reserve_imports(imports.get_count())?;
 
     for entry in imports {
         let import = entry?;
         let module_name = import.module;
         let field_name = import.field;
 
         match import.ty {
             ImportSectionEntryType::Function(sig) => {
-                environ.declare_func_import(SignatureIndex::from_u32(sig), module_name, field_name);
+                environ.declare_func_import(
+                    SignatureIndex::from_u32(sig),
+                    module_name,