Merge inbound to mozilla-central. a=merge
authorOana Pop Rus <opoprus@mozilla.com>
Thu, 24 Jan 2019 18:50:45 +0200
changeset 515310 2c5eb9d8b8e2680e7ff3d8fe2714bfd78ef0c7d0
parent 515266 0aa259de1b77361ef89c82592b7485f66d4d33fb (current diff)
parent 515301 7df604faea71828fb893e6b5c8f090a35f57d261 (diff)
child 515311 8c7dea6754c6929e05bbd313a9f27c2c96f4148c
child 515313 82d47da67b4dfe32998f5e443d16530adc1daf0e
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9635,39 +9635,42 @@ nsresult nsDocShell::DoURILoad(nsDocShel
     return rv;
   }
 
   if (IsFrame()) {
     MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
                    aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
                "DoURILoad thinks this is a frame and InternalLoad does not");
 
-    // Only allow URLs able to return data in iframes.
-    bool doesNotReturnData = false;
-    NS_URIChainHasFlags(aLoadState->URI(),
-                        nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
-                        &doesNotReturnData);
-    if (doesNotReturnData) {
-      bool popupBlocked = true;
-
-      // Let's consider external protocols as popups and let's check if the page
-      // is allowed to open them without abuse regardless of allowed events
-      if (PopupBlocker::GetPopupControlState() <= PopupBlocker::openBlocked) {
-        popupBlocked = !PopupBlocker::TryUsePopupOpeningToken();
-      } else {
-        nsCOMPtr<nsINode> loadingNode =
-            mScriptGlobal->AsOuter()->GetFrameElementInternal();
-        if (loadingNode) {
-          popupBlocked = !PopupBlocker::CanShowPopupByPermission(
-              loadingNode->NodePrincipal());
+    if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
+      // Only allow URLs able to return data in iframes.
+      bool doesNotReturnData = false;
+      NS_URIChainHasFlags(aLoadState->URI(),
+                          nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
+                          &doesNotReturnData);
+      if (doesNotReturnData) {
+        bool popupBlocked = true;
+
+        // Let's consider external protocols as popups and let's check if the
+        // page is allowed to open them without abuse regardless of allowed
+        // events
+        if (PopupBlocker::GetPopupControlState() <= PopupBlocker::openBlocked) {
+          popupBlocked = !PopupBlocker::TryUsePopupOpeningToken();
+        } else {
+          nsCOMPtr<nsINode> loadingNode =
+              mScriptGlobal->AsOuter()->GetFrameElementInternal();
+          if (loadingNode) {
+            popupBlocked = !PopupBlocker::CanShowPopupByPermission(
+                loadingNode->NodePrincipal());
+          }
         }
-      }
-
-      if (popupBlocked) {
-        return NS_ERROR_UNKNOWN_PROTOCOL;
+
+        if (popupBlocked) {
+          return NS_ERROR_UNKNOWN_PROTOCOL;
+        }
       }
     }
 
     // Only allow view-source scheme in top-level docshells. view-source is
     // the only scheme to which this applies at the moment due to potential
     // timing attacks to read data from cross-origin iframes. If this widens
     // we should add a protocol flag for whether the scheme is allowed in
     // frames and use something like nsNetUtil::NS_URIChainHasFlags.
--- a/dom/html/test/test_external_protocol_iframe.html
+++ b/dom/html/test/test_external_protocol_iframe.html
@@ -5,35 +5,40 @@
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <div id='foo'><a href='#'>Click here to test this issue</a></div>
   <script>
 
-SimpleTest.waitForExplicitFinish();
+function next() {
+  let foo = document.getElementById('foo');
+  foo.addEventListener('click', _ => {
+    is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
+    ok(!ChromeUtils.isPopupTokenUnused(), "Popup token has not been used yet");
 
-let foo = document.getElementById('foo');
-foo.addEventListener('click', _ => {
-  is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
-  ok(!ChromeUtils.isPopupTokenUnused(), "Popup token has not been used yet");
+    for (let i = 0; i < 10; ++i) {
+      let ifr = document.createElement('iframe');
+      ifr.src = "foo+bar:all_good";
+      document.body.appendChild(ifr);
 
-  for (let i = 0; i < 10; ++i) {
-    let ifr = document.createElement('iframe');
-    ifr.src = "foo+bar:all_good";
-    document.body.appendChild(ifr);
+      is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
+      ok(ChromeUtils.isPopupTokenUnused(), "Popup token has been used!");
+    }
 
-    is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
-    ok(ChromeUtils.isPopupTokenUnused(), "Popup token has been used!");
-  }
+    SimpleTest.finish();
 
-  SimpleTest.finish();
+  }, {once: true});
 
-}, {once: true});
+  setTimeout(_ => {
+    sendMouseEvent({type:'click'}, 'foo');
+  }, 0);
+}
 
-setTimeout(_ => {
-  sendMouseEvent({type:'click'}, 'foo');
-}, 0);
+SpecialPowers.pushPrefEnv({'set': [
+  ['dom.block_external_protocol_in_iframes', true],
+]}, next);
 
+SimpleTest.waitForExplicitFinish();
   </script>
 </body>
 </html>
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -278,17 +278,17 @@ void LoadContextOptions(const char* aPre
   // Context options.
   JS::ContextOptions contextOptions;
   contextOptions.setAsmJS(GetWorkerPref<bool>(NS_LITERAL_CSTRING("asmjs")))
       .setWasm(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm")))
       .setWasmBaseline(
           GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_baselinejit")))
       .setWasmIon(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_ionjit")))
 #ifdef ENABLE_WASM_CRANELIFT
-      .setWasmForceCranelift(
+      .setWasmCranelift(
           GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_cranelift")))
 #endif
 #ifdef ENABLE_WASM_REFTYPES
       .setWasmGc(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_gc")))
 #endif
       .setWasmVerbose(GetWorkerPref<bool>(NS_LITERAL_CSTRING("wasm_verbose")))
       .setThrowOnAsmJSValidationFailure(GetWorkerPref<bool>(
           NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure")))
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -68,16 +68,17 @@
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/SavedStacks.h"
 #include "vm/Stack.h"
 #include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #include "wasm/AsmJS.h"
+#include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmJS.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTextToBinary.h"
 #include "wasm/WasmTypes.h"
 
 #include "vm/Compartment-inl.h"
 #include "vm/Debugger-inl.h"
@@ -641,30 +642,30 @@ static bool WasmCachingIsSupported(JSCon
   args.rval().setBoolean(wasm::HasCachingSupport(cx));
   return true;
 }
 
 static bool WasmThreadsSupported(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   bool isSupported = wasm::HasSupport(cx);
 #ifdef ENABLE_WASM_CRANELIFT
-  if (cx->options().wasmForceCranelift()) {
+  if (cx->options().wasmCranelift()) {
     isSupported = false;
   }
 #endif
   args.rval().setBoolean(isSupported);
   return true;
 }
 
 static bool WasmBulkMemSupported(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef ENABLE_WASM_BULKMEM_OPS
   bool isSupported = true;
 #  ifdef ENABLE_WASM_CRANELIFT
-  if (cx->options().wasmForceCranelift()) {
+  if (cx->options().wasmCranelift()) {
     isSupported = false;
   }
 #  endif
 #else
   bool isSupported = false;
 #endif
   args.rval().setBoolean(isSupported);
   return true;
@@ -681,40 +682,58 @@ static bool WasmGcEnabled(JSContext* cx,
 #ifdef ENABLE_WASM_GC
   args.rval().setBoolean(wasm::HasReftypesSupport(cx));
 #else
   args.rval().setBoolean(false);
 #endif
   return true;
 }
 
+static bool WasmDebugSupport(JSContext* cx, unsigned argc, Value* vp) {
+  CallArgs args = CallArgsFromVp(argc, vp);
+  args.rval().setBoolean(cx->options().wasmBaseline() && wasm::BaselineCanCompile());
+  return true;
+}
+
 static bool WasmGeneralizedTables(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef ENABLE_WASM_GENERALIZED_TABLES
   // Generalized tables depend on anyref, though not currently on (ref T)
   // types nor on structures or other GC-proposal features.
   bool isSupported = wasm::HasReftypesSupport(cx);
 #else
   bool isSupported = false;
 #endif
   args.rval().setBoolean(isSupported);
   return true;
 }
 
 static bool WasmCompileMode(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
 
+  bool baseline = cx->options().wasmBaseline();
+  bool ion = cx->options().wasmIon();
+#ifdef ENABLE_WASM_CRANELIFT
+  bool cranelift = cx->options().wasmCranelift();
+#else
+  bool cranelift = false;
+#endif
+
   // We default to ion if nothing is enabled, as does the Wasm compiler.
   JSString* result;
   if (!wasm::HasSupport(cx)) {
-    result = JS_NewStringCopyZ(cx, "disabled");
-  } else if (cx->options().wasmBaseline() && cx->options().wasmIon()) {
-    result = JS_NewStringCopyZ(cx, "baseline-or-ion");
-  } else if (cx->options().wasmBaseline()) {
+    result = JS_NewStringCopyZ(cx, "none");
+  } else if (baseline && ion) {
+    result = JS_NewStringCopyZ(cx, "baseline+ion");
+  } else if (baseline && cranelift) {
+    result = JS_NewStringCopyZ(cx, "baseline+cranelift");
+  } else if (baseline) {
     result = JS_NewStringCopyZ(cx, "baseline");
+  } else if (cranelift) {
+    result = JS_NewStringCopyZ(cx, "cranelift");
   } else {
     result = JS_NewStringCopyZ(cx, "ion");
   }
 
   if (!result) {
     return false;
   }
 
@@ -5970,16 +5989,20 @@ gc::ZealModeHelpText),
     JS_FN_HELP("wasmReftypesEnabled", WasmReftypesEnabled, 1, 0,
 "wasmReftypesEnabled(bool)",
 "  Returns a boolean indicating whether the WebAssembly reftypes proposal is enabled."),
 
     JS_FN_HELP("wasmGcEnabled", WasmGcEnabled, 1, 0,
 "wasmGcEnabled(bool)",
 "  Returns a boolean indicating whether the WebAssembly GC types proposal is enabled."),
 
+    JS_FN_HELP("wasmDebugSupport", WasmDebugSupport, 1, 0,
+"wasmDebugSupport(bool)",
+"  Returns a boolean indicating whether the WebAssembly compilers support debugging."),
+
     JS_FN_HELP("wasmGeneralizedTables", WasmGeneralizedTables, 1, 0,
 "wasmGeneralizedTables(bool)",
 "  Returns a boolean indicating whether generalized tables are available.\n"
 "  This feature set includes 'anyref' as a table type, and new instructions\n"
 "  including table.get, table.set, table.grow, and table.size."),
 
     JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,
 "isLazyFunction(fun)",
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -220,17 +220,17 @@ def main(argv):
     # wasm-baseline run when requesting --jitflags=interp, but the test
     # contains test-also-noasmjs.)
     test_flags = get_jitflags(options.jitflags)
     options.asmjs_enabled = True
     options.wasm_enabled = True
     if all(['--no-asmjs' in flags for flags in test_flags]):
         options.asmjs_enabled = False
         options.wasm_enabled = False
-    if all(['--no-wasm' in flags for flags in test_flags]):
+    if all(['--wasm-compiler=none' in flags for flags in test_flags]):
         options.asmjs_enabled = False
         options.wasm_enabled = False
 
     if test_args:
         read_all = False
         for arg in test_args:
             test_list += jittests.find_tests(arg, run_binast=options.run_binast)
 
--- a/js/src/jit-test/tests/debug/bug1330339.js
+++ b/js/src/jit-test/tests/debug/bug1330339.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; error: TestComplete
+// |jit-test| test-also-wasm-compiler-ion; error: TestComplete
 
 if (!wasmDebuggingIsSupported())
      throw "TestComplete";
 
 let module = new WebAssembly.Module(wasmTextToBinary(`
     (module
         (import "global" "func")
         (func (export "test")
--- a/js/src/jit-test/tests/debug/bug1330489-sps.js
+++ b/js/src/jit-test/tests/debug/bug1330489-sps.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; error: TestComplete
+// |jit-test| test-also-wasm-compiler-ion; error: TestComplete
 
 load(libdir + "asserts.js");
 
 if (!wasmDebuggingIsSupported())
     throw "TestComplete";
 
 // Single-step profiling currently only works in the ARM simulator
 if (!getBuildConfiguration()["arm-simulator"])
--- a/js/src/jit-test/tests/debug/bug1330489.js
+++ b/js/src/jit-test/tests/debug/bug1330489.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; error: TestComplete
+// |jit-test| test-also-wasm-compiler-ion; error: TestComplete
 
 load(libdir + "asserts.js");
 
 if (!wasmDebuggingIsSupported())
     throw "TestComplete";
 
 var g = newGlobal({newCompartment: true});
 g.parent = this;
--- a/js/src/jit-test/tests/debug/bug1330491.js
+++ b/js/src/jit-test/tests/debug/bug1330491.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; error: TestComplete
+// |jit-test| test-also-wasm-compiler-ion; error: TestComplete
 
 if (!wasmDebuggingIsSupported())
      throw "TestComplete";
 
 (function createShortLivedDebugger() {
     var g = newGlobal({newCompartment: true});
     g.debuggeeGlobal = this;
     g.eval("(" + function () {
--- a/js/src/jit-test/tests/debug/bug1331064.js
+++ b/js/src/jit-test/tests/debug/bug1331064.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
 
 load(libdir + "asserts.js");
 
 var g = newGlobal();
 g.parent = this;
 g.eval("new Debugger(parent).onExceptionUnwind = function () {  some_error; };");
 
 var module = new WebAssembly.Module(wasmTextToBinary(`
--- a/js/src/jit-test/tests/debug/bug1331592.js
+++ b/js/src/jit-test/tests/debug/bug1331592.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; error: TestComplete
+// |jit-test| test-also-wasm-compiler-ion; error: TestComplete
 
 if (!wasmDebuggingIsSupported())
      throw "TestComplete";
 
 var module = new WebAssembly.Module(wasmTextToBinary(`
     (module
         (import "global" "func" (result i32))
         (func (export "func_0") (result i32)
--- a/js/src/jit-test/tests/debug/bug1332493.js
+++ b/js/src/jit-test/tests/debug/bug1332493.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
 // Checking in debug frame is initialized properly during stack overflow.
 
 var dbg;
 (function () { dbg = new (newGlobal().Debugger)(this); })();
 
 var m = new WebAssembly.Module(wasmTextToBinary(`(module
     (import "a" "b" (result f64))
     ;; function that allocated large space for locals on the stack
--- a/js/src/jit-test/tests/debug/bug1343579.js
+++ b/js/src/jit-test/tests/debug/bug1343579.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 // Checking if Debugger.Script.isInCatchScope return false for wasm.
 
 load(libdir + "wasm.js");
 
 var results;
 wasmRunWithDebugger(
     '(module (memory 1) ' +
     '(func $func0 i32.const 1000000 i32.load drop) ' +
--- a/js/src/jit-test/tests/debug/wasm-06-onEnterFrame-null.js
+++ b/js/src/jit-test/tests/debug/wasm-06-onEnterFrame-null.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
 // Checking resumption values for 'null' at onEnterFrame.
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('');
 var dbg = new Debugger();
 dbg.addDebuggee(g);
 sandbox.eval(`
--- a/js/src/jit-test/tests/debug/wasm-06-onPop-null.js
+++ b/js/src/jit-test/tests/debug/wasm-06-onPop-null.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; exitstatus: 3; skip-if: !wasmDebuggingIsSupported()
 // Checking resumption values for 'null' at frame's onPop.
 
 load(libdir + "asserts.js");
 
 var g = newGlobal('');
 var dbg = new Debugger();
 dbg.addDebuggee(g);
 sandbox.eval(`
--- a/js/src/jit-test/tests/debug/wasm-06.js
+++ b/js/src/jit-test/tests/debug/wasm-06.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; error: TestComplete; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; error: TestComplete; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm module scripts raises onEnterFrame and onLeaveFrame events.
 
 load(libdir + "asserts.js");
 
 function runWasmWithDebugger(wast, lib, init, done) {
     let g = newGlobal({newCompartment: true});
     let dbg = new Debugger(g);
 
--- a/js/src/jit-test/tests/debug/wasm-07.js
+++ b/js/src/jit-test/tests/debug/wasm-07.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 
 // Checking existence of all frame.offset references during onEnterFrame,
 // onLeaveFrame and onStep events in the source code, and that we can
 // potentially resolve offset back to the line/column.
 
 load(libdir + "wasm.js");
 
 var offsets;
--- a/js/src/jit-test/tests/debug/wasm-08.js
+++ b/js/src/jit-test/tests/debug/wasm-08.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 // Checking if we destroying work registers by breakpoint/step handler.
 
 load(libdir + "wasm.js");
 
 // Running the following code compiled from C snippet:
 //
 //     signed func0(signed n) {
 //         double a = 1; float b = 0; signed c = 1; long long d = 1;
--- a/js/src/jit-test/tests/debug/wasm-09.js
+++ b/js/src/jit-test/tests/debug/wasm-09.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 // Tests debugEnabled state of wasm when allowUnobservedAsmJS == true.
 
 load(libdir + "asserts.js");
 
 // Checking that there are no offsets are present in a wasm instance script for
 // which debug mode was not enabled.
 function getWasmScriptWithoutAllowUnobservedAsmJS(wast) {
     var sandbox = newGlobal({newCompartment: true});
--- a/js/src/jit-test/tests/debug/wasm-10.js
+++ b/js/src/jit-test/tests/debug/wasm-10.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm module scripts has inspectable locals.
 
 load(libdir + "wasm.js");
 load(libdir + 'eqArrayHelper.js');
 
 function monitorLocalValues(wast, lib, expected) {
     function setupFrame(frame) {
         var locals = {};
--- a/js/src/jit-test/tests/debug/wasm-11.js
+++ b/js/src/jit-test/tests/debug/wasm-11.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 
 // Test single-stepping where the TLS register can be evicted by a non-trivial
 // function body.
 
 var g = newGlobal({newCompartment: true});
 g.parent = this;
 g.eval(`
     var dbg = new Debugger(parent);
--- a/js/src/jit-test/tests/debug/wasm-12.js
+++ b/js/src/jit-test/tests/debug/wasm-12.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 
 // Tests that wasm module scripts have special URLs.
 
 var g = newGlobal({newCompartment: true});
 var dbg = new Debugger(g);
 g.eval(`
 function initWasm(s) { return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(s))); }
 o1 = initWasm('(module (func) (export "" 0))');
--- a/js/src/jit-test/tests/debug/wasm-13.js
+++ b/js/src/jit-test/tests/debug/wasm-13.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| skip-if: !wasmDebugSupport()
 // Tests that wasm module scripts has inspectable globals and memory.
 
 load(libdir + "wasm.js");
 load(libdir + 'eqArrayHelper.js');
 
 function monitorGlobalValues(wast, lib, expected) {
     function setupFrame(frame) {
         var globals = {};
--- a/js/src/jit-test/tests/debug/wasm-breakpoint.js
+++ b/js/src/jit-test/tests/debug/wasm-breakpoint.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm module scripts handles basic breakpoint operations.
 
 load(libdir + "wasm.js");
 
 function runTest(wast, initFunc, doneFunc) {
     let g = newGlobal({newCompartment: true});
     let dbg = new Debugger(g);
 
--- a/js/src/jit-test/tests/debug/wasm-get-return.js
+++ b/js/src/jit-test/tests/debug/wasm-get-return.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm frame opPop event can access function resumption value.
 
 load(libdir + "wasm.js");
 load(libdir + 'eqArrayHelper.js');
 
 function monitorFrameOnPopReturns(wast, expected) {
     var values = [];
     wasmRunWithDebugger(
--- a/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js
+++ b/js/src/jit-test/tests/debug/wasm-getAllColumnOffsets.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 
 // Tests that wasm module scripts have column and line to bytecode offset
 // information when source text is generated.
 
 load(libdir + "asserts.js");
 
 // Checking if experimental format generates internal source map to binary file
 // by querying debugger scripts getAllColumnOffsets.
--- a/js/src/jit-test/tests/debug/wasm-jseval.js
+++ b/js/src/jit-test/tests/debug/wasm-jseval.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline
+// |jit-test| skip-if: !wasmDebugSupport()
 // Tests that JS can be evaluated on wasm module scripts frames.
 
 load(libdir + "wasm.js");
 
 wasmRunWithDebugger(
     '(module (memory 1 1)\
      (global (mut f64) (f64.const 0.5))\
      (global f32 (f32.const 3.5))\
--- a/js/src/jit-test/tests/debug/wasm-responseurls.js
+++ b/js/src/jit-test/tests/debug/wasm-responseurls.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 // Tests that wasm module can accept URL and sourceMapURL from response
 // when instantiateStreaming is used.
 
 ignoreUnhandledRejections();
 
 try {
     WebAssembly.compileStreaming();
 } catch (err) {
--- a/js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
+++ b/js/src/jit-test/tests/debug/wasm-sourceMappingURL.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 
 // Tests that wasm module sourceMappingURL section is parsed.
 
 load(libdir + "asserts.js");
 load(libdir + "wasm-binary.js");
 
 var g = newGlobal({newCompartment: true});
 var dbg = new Debugger(g);
--- a/js/src/jit-test/tests/debug/wasm-step.js
+++ b/js/src/jit-test/tests/debug/wasm-step.js
@@ -1,9 +1,9 @@
-// |jit-test| test-also-no-wasm-baseline; skip-if: !wasmDebuggingIsSupported()
+// |jit-test| test-also-wasm-compiler-ion; skip-if: !wasmDebuggingIsSupported()
 
 // Tests that wasm module scripts raises onEnterFrame and onLeaveFrame events.
 
 load(libdir + "wasm.js");
 
 // Checking if we stop at every wasm instruction during step.
 var onEnterFrameCalled, onLeaveFrameCalled, onStepCalled;
 wasmRunWithDebugger(
--- a/js/src/jit-test/tests/wasm/bench/directives.txt
+++ b/js/src/jit-test/tests/wasm/bench/directives.txt
@@ -1,1 +1,1 @@
-|jit-test| test-also-no-wasm-baseline; test-also-no-wasm-ion; test-also-wasm-tiering; include:wasm.js
+|jit-test| test-also-wasm-compiler-ion; test-also-wasm-compiler-baseline; test-also-wasm-tiering; include:wasm.js
--- a/js/src/jit-test/tests/wasm/directives.txt
+++ b/js/src/jit-test/tests/wasm/directives.txt
@@ -1,1 +1,1 @@
-|jit-test| test-also-no-wasm-baseline; test-also-no-wasm-ion; test-also-wasm-tiering; include:wasm.js
+|jit-test| test-also-wasm-compiler-ion; test-also-wasm-compiler-baseline; test-also-wasm-tiering; include:wasm.js
--- a/js/src/jit-test/tests/wasm/ion-debugger.js
+++ b/js/src/jit-test/tests/wasm/ion-debugger.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 var g = newGlobal({newCompartment: true});
 g.parent = this;
 g.eval("Debugger(parent).onExceptionUnwind = function () {};");
 lfModule = new WebAssembly.Module(wasmTextToBinary(`
 (module
   (export "f" $func0)
   (func $func0 (result i32)
     i32.const -1
--- a/js/src/jit-test/tests/wasm/regress/bug1440512.js
+++ b/js/src/jit-test/tests/wasm/regress/bug1440512.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 var g = newGlobal({newCompartment: true});
 var dbg = new g.Debugger(this);
 var dbg = new Debugger;
 var kWasmH0 = 0;
 var kWasmH1 = 0x61;
 var kWasmH2 = 0x73;
 var kWasmH3 = 0x6d;
 var kWasmV0 = 0x1;
--- a/js/src/jit-test/tests/wasm/regress/bug1491322.js
+++ b/js/src/jit-test/tests/wasm/regress/bug1491322.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 var evalInFrame = (function(global) {
     var dbgGlobal = newGlobal({newCompartment: true});
     var dbg = new dbgGlobal.Debugger();
     dbg.addDebuggee(global);
 })(this);
 const Module = WebAssembly.Module;
 const Instance = WebAssembly.Instance;
 var m = new Module(wasmTextToBinary(`(module
--- a/js/src/jit-test/tests/wasm/regress/debug-clone-segment.js
+++ b/js/src/jit-test/tests/wasm/regress/debug-clone-segment.js
@@ -1,8 +1,10 @@
+// |jit-test| skip-if: !wasmDebugSupport()
+//
 var mod = new WebAssembly.Module(wasmTextToBinary(`
     (module
         (func (export "func_0") (result i32)
          call 0
         )
     )
 `));
 
--- a/js/src/jit-test/tests/wasm/regress/debug-exception-in-fast-import.js
+++ b/js/src/jit-test/tests/wasm/regress/debug-exception-in-fast-import.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 g = newGlobal({newCompartment: true});
 g.parent = this;
 g.eval("(" + function() {
     Debugger(parent).onExceptionUnwind = function(frame) {}
 } + ")()")
 
 o = {};
 
--- a/js/src/jit-test/tests/wasm/regress/debug-osr.js
+++ b/js/src/jit-test/tests/wasm/regress/debug-osr.js
@@ -1,8 +1,9 @@
+// |jit-test| skip-if: !wasmDebugSupport()
 g = newGlobal({newCompartment: true});
 g.parent = this;
 g.eval("(" + function() {
     Debugger(parent).onExceptionUnwind = function(frame) {
         frame.older
     }
 } + ")()");
 
--- a/js/src/jit-test/tests/wasm/regress/directives.txt
+++ b/js/src/jit-test/tests/wasm/regress/directives.txt
@@ -1,1 +1,1 @@
-|jit-test| test-also-no-wasm-baseline; test-also-no-wasm-ion; test-also-wasm-tiering; include:wasm.js
+|jit-test| test-also-wasm-compiler-ion; test-also-wasm-compiler-baseline; test-also-wasm-tiering; include:wasm.js
--- a/js/src/jit-test/tests/wasm/spec/directives.txt
+++ b/js/src/jit-test/tests/wasm/spec/directives.txt
@@ -1,1 +1,1 @@
-|jit-test| test-also-no-wasm-baseline; test-also-no-wasm-ion; test-also-wasm-tiering; include:wasm-testharness.js
+|jit-test| test-also-wasm-compiler-ion; test-also-wasm-compiler-baseline; test-also-wasm-tiering; include:wasm-testharness.js
--- a/js/src/jit-test/tests/wasm/timeout/directives.txt
+++ b/js/src/jit-test/tests/wasm/timeout/directives.txt
@@ -1,2 +1,2 @@
-|jit-test| test-also-no-wasm-baseline; test-also-no-wasm-ion; test-also-wasm-tiering
+|jit-test| test-also-wasm-compiler-ion; test-also-wasm-compiler-baseline; test-also-wasm-tiering
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -391,17 +391,17 @@ class JS_PUBLIC_API ContextOptions {
       : baseline_(true),
         ion_(true),
         asmJS_(true),
         wasm_(true),
         wasmVerbose_(false),
         wasmBaseline_(true),
         wasmIon_(true),
 #ifdef ENABLE_WASM_CRANELIFT
-        wasmForceCranelift_(false),
+        wasmCranelift_(false),
 #endif
 #ifdef ENABLE_WASM_REFTYPES
         wasmGc_(false),
 #endif
         testWasmAwaitTier2_(false),
         throwOnAsmJSValidationFailure_(false),
         nativeRegExp_(true),
         asyncStack_(true),
@@ -471,19 +471,19 @@ class JS_PUBLIC_API ContextOptions {
 
   bool wasmIon() const { return wasmIon_; }
   ContextOptions& setWasmIon(bool flag) {
     wasmIon_ = flag;
     return *this;
   }
 
 #ifdef ENABLE_WASM_CRANELIFT
-  bool wasmForceCranelift() const { return wasmForceCranelift_; }
-  ContextOptions& setWasmForceCranelift(bool flag) {
-    wasmForceCranelift_ = flag;
+  bool wasmCranelift() const { return wasmCranelift_; }
+  ContextOptions& setWasmCranelift(bool flag) {
+    wasmCranelift_ = flag;
     return *this;
   }
 #endif
 
   bool testWasmAwaitTier2() const { return testWasmAwaitTier2_; }
   ContextOptions& setTestWasmAwaitTier2(bool flag) {
     testWasmAwaitTier2_ = flag;
     return *this;
@@ -590,17 +590,17 @@ class JS_PUBLIC_API ContextOptions {
   bool baseline_ : 1;
   bool ion_ : 1;
   bool asmJS_ : 1;
   bool wasm_ : 1;
   bool wasmVerbose_ : 1;
   bool wasmBaseline_ : 1;
   bool wasmIon_ : 1;
 #ifdef ENABLE_WASM_CRANELIFT
-  bool wasmForceCranelift_ : 1;
+  bool wasmCranelift_ : 1;
 #endif
 #ifdef ENABLE_WASM_REFTYPES
   bool wasmGc_ : 1;
 #endif
   bool testWasmAwaitTier2_ : 1;
   bool throwOnAsmJSValidationFailure_ : 1;
   bool nativeRegExp_ : 1;
   bool asyncStack_ : 1;
--- a/js/src/shell/fuzz-flags.txt
+++ b/js/src/shell/fuzz-flags.txt
@@ -46,18 +46,18 @@
 # --gc-zeal=2
 # --gc-zeal=10
 --no-cgc
 --no-ggc
 --no-incremental-gc
 
 # wasm flags
 --wasm-gc
---no-wasm-baseline
---no-wasm-ion
+--wasm-compiler=ion
+--wasm-compiler=baseline
 --test-wasm-await-tier2
 
 # CPU instruction set-related
 --enable-avx
 --no-avx
 --no-sse3
 --no-sse4
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -486,19 +486,17 @@ static bool offthreadCompilation = false
 static bool enableBaseline = false;
 static bool enableIon = false;
 static bool enableAsmJS = false;
 static bool enableWasm = false;
 static bool enableNativeRegExp = false;
 static bool enableSharedMemory = SHARED_MEMORY_DEFAULT;
 static bool enableWasmBaseline = false;
 static bool enableWasmIon = false;
-#ifdef ENABLE_WASM_CRANELIFT
-static bool wasmForceCranelift = false;
-#endif
+static bool enableWasmCranelift = false;
 #ifdef ENABLE_WASM_REFTYPES
 static bool enableWasmGc = false;
 #endif
 static bool enableWasmVerbose = false;
 static bool enableTestWasmAwaitTier2 = false;
 static bool enableAsyncStacks = false;
 static bool enableStreams = false;
 #ifdef ENABLE_BIGINT
@@ -10179,33 +10177,48 @@ static MOZ_MUST_USE bool ProcessArgs(JSC
 
   return true;
 }
 
 static bool SetContextOptions(JSContext* cx, const OptionParser& op) {
   enableBaseline = !op.getBoolOption("no-baseline");
   enableIon = !op.getBoolOption("no-ion");
   enableAsmJS = !op.getBoolOption("no-asmjs");
-  enableWasm = !op.getBoolOption("no-wasm");
   enableNativeRegExp = !op.getBoolOption("no-native-regexp");
-  enableWasmBaseline = !op.getBoolOption("no-wasm-baseline");
-  enableWasmIon = !op.getBoolOption("no-wasm-ion");
-#ifdef ENABLE_WASM_CRANELIFT
-  wasmForceCranelift = op.getBoolOption("wasm-force-cranelift");
-#endif
+
+  // Default values for wasm.
+  enableWasm = true;
+  enableWasmBaseline = true;
+  enableWasmIon = true;
+  if (const char* str = op.getStringOption("wasm-compiler")) {
+    if (strcmp(str, "none") == 0) {
+      enableWasm = false;
+    } else if (strcmp(str, "baseline") == 0) {
+      // Baseline is enabled by default.
+      enableWasmIon = false;
+    } else if (strcmp(str, "ion") == 0) {
+      // Ion is enabled by default.
+      enableWasmBaseline = false;
+    } else if (strcmp(str, "cranelift") == 0) {
+      enableWasmBaseline = false;
+      enableWasmIon = false;
+      enableWasmCranelift = true;
+    } else if (strcmp(str, "baseline+ion") == 0) {
+      // Default.
+    } else if (strcmp(str, "baseline+cranelift") == 0) {
+      // Baseline is enabled by default.
+      enableWasmIon = false;
+      enableWasmCranelift = true;
+    } else {
+      return OptionFailure("wasm-compiler", str);
+    }
+  }
+
 #ifdef ENABLE_WASM_REFTYPES
   enableWasmGc = op.getBoolOption("wasm-gc");
-#  ifdef ENABLE_WASM_CRANELIFT
-  if (enableWasmGc && wasmForceCranelift) {
-    fprintf(stderr,
-            "Do not combine --wasm-gc and --wasm-force-cranelift, they are "
-            "incompatible.\n");
-  }
-  enableWasmGc = enableWasmGc && !wasmForceCranelift;
-#  endif
 #endif
   enableWasmVerbose = op.getBoolOption("wasm-verbose");
   enableTestWasmAwaitTier2 = op.getBoolOption("test-wasm-await-tier2");
   enableAsyncStacks = !op.getBoolOption("no-async-stacks");
   enableStreams = !op.getBoolOption("no-streams");
 #ifdef ENABLE_BIGINT
   enableBigInt = !op.getBoolOption("no-bigint");
 #endif
@@ -10213,17 +10226,17 @@ static bool SetContextOptions(JSContext*
   JS::ContextOptionsRef(cx)
       .setBaseline(enableBaseline)
       .setIon(enableIon)
       .setAsmJS(enableAsmJS)
       .setWasm(enableWasm)
       .setWasmBaseline(enableWasmBaseline)
       .setWasmIon(enableWasmIon)
 #ifdef ENABLE_WASM_CRANELIFT
-      .setWasmForceCranelift(wasmForceCranelift)
+      .setWasmCranelift(enableWasmCranelift)
 #endif
 #ifdef ENABLE_WASM_REFTYPES
       .setWasmGc(enableWasmGc)
 #endif
       .setWasmVerbose(enableWasmVerbose)
       .setTestWasmAwaitTier2(enableTestWasmAwaitTier2)
       .setNativeRegExp(enableNativeRegExp)
       .setAsyncStack(enableAsyncStacks);
@@ -10530,17 +10543,17 @@ static void SetWorkerContextOptions(JSCo
   JS::ContextOptionsRef(cx)
       .setBaseline(enableBaseline)
       .setIon(enableIon)
       .setAsmJS(enableAsmJS)
       .setWasm(enableWasm)
       .setWasmBaseline(enableWasmBaseline)
       .setWasmIon(enableWasmIon)
 #ifdef ENABLE_WASM_CRANELIFT
-      .setWasmForceCranelift(wasmForceCranelift)
+      .setWasmCranelift(enableWasmCranelift)
 #endif
 #ifdef ENABLE_WASM_REFTYPES
       .setWasmGc(enableWasmGc)
 #endif
       .setWasmVerbose(enableWasmVerbose)
       .setTestWasmAwaitTier2(enableTestWasmAwaitTier2)
       .setNativeRegExp(enableNativeRegExp);
 
@@ -10927,26 +10940,22 @@ int main(int argc, char** argv, char** e
           "default is the actual number of CPUs. The total number of "
           "background helper threads is the CPU count plus some constant.",
           -1) ||
       !op.addIntOption('\0', "thread-count", "COUNT", "Alias for --cpu-count.",
                        -1) ||
       !op.addBoolOption('\0', "ion", "Enable IonMonkey (default)") ||
       !op.addBoolOption('\0', "no-ion", "Disable IonMonkey") ||
       !op.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") ||
-      !op.addBoolOption('\0', "no-wasm", "Disable WebAssembly compilation") ||
-      !op.addBoolOption('\0', "no-wasm-baseline",
-                        "Disable wasm baseline compiler") ||
-      !op.addBoolOption('\0', "no-wasm-ion", "Disable wasm ion compiler")
-#ifdef ENABLE_WASM_CRANELIFT
-      || !op.addBoolOption('\0', "wasm-force-cranelift",
-                           "Enable wasm Cranelift compiler")
-#endif
-      || !op.addBoolOption('\0', "wasm-verbose",
-                           "Enable WebAssembly verbose logging") ||
+      !op.addStringOption(
+          '\0', "wasm-compiler", "[option]",
+          "Choose to enable a subset of the wasm compilers (valid options are "
+          "none/baseline/ion/cranelift/baseline+ion/baseline+cranelift)") ||
+      !op.addBoolOption('\0', "wasm-verbose",
+                        "Enable WebAssembly verbose logging") ||
       !op.addBoolOption('\0', "test-wasm-await-tier2",
                         "Forcibly activate tiering and block "
                         "instantiation on completion of tier2")
 #ifdef ENABLE_WASM_REFTYPES
       || !op.addBoolOption('\0', "wasm-gc", "Enable wasm GC features")
 #else
       || !op.addBoolOption('\0', "wasm-gc", "No-op")
 #endif
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -299,22 +299,22 @@ class JitTest:
                         test.allow_overrecursed = True
                     elif name == 'valgrind':
                         test.valgrind = options.valgrind
                     elif name == 'tz-pacific':
                         test.tz_pacific = True
                     elif name == 'test-also-noasmjs':
                         if options.asmjs_enabled:
                             test.test_also.append(['--no-asmjs'])
-                    elif name == 'test-also-no-wasm-baseline':
+                    elif name == 'test-also-wasm-compiler-ion':
                         if options.wasm_enabled:
-                            test.test_also.append(['--no-wasm-baseline'])
-                    elif name == 'test-also-no-wasm-ion':
+                            test.test_also.append(['--wasm-compiler=ion'])
+                    elif name == 'test-also-wasm-compiler-baseline':
                         if options.wasm_enabled:
-                            test.test_also.append(['--no-wasm-ion'])
+                            test.test_also.append(['--wasm-compiler=baseline'])
                     elif name == 'test-also-wasm-tiering':
                         if options.wasm_enabled:
                             test.test_also.append(['--test-wasm-await-tier2'])
                     elif name.startswith('test-also='):
                         test.test_also.append([name[len('test-also='):]])
                     elif name.startswith('test-join='):
                         test.test_join.append([name[len('test-join='):]])
                     elif name == 'module':
--- a/js/src/tests/lib/tests.py
+++ b/js/src/tests/lib/tests.py
@@ -38,17 +38,17 @@ JITFLAGS = {
         ['--ion-eager', '--ion-check-range-analysis', '--ion-extra-checks', '--no-sse3'],
         ['--no-baseline', '--no-ion'],
     ],
     'baseline': [
         ['--no-ion'],
     ],
     # Interpreter-only, for tools that cannot handle binary code generation.
     'interp': [
-        ['--no-baseline', '--no-asmjs', '--no-wasm', '--no-native-regexp']
+        ['--no-baseline', '--no-asmjs', '--wasm-compiler=none', '--no-native-regexp']
     ],
     'none': [
         []  # no flags, normal baseline and ion
     ]
 }
 
 
 def get_jitflags(variant, **kwargs):
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -1364,20 +1364,20 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
         validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
         funcDefs_(cx),
         tables_(cx),
         globalMap_(cx),
         sigSet_(cx),
         funcImportMap_(cx),
         arrayViews_(cx),
         compilerEnv_(CompileMode::Once, Tier::Optimized, OptimizedBackend::Ion,
-                     DebugEnabled::False, HasGcTypes::False),
-        env_(HasGcTypes::False, &compilerEnv_, Shareable::False,
+                     DebugEnabled::False, /* gc types */ false),
+        env_(/* gc types */ false, &compilerEnv_, Shareable::False,
              ModuleKind::AsmJS) {
-    compilerEnv_.computeParameters(HasGcTypes::False);
+    compilerEnv_.computeParameters(/* gc types */ false);
     env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
   }
 
  protected:
   MOZ_MUST_USE bool addStandardLibraryMathInfo() {
     static constexpr struct {
       const char* name;
       AsmJSMathBuiltinFunction func;
@@ -2132,18 +2132,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED Modu
     if (parser_.ss->filename()) {
       scriptedCaller.line = 0;  // unused
       scriptedCaller.filename = DuplicateString(parser_.ss->filename());
       if (!scriptedCaller.filename) {
         return nullptr;
       }
     }
 
-    MutableCompileArgs args =
-        cx_->new_<CompileArgs>(cx_, std::move(scriptedCaller));
+    SharedCompileArgs args = CompileArgs::build(cx_, std::move(scriptedCaller));
     if (!args) {
       return nullptr;
     }
 
     uint32_t codeSectionSize = 0;
     for (const Func& func : funcDefs_) {
       codeSectionSize += func.bytes().length();
     }
--- a/js/src/wasm/Makefile
+++ b/js/src/wasm/Makefile
@@ -18,17 +18,17 @@ help:
 
 update:
 	[ -d ./spec ] || git clone https://github.com/webassembly/spec ./spec
 	(cd ./spec/interpreter && make)
 	./spec/test/build.py \
 		--use-sync \
 		--js ../jit-test/tests/wasm/spec \
 		--html ../../../testing/web-platform/mozilla/tests/wasm
-	echo "|jit-test| test-also-no-wasm-baseline; test-also-no-wasm-ion; test-also-wasm-tiering; include:wasm-testharness.js" > ../jit-test/tests/wasm/spec/directives.txt
+	echo "|jit-test| test-also-wasm-compiler-ion; test-also-wasm-compiler-baseline; test-also-wasm-tiering; include:wasm-testharness.js" > ../jit-test/tests/wasm/spec/directives.txt
 	echo "|jit-test| skip-if:true" > ../jit-test/tests/wasm/spec/harness/directives.txt
 
 run:
 	@[ -z $(MOZCONFIG) ] && echo "You need to define the MOZCONFIG env variable first."
 	@[ -z $(MOZCONFIG) ] || ../../../mach wpt /_mozilla/wasm
 
 expectations:
 	@[ -z $(MOZCONFIG) ] && echo "You need to define the MOZCONFIG env variable first." || true
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -11444,31 +11444,31 @@ bool BaseCompiler::emitBody() {
       // Memory Related
       case uint16_t(Op::GrowMemory):
         CHECK_NEXT(emitGrowMemory());
       case uint16_t(Op::CurrentMemory):
         CHECK_NEXT(emitCurrentMemory());
 
 #ifdef ENABLE_WASM_GC
       case uint16_t(Op::RefEq):
-        if (env_.gcTypesEnabled() == HasGcTypes::False) {
+        if (!env_.gcTypesEnabled()) {
           return iter_.unrecognizedOpcode(&op);
         }
         CHECK_NEXT(
             emitComparison(emitCompareRef, ValType::AnyRef, Assembler::Equal));
 #endif
 #ifdef ENABLE_WASM_REFTYPES
       case uint16_t(Op::RefNull):
-        if (env_.gcTypesEnabled() == HasGcTypes::False) {
+        if (!env_.gcTypesEnabled()) {
           return iter_.unrecognizedOpcode(&op);
         }
         CHECK_NEXT(emitRefNull());
         break;
       case uint16_t(Op::RefIsNull):
-        if (env_.gcTypesEnabled() == HasGcTypes::False) {
+        if (!env_.gcTypesEnabled()) {
           return iter_.unrecognizedOpcode(&op);
         }
         CHECK_NEXT(
             emitConversion(emitRefIsNull, ValType::AnyRef, ValType::I32));
         break;
 #endif
 
       // "Miscellaneous" operations
@@ -11553,32 +11553,32 @@ bool BaseCompiler::emitBody() {
             CHECK_NEXT(emitTableGrow());
           case uint16_t(MiscOp::TableSet):
             CHECK_NEXT(emitTableSet());
           case uint16_t(MiscOp::TableSize):
             CHECK_NEXT(emitTableSize());
 #endif
 #ifdef ENABLE_WASM_GC
           case uint16_t(MiscOp::StructNew):
-            if (env_.gcTypesEnabled() == HasGcTypes::False) {
+            if (!env_.gcTypesEnabled()) {
               return iter_.unrecognizedOpcode(&op);
             }
             CHECK_NEXT(emitStructNew());
           case uint16_t(MiscOp::StructGet):
-            if (env_.gcTypesEnabled() == HasGcTypes::False) {
+            if (!env_.gcTypesEnabled()) {
               return iter_.unrecognizedOpcode(&op);
             }
             CHECK_NEXT(emitStructGet());
           case uint16_t(MiscOp::StructSet):
-            if (env_.gcTypesEnabled() == HasGcTypes::False) {
+            if (!env_.gcTypesEnabled()) {
               return iter_.unrecognizedOpcode(&op);
             }
             CHECK_NEXT(emitStructSet());
           case uint16_t(MiscOp::StructNarrow):
-            if (env_.gcTypesEnabled() == HasGcTypes::False) {
+            if (!env_.gcTypesEnabled()) {
               return iter_.unrecognizedOpcode(&op);
             }
             CHECK_NEXT(emitStructNarrow());
 #endif
           default:
             break;
         }  // switch (op.b1)
         return iter_.unrecognizedOpcode(&op);
--- a/js/src/wasm/WasmCode.cpp
+++ b/js/src/wasm/WasmCode.cpp
@@ -643,18 +643,17 @@ struct ProjectLazyFuncIndex {
       : funcExports(funcExports) {}
   uint32_t operator[](size_t index) const {
     return funcExports[index].funcIndex;
   }
 };
 
 static constexpr unsigned LAZY_STUB_LIFO_DEFAULT_CHUNK_SIZE = 8 * 1024;
 
-bool LazyStubTier::createMany(HasGcTypes gcTypesConfigured,
-                              const Uint32Vector& funcExportIndices,
+bool LazyStubTier::createMany(const Uint32Vector& funcExportIndices,
                               const CodeTier& codeTier,
                               size_t* stubSegmentIndex) {
   MOZ_ASSERT(funcExportIndices.length());
 
   LifoAlloc lifo(LAZY_STUB_LIFO_DEFAULT_CHUNK_SIZE);
   TempAllocator alloc(&lifo);
   JitContext jitContext(&alloc);
   WasmMacroAssembler masm(alloc);
@@ -668,18 +667,17 @@ bool LazyStubTier::createMany(HasGcTypes
   for (uint32_t funcExportIndex : funcExportIndices) {
     const FuncExport& fe = funcExports[funcExportIndex];
     numExpectedRanges += fe.funcType().temporarilyUnsupportedAnyRef() ? 1 : 2;
     void* calleePtr =
         moduleSegmentBase + metadata.codeRange(fe).funcNormalEntry();
     Maybe<ImmPtr> callee;
     callee.emplace(calleePtr, ImmPtr::NoCheckToken());
     if (!GenerateEntryStubs(masm, funcExportIndex, fe, callee,
-                            /* asmjs */ false, gcTypesConfigured,
-                            &codeRanges)) {
+                            /* asmjs */ false, &codeRanges)) {
       return false;
     }
   }
   MOZ_ASSERT(codeRanges.length() == numExpectedRanges,
              "incorrect number of entries per function");
 
   masm.finish();
 
@@ -764,18 +762,17 @@ bool LazyStubTier::createMany(HasGcTypes
 bool LazyStubTier::createOne(uint32_t funcExportIndex,
                              const CodeTier& codeTier) {
   Uint32Vector funcExportIndexes;
   if (!funcExportIndexes.append(funcExportIndex)) {
     return false;
   }
 
   size_t stubSegmentIndex;
-  if (!createMany(codeTier.code().metadata().temporaryGcTypesConfigured,
-                  funcExportIndexes, codeTier, &stubSegmentIndex)) {
+  if (!createMany(funcExportIndexes, codeTier, &stubSegmentIndex)) {
     return false;
   }
 
   const UniqueLazyStubSegment& segment = stubSegments_[stubSegmentIndex];
   const CodeRangeVector& codeRanges = segment->codeRanges();
 
   // Functions that have anyref in their sig don't get a jit entry.
   if (codeTier.metadata()
@@ -792,27 +789,25 @@ bool LazyStubTier::createOne(uint32_t fu
 
   const CodeRange& cr = codeRanges[codeRanges.length() - 1];
   MOZ_ASSERT(cr.isJitEntry());
 
   codeTier.code().setJitEntry(cr.funcIndex(), segment->base() + cr.begin());
   return true;
 }
 
-bool LazyStubTier::createTier2(HasGcTypes gcTypesConfigured,
-                               const Uint32Vector& funcExportIndices,
+bool LazyStubTier::createTier2(const Uint32Vector& funcExportIndices,
                                const CodeTier& codeTier,
                                Maybe<size_t>* outStubSegmentIndex) {
   if (!funcExportIndices.length()) {
     return true;
   }
 
   size_t stubSegmentIndex;
-  if (!createMany(gcTypesConfigured, funcExportIndices, codeTier,
-                  &stubSegmentIndex)) {
+  if (!createMany(funcExportIndices, codeTier, &stubSegmentIndex)) {
     return false;
   }
 
   outStubSegmentIndex->emplace(stubSegmentIndex);
   return true;
 }
 
 void LazyStubTier::setJitEntries(const Maybe<size_t>& stubSegmentIndex,
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -307,28 +307,26 @@ enum class MemoryUsage { None = false, U
 // The Metadata structure is split into tier-invariant and tier-variant parts;
 // the former points to instances of the latter.  Additionally, the asm.js
 // subsystem subclasses the Metadata, adding more tier-invariant data, some of
 // which is serialized.  See AsmJS.cpp.
 
 struct MetadataCacheablePod {
   ModuleKind kind;
   MemoryUsage memoryUsage;
-  HasGcTypes temporaryGcTypesConfigured;
   uint32_t minMemoryLength;
   uint32_t globalDataLength;
   Maybe<uint32_t> maxMemoryLength;
   Maybe<uint32_t> startFuncIndex;
   Maybe<uint32_t> nameCustomSectionIndex;
   bool filenameIsURL;
 
   explicit MetadataCacheablePod(ModuleKind kind)
       : kind(kind),
         memoryUsage(MemoryUsage::None),
-        temporaryGcTypesConfigured(HasGcTypes::False),
         minMemoryLength(0),
         globalDataLength(0),
         filenameIsURL(false) {}
 };
 
 typedef uint8_t ModuleHash[8];
 typedef Vector<ValTypeVector, 0, SystemAllocPolicy> FuncArgTypesVector;
 typedef Vector<ExprType, 0, SystemAllocPolicy> FuncReturnTypesVector;
@@ -496,18 +494,17 @@ using LazyFuncExportVector = Vector<Lazy
 // write lazy stubs at any time while a background thread can regenerate lazy
 // stubs for tier2 at any time.
 
 class LazyStubTier {
   LazyStubSegmentVector stubSegments_;
   LazyFuncExportVector exports_;
   size_t lastStubSegmentIndex_;
 
-  bool createMany(HasGcTypes gcTypesEnabled,
-                  const Uint32Vector& funcExportIndices,
+  bool createMany(const Uint32Vector& funcExportIndices,
                   const CodeTier& codeTier, size_t* stubSegmentIndex);
 
  public:
   LazyStubTier() : lastStubSegmentIndex_(0) {}
 
   bool empty() const { return stubSegments_.empty(); }
   bool hasStub(uint32_t funcIndex) const;
 
@@ -518,18 +515,17 @@ class LazyStubTier {
   // Creates one lazy stub for the exported function, for which the jit entry
   // will be set to the lazily-generated one.
   bool createOne(uint32_t funcExportIndex, const CodeTier& codeTier);
 
   // Create one lazy stub for all the functions in funcExportIndices, putting
   // them in a single stub. Jit entries won't be used until
   // setJitEntries() is actually called, after the Code owner has committed
   // tier2.
-  bool createTier2(HasGcTypes gcTypesConfigured,
-                   const Uint32Vector& funcExportIndices,
+  bool createTier2(const Uint32Vector& funcExportIndices,
                    const CodeTier& codeTier, Maybe<size_t>* stubSegmentIndex);
   void setJitEntries(const Maybe<size_t>& stubSegmentIndex, const Code& code);
 
   void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code,
                      size_t* data) const;
 };
 
 // CodeTier contains all the data related to a given compilation tier. It is
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -19,16 +19,17 @@
 #include "wasm/WasmCompile.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Unused.h"
 
 #include "jit/ProcessExecutableMemory.h"
 #include "util/Text.h"
 #include "wasm/WasmBaselineCompile.h"
+#include "wasm/WasmCraneliftCompile.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmOpIter.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
@@ -67,37 +68,70 @@ uint32_t wasm::ObservedCPUFeatures() {
   return MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS);
 #elif defined(JS_CODEGEN_NONE)
   return 0;
 #else
 #  error "unknown architecture"
 #endif
 }
 
-CompileArgs::CompileArgs(JSContext* cx, ScriptedCaller&& scriptedCaller)
-    : scriptedCaller(std::move(scriptedCaller)) {
-  bool gcEnabled = HasReftypesSupport(cx);
-
-  baselineEnabled = cx->options().wasmBaseline();
-  ionEnabled = cx->options().wasmIon();
+SharedCompileArgs
+CompileArgs::build(JSContext* cx, ScriptedCaller&& scriptedCaller)
+{
+  bool baseline = BaselineCanCompile() && cx->options().wasmBaseline();
+  bool ion = IonCanCompile() && cx->options().wasmIon();
 #ifdef ENABLE_WASM_CRANELIFT
-  forceCranelift = cx->options().wasmForceCranelift();
+  bool cranelift = CraneliftCanCompile() && cx->options().wasmCranelift();
 #else
-  forceCranelift = false;
+  bool cranelift = false;
 #endif
-  sharedMemoryEnabled =
-      cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
-  gcTypesConfigured = gcEnabled ? HasGcTypes::True : HasGcTypes::False;
-  testTiering = cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2;
 
   // Debug information such as source view or debug traps will require
   // additional memory and permanently stay in baseline code, so we try to
   // only enable it when a developer actually cares: when the debugger tab
   // is open.
-  debugEnabled = cx->realm()->debuggerObservesAsmJS();
+  bool debug = cx->realm()->debuggerObservesAsmJS();
+  bool gc = cx->options().wasmGc();
+
+  bool sharedMemory = cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled();
+  bool forceTiering = cx->options().testWasmAwaitTier2() || JitOptions.wasmDelayTier2;
+
+  if (debug || gc) {
+    if (!baseline) {
+      JS_ReportErrorASCII(cx, "can't use wasm debug/gc without baseline");
+      return nullptr;
+    }
+    ion = false;
+    cranelift = false;
+  }
+
+  if (forceTiering && (!baseline || (!cranelift && !ion))) {
+    // This can happen only in testing, and in this case we don't have a
+    // proper way to signal the error, so just silently override the default,
+    // instead of adding a skip-if directive to every test using debug/gc.
+    forceTiering = false;
+  }
+
+  // HasCompilerSupport() should prevent failure here.
+  MOZ_RELEASE_ASSERT(baseline || ion || cranelift);
+
+  CompileArgs* target = cx->new_<CompileArgs>(std::move(scriptedCaller));
+  if (!target) {
+    return nullptr;
+  }
+
+  target->baselineEnabled = baseline;
+  target->ionEnabled = ion;
+  target->craneliftEnabled = cranelift;
+  target->debugEnabled = debug;
+  target->sharedMemoryEnabled = sharedMemory;
+  target->forceTiering = forceTiering;
+  target->gcEnabled = gc;
+
+  return target;
 }
 
 // Classify the current system as one of a set of recognizable classes.  This
 // really needs to get our tier-1 systems right.
 //
 // TODO: We don't yet have a good measure of how fast a system is.  We
 // distinguish between mobile and desktop because these are very different kinds
 // of systems, but we could further distinguish between low / medium / high end
@@ -380,84 +414,75 @@ static bool TieringBeneficial(uint32_t c
 }
 
 CompilerEnvironment::CompilerEnvironment(const CompileArgs& args)
     : state_(InitialWithArgs), args_(&args) {}
 
 CompilerEnvironment::CompilerEnvironment(CompileMode mode, Tier tier,
                                          OptimizedBackend optimizedBackend,
                                          DebugEnabled debugEnabled,
-                                         HasGcTypes gcTypesConfigured)
+                                         bool gcTypesConfigured)
     : state_(InitialWithModeTierDebug),
       mode_(mode),
       tier_(tier),
       optimizedBackend_(optimizedBackend),
       debug_(debugEnabled),
       gcTypes_(gcTypesConfigured) {}
 
-void CompilerEnvironment::computeParameters(HasGcTypes gcFeatureOptIn) {
+void CompilerEnvironment::computeParameters(bool gcFeatureOptIn) {
   MOZ_ASSERT(state_ == InitialWithModeTierDebug);
 
-  if (gcTypes_ == HasGcTypes::True) {
+  if (gcTypes_) {
     gcTypes_ = gcFeatureOptIn;
   }
   state_ = Computed;
 }
 
-void CompilerEnvironment::computeParameters(Decoder& d,
-                                            HasGcTypes gcFeatureOptIn) {
+void CompilerEnvironment::computeParameters(Decoder& d, bool gcFeatureOptIn) {
   MOZ_ASSERT(!isComputed());
 
   if (state_ == InitialWithModeTierDebug) {
     computeParameters(gcFeatureOptIn);
     return;
   }
 
-  bool gcEnabled = args_->gcTypesConfigured == HasGcTypes::True &&
-                   gcFeatureOptIn == HasGcTypes::True;
-  bool argBaselineEnabled = args_->baselineEnabled || gcEnabled;
-  bool argIonEnabled = args_->ionEnabled && !gcEnabled;
-  bool argDebugEnabled = args_->debugEnabled;
+  bool gcEnabled = args_->gcEnabled && gcFeatureOptIn;
+  bool baselineEnabled = args_->baselineEnabled;
+  bool ionEnabled = args_->ionEnabled;
+  bool debugEnabled = args_->debugEnabled;
+  bool craneliftEnabled = args_->craneliftEnabled;
+  bool forceTiering = args_->forceTiering;
+
+  bool hasSecondTier = ionEnabled || craneliftEnabled;
+  MOZ_ASSERT_IF(gcEnabled || debugEnabled, baselineEnabled);
+  MOZ_ASSERT_IF(forceTiering, baselineEnabled && hasSecondTier);
+
+  // HasCompilerSupport() should prevent failure here
+  MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled || craneliftEnabled);
 
   uint32_t codeSectionSize = 0;
 
   SectionRange range;
   if (StartsCodeSection(d.begin(), d.end(), &range)) {
     codeSectionSize = range.size;
   }
 
-  // Attempt to default to ion if baseline is disabled.
-  bool baselineEnabled = BaselineCanCompile() && argBaselineEnabled;
-  bool debugEnabled = BaselineCanCompile() && argDebugEnabled;
-  bool ionEnabled = IonCanCompile() && (argIonEnabled || !baselineEnabled);
-#ifdef ENABLE_WASM_CRANELIFT
-  bool forceCranelift = args_->forceCranelift;
-#endif
-
-  // HasCompilerSupport() should prevent failure here
-  MOZ_RELEASE_ASSERT(baselineEnabled || ionEnabled);
-
-  if (baselineEnabled && ionEnabled && !debugEnabled && CanUseExtraThreads() &&
-      (TieringBeneficial(codeSectionSize) || args_->testTiering)) {
+  if (baselineEnabled && hasSecondTier && CanUseExtraThreads() &&
+      (TieringBeneficial(codeSectionSize) || forceTiering)) {
     mode_ = CompileMode::Tier1;
     tier_ = Tier::Baseline;
   } else {
     mode_ = CompileMode::Once;
-    tier_ = debugEnabled || !ionEnabled ? Tier::Baseline : Tier::Optimized;
+    tier_ = hasSecondTier ? Tier::Optimized : Tier::Baseline;
   }
 
-  optimizedBackend_ = OptimizedBackend::Ion;
-#ifdef ENABLE_WASM_CRANELIFT
-  if (forceCranelift) {
-    optimizedBackend_ = OptimizedBackend::Cranelift;
-  }
-#endif
+  optimizedBackend_ = craneliftEnabled ? OptimizedBackend::Cranelift : OptimizedBackend::Ion;
 
   debug_ = debugEnabled ? DebugEnabled::True : DebugEnabled::False;
-  gcTypes_ = gcEnabled ? HasGcTypes::True : HasGcTypes::False;
+  gcTypes_ = gcEnabled;
   state_ = Computed;
 }
 
 template <class DecoderT>
 static bool DecodeFunctionBody(DecoderT& d, ModuleGenerator& mg,
                                uint32_t funcIndex) {
   uint32_t bodySize;
   if (!d.readVarU32(&bodySize)) {
@@ -519,17 +544,17 @@ SharedModule wasm::CompileBuffer(const C
                                  const ShareableBytes& bytecode,
                                  UniqueChars* error,
                                  UniqueCharsVector* warnings,
                                  UniqueLinkData* maybeLinkData) {
   Decoder d(bytecode.bytes, 0, error, warnings);
 
   CompilerEnvironment compilerEnv(args);
   ModuleEnvironment env(
-      args.gcTypesConfigured, &compilerEnv,
+      args.gcEnabled, &compilerEnv,
       args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
   if (!DecodeModuleEnvironment(d, &env)) {
     return nullptr;
   }
 
   ModuleGenerator mg(args, &env, nullptr, error);
   if (!mg.init()) {
     return nullptr;
@@ -546,34 +571,32 @@ SharedModule wasm::CompileBuffer(const C
   return mg.finishModule(bytecode, nullptr, maybeLinkData);
 }
 
 void wasm::CompileTier2(const CompileArgs& args, const Bytes& bytecode,
                         const Module& module, Atomic<bool>* cancelled) {
   UniqueChars error;
   Decoder d(bytecode, 0, &error);
 
-  HasGcTypes gcTypesConfigured =
-      HasGcTypes::False;  // No optimized backend support yet
+  bool gcTypesConfigured = false;  // No optimized backend support yet
   OptimizedBackend optimizedBackend =
-      args.forceCranelift ? OptimizedBackend::Cranelift : OptimizedBackend::Ion;
+      args.craneliftEnabled ? OptimizedBackend::Cranelift : OptimizedBackend::Ion;
 
   CompilerEnvironment compilerEnv(CompileMode::Tier2, Tier::Optimized,
                                   optimizedBackend, DebugEnabled::False,
                                   gcTypesConfigured);
 
   ModuleEnvironment env(
       gcTypesConfigured, &compilerEnv,
       args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
   if (!DecodeModuleEnvironment(d, &env)) {
     return;
   }
 
-  MOZ_ASSERT(env.gcTypesEnabled() == HasGcTypes::False,
-             "can't ion-compile with gc types yet");
+  MOZ_ASSERT(!env.gcTypesEnabled(), "can't ion-compile with gc types yet");
 
   ModuleGenerator mg(args, &env, cancelled, &error);
   if (!mg.init()) {
     return;
   }
 
   if (!DecodeCodeSection(env, d, mg)) {
     return;
@@ -673,17 +696,17 @@ SharedModule wasm::CompileStreaming(
     const Atomic<bool>& cancelled, UniqueChars* error,
     UniqueCharsVector* warnings) {
   CompilerEnvironment compilerEnv(args);
   Maybe<ModuleEnvironment> env;
 
   {
     Decoder d(envBytes, 0, error, warnings);
 
-    env.emplace(args.gcTypesConfigured, &compilerEnv,
+    env.emplace(args.gcEnabled, &compilerEnv,
                 args.sharedMemoryEnabled ? Shareable::True : Shareable::False);
     if (!DecodeModuleEnvironment(d, env.ptr())) {
       return nullptr;
     }
 
     MOZ_ASSERT(d.done());
   }
 
--- a/js/src/wasm/WasmCompile.h
+++ b/js/src/wasm/WasmCompile.h
@@ -37,43 +37,54 @@ struct ScriptedCaller {
   bool filenameIsURL;
   unsigned line;
 
   ScriptedCaller() : filenameIsURL(false), line(0) {}
 };
 
 // Describes all the parameters that control wasm compilation.
 
+struct CompileArgs;
+typedef RefPtr<CompileArgs> MutableCompileArgs;
+typedef RefPtr<const CompileArgs> SharedCompileArgs;
+
 struct CompileArgs : ShareableBase<CompileArgs> {
   ScriptedCaller scriptedCaller;
   UniqueChars sourceMapURL;
+
   bool baselineEnabled;
-  bool forceCranelift;
-  bool debugEnabled;
   bool ionEnabled;
+  bool craneliftEnabled;
+  bool debugEnabled;
   bool sharedMemoryEnabled;
-  HasGcTypes gcTypesConfigured;
-  bool testTiering;
+  bool forceTiering;
+  bool gcEnabled;
+
+  // CompileArgs has two constructors:
+  //
+  // - one through a factory function `build`, which checks that flags are
+  // consistent with each other.
+  // - one that gives complete access to underlying fields.
+  //
+  // You should use the first one in general, unless you have a very good
+  // reason (i.e. no JSContext around and you know which flags have been used).
+
+  static SharedCompileArgs build(JSContext* cx, ScriptedCaller&& scriptedCaller);
 
   explicit CompileArgs(ScriptedCaller&& scriptedCaller)
       : scriptedCaller(std::move(scriptedCaller)),
         baselineEnabled(false),
-        forceCranelift(false),
+        ionEnabled(false),
+        craneliftEnabled(false),
         debugEnabled(false),
-        ionEnabled(false),
         sharedMemoryEnabled(false),
-        gcTypesConfigured(HasGcTypes::False),
-        testTiering(false) {}
-
-  CompileArgs(JSContext* cx, ScriptedCaller&& scriptedCaller);
+        forceTiering(false),
+        gcEnabled(false) {}
 };
 
-typedef RefPtr<CompileArgs> MutableCompileArgs;
-typedef RefPtr<const CompileArgs> SharedCompileArgs;
-
 // Return the estimated compiled (machine) code size for the given bytecode size
 // compiled at the given tier.
 
 double EstimateCompiledCodeSize(Tier tier, size_t bytecodeSize);
 
 // Compile the given WebAssembly bytecode with the given arguments into a
 // wasm::Module. On success, the Module is returned. On failure, the returned
 // SharedModule pointer is null and either:
--- a/js/src/wasm/WasmCraneliftCompile.cpp
+++ b/js/src/wasm/WasmCraneliftCompile.cpp
@@ -25,16 +25,24 @@
 #include "wasm/WasmGenerator.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
+bool wasm::CraneliftCanCompile() {
+#ifdef JS_CODEGEN_X64
+  return true;
+#else
+  return false;
+#endif
+}
+
 static inline SymbolicAddress ToSymbolicAddress(BD_SymbolicAddress bd) {
   switch (bd) {
     case BD_SymbolicAddress::GrowMemory:
       return SymbolicAddress::GrowMemory;
     case BD_SymbolicAddress::CurrentMemory:
       return SymbolicAddress::CurrentMemory;
     case BD_SymbolicAddress::FloorF32:
       return SymbolicAddress::FloorF;
@@ -269,16 +277,19 @@ const GlobalDesc* env_global(const Crane
 }
 
 bool wasm::CraneliftCompileFunctions(const ModuleEnvironment& env,
                                      LifoAlloc& lifo,
                                      const FuncCompileInputVector& inputs,
                                      CompiledCode* code,
                                      ExclusiveDeferredValidationState& dvs,
                                      UniqueChars* error) {
+
+  MOZ_RELEASE_ASSERT(CraneliftCanCompile());
+
   MOZ_ASSERT(env.tier() == Tier::Optimized);
   MOZ_ASSERT(env.optimizedBackend() == OptimizedBackend::Cranelift);
   MOZ_ASSERT(!env.isAsmJS());
 
   AutoCranelift compiler(env);
   if (!compiler.init()) {
     return false;
   }
--- a/js/src/wasm/WasmCraneliftCompile.h
+++ b/js/src/wasm/WasmCraneliftCompile.h
@@ -21,16 +21,22 @@
 
 #include "mozilla/Attributes.h"
 
 #include "wasm/WasmGenerator.h"
 
 namespace js {
 namespace wasm {
 
+#ifdef ENABLE_WASM_CRANELIFT
+MOZ_MUST_USE bool CraneliftCanCompile();
+#else
+MOZ_MUST_USE inline bool CraneliftCanCompile() { return false; }
+#endif
+
 // Generates code with Cranelift.
 MOZ_MUST_USE bool CraneliftCompileFunctions(
     const ModuleEnvironment& env, LifoAlloc& lifo,
     const FuncCompileInputVector& inputs, CompiledCode* code,
     ExclusiveDeferredValidationState& dvs, UniqueChars* error);
 
 }  // namespace wasm
 }  // namespace js
--- a/js/src/wasm/WasmGenerator.cpp
+++ b/js/src/wasm/WasmGenerator.cpp
@@ -1051,17 +1051,16 @@ UniqueCodeTier ModuleGenerator::finishCo
 SharedMetadata ModuleGenerator::finishMetadata(const Bytes& bytecode) {
   // Finish initialization of Metadata, which is only needed for constructing
   // the initial Module, not for tier-2 compilation.
   MOZ_ASSERT(mode() != CompileMode::Tier2);
 
   // Copy over data from the ModuleEnvironment.
 
   metadata_->memoryUsage = env_->memoryUsage;
-  metadata_->temporaryGcTypesConfigured = env_->gcTypesConfigured;
   metadata_->minMemoryLength = env_->minMemoryLength;
   metadata_->maxMemoryLength = env_->maxMemoryLength;
   metadata_->startFuncIndex = env_->startFuncIndex;
   metadata_->tables = std::move(env_->tables);
   metadata_->globals = std::move(env_->globals);
   metadata_->nameCustomSectionIndex = env_->nameCustomSectionIndex;
   metadata_->moduleName = env_->moduleName;
   metadata_->funcNames = std::move(env_->funcNames);
--- a/js/src/wasm/WasmJS.cpp
+++ b/js/src/wasm/WasmJS.cpp
@@ -31,16 +31,17 @@
 #include "js/Printf.h"
 #include "js/PropertySpec.h"  // JS_{PS,FN}{,_END}
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/Interpreter.h"
 #include "vm/StringType.h"
 #include "wasm/WasmBaselineCompile.h"
 #include "wasm/WasmCompile.h"
+#include "wasm/WasmCraneliftCompile.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmIonCompile.h"
 #include "wasm/WasmModule.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmStubs.h"
 #include "wasm/WasmValidate.h"
 
 #include "vm/ArrayBufferObject-inl.h"
@@ -54,17 +55,17 @@ using namespace js::wasm;
 using mozilla::CheckedInt;
 using mozilla::Nothing;
 using mozilla::RangedPtr;
 
 extern mozilla::Atomic<bool> fuzzingSafe;
 
 bool wasm::HasReftypesSupport(JSContext* cx) {
 #ifdef ENABLE_WASM_CRANELIFT
-  if (cx->options().wasmForceCranelift()) {
+  if (cx->options().wasmCranelift()) {
     return false;
   }
 #endif
 #ifdef ENABLE_WASM_REFTYPES
   return cx->options().wasmGc() && cx->options().wasmBaseline();
 #else
   return false;
 #endif
@@ -97,42 +98,49 @@ bool wasm::HasCompilerSupport(JSContext*
   }
 
 #ifdef JS_SIMULATOR
   if (!Simulator::supportsAtomics()) {
     return false;
   }
 #endif
 
-  return BaselineCanCompile() || IonCanCompile();
+  return BaselineCanCompile() || IonCanCompile() || CraneliftCanCompile();
+}
+
+bool wasm::HasOptimizedCompilerTier(JSContext* cx) {
+  return (cx->options().wasmIon() && IonCanCompile())
+#ifdef ENABLE_WASM_CRANELIFT
+         || (cx->options().wasmCranelift() && CraneliftCanCompile())
+#endif
+  ;
 }
 
 // Return whether wasm compilation is allowed by prefs.  This check
 // only makes sense if HasCompilerSupport() is true.
 static bool HasAvailableCompilerTier(JSContext* cx) {
   return (cx->options().wasmBaseline() && BaselineCanCompile()) ||
-         (cx->options().wasmIon() && IonCanCompile());
+         HasOptimizedCompilerTier(cx);
 }
 
 bool wasm::HasSupport(JSContext* cx) {
   return cx->options().wasm() && HasCompilerSupport(cx) &&
          HasAvailableCompilerTier(cx);
 }
 
 bool wasm::HasStreamingSupport(JSContext* cx) {
   // This should match EnsureStreamSupport().
-
   return HasSupport(cx) &&
          cx->runtime()->offThreadPromiseState.ref().initialized() &&
          CanUseExtraThreads() && cx->runtime()->consumeStreamCallback &&
          cx->runtime()->reportStreamErrorCallback;
 }
 
 bool wasm::HasCachingSupport(JSContext* cx) {
-  return HasStreamingSupport(cx) && cx->options().wasmIon() && IonCanCompile();
+  return HasStreamingSupport(cx) && HasOptimizedCompilerTier(cx);
 }
 
 static bool ToWebAssemblyValue(JSContext* cx, ValType targetType, HandleValue v,
                                MutableHandleVal val) {
   switch (targetType.code()) {
     case ValType::I32: {
       int32_t i32;
       if (!ToInt32(cx, v, &i32)) {
@@ -404,18 +412,17 @@ bool wasm::Eval(JSContext* cx, Handle<Ty
     return false;
   }
 
   ScriptedCaller scriptedCaller;
   if (!DescribeScriptedCaller(cx, &scriptedCaller, "wasm_eval")) {
     return false;
   }
 
-  MutableCompileArgs compileArgs =
-      cx->new_<CompileArgs>(cx, std::move(scriptedCaller));
+  SharedCompileArgs compileArgs = CompileArgs::build(cx, std::move(scriptedCaller));
   if (!compileArgs) {
     return false;
   }
 
   UniqueChars error;
   UniqueCharsVector warnings;
   SharedModule module =
       CompileBuffer(*compileArgs, *bytecode, &error, &warnings);
@@ -1027,24 +1034,24 @@ static bool GetBufferSource(JSContext* c
   if (!(*bytecode)->append(dataPointer.unwrap(), byteLength)) {
     ReportOutOfMemory(cx);
     return false;
   }
 
   return true;
 }
 
-static MutableCompileArgs InitCompileArgs(JSContext* cx,
-                                          const char* introducer) {
+static SharedCompileArgs InitCompileArgs(JSContext* cx,
+                                         const char* introducer) {
   ScriptedCaller scriptedCaller;
   if (!DescribeScriptedCaller(cx, &scriptedCaller, introducer)) {
     return nullptr;
   }
 
-  return cx->new_<CompileArgs>(cx, std::move(scriptedCaller));
+  return CompileArgs::build(cx, std::move(scriptedCaller));
 }
 
 static bool ReportCompileWarnings(JSContext* cx,
                                   const UniqueCharsVector& warnings) {
   // Avoid spamming the console.
   size_t numWarnings = Min<size_t>(warnings.length(), 3);
 
   for (size_t i = 0; i < numWarnings; i++) {
@@ -3333,29 +3340,29 @@ class ResolveResponseClosure : public Na
   static void finalize(FreeOp* fop, JSObject* obj) {
     obj->as<ResolveResponseClosure>().compileArgs().Release();
   }
 
  public:
   static const unsigned RESERVED_SLOTS = 4;
   static const Class class_;
 
-  static ResolveResponseClosure* create(JSContext* cx, CompileArgs& args,
+  static ResolveResponseClosure* create(JSContext* cx, const CompileArgs& args,
                                         HandleObject promise, bool instantiate,
                                         HandleObject importObj) {
     MOZ_ASSERT_IF(importObj, instantiate);
 
     AutoSetNewObjectMetadata metadata(cx);
     auto* obj = NewObjectWithGivenProto<ResolveResponseClosure>(cx, nullptr);
     if (!obj) {
       return nullptr;
     }
 
     args.AddRef();
-    obj->setReservedSlot(COMPILE_ARGS_SLOT, PrivateValue(&args));
+    obj->setReservedSlot(COMPILE_ARGS_SLOT, PrivateValue((void*)&args));
     obj->setReservedSlot(PROMISE_OBJ_SLOT, ObjectValue(*promise));
     obj->setReservedSlot(INSTANTIATE_SLOT, BooleanValue(instantiate));
     obj->setReservedSlot(IMPORT_OBJ_SLOT, ObjectOrNullValue(importObj));
     return obj;
   }
 
   CompileArgs& compileArgs() const {
     return *(CompileArgs*)getReservedSlot(COMPILE_ARGS_SLOT).toPrivate();
@@ -3454,17 +3461,17 @@ static bool ResolveResponse(JSContext* c
                             Handle<PromiseObject*> promise,
                             bool instantiate = false,
                             HandleObject importObj = nullptr) {
   MOZ_ASSERT_IF(importObj, instantiate);
 
   const char* introducer = instantiate ? "WebAssembly.instantiateStreaming"
                                        : "WebAssembly.compileStreaming";
 
-  MutableCompileArgs compileArgs = InitCompileArgs(cx, introducer);
+  SharedCompileArgs compileArgs = InitCompileArgs(cx, introducer);
   if (!compileArgs) {
     return false;
   }
 
   RootedObject closure(
       cx, ResolveResponseClosure::create(cx, *compileArgs, promise, instantiate,
                                          importObj));
   if (!closure) {
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -35,16 +35,20 @@ class SharedArrayRawBuffer;
 namespace wasm {
 
 // Return whether WebAssembly can be compiled on this platform.
 // This must be checked and must be true to call any of the top-level wasm
 // eval/compile methods.
 
 bool HasCompilerSupport(JSContext* cx);
 
+// Return whether WebAssembly has support for an optimized compiler backend.
+
+bool HasOptimizedCompilerTier(JSContext* cx);
+
 // Return whether WebAssembly is supported on this platform. This determines
 // whether the WebAssembly object is exposed to JS and takes into account
 // configuration options that disable various modes.
 
 bool HasSupport(JSContext* cx);
 
 // Return whether WebAssembly streaming/caching is supported on this platform.
 // This takes into account prefs and necessary embedding callbacks.
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -127,22 +127,20 @@ bool Module::finishTier2(const LinkData&
       if (!stubs1->hasStub(fe.funcIndex())) {
         continue;
       }
       if (!funcExportIndices.emplaceBack(i)) {
         return false;
       }
     }
 
-    HasGcTypes gcTypesConfigured = code().metadata().temporaryGcTypesConfigured;
     const CodeTier& tier2 = code().codeTier(Tier::Optimized);
 
     Maybe<size_t> stub2Index;
-    if (!stubs2->createTier2(gcTypesConfigured, funcExportIndices, tier2,
-                             &stub2Index)) {
+    if (!stubs2->createTier2(funcExportIndices, tier2, &stub2Index)) {
       return false;
     }
 
     // Now that we can't fail or otherwise abort tier2, make it live.
 
     MOZ_ASSERT(!code().hasTier2());
     code().commitTier2();
 
--- a/js/src/wasm/WasmOpIter.h
+++ b/js/src/wasm/WasmOpIter.h
@@ -582,18 +582,17 @@ inline bool OpIter<Policy>::Join(StackTy
     return true;
   }
 
   if (two == StackType::TVar) {
     *result = one;
     return true;
   }
 
-  if (env_.gcTypesEnabled() == HasGcTypes::True && one.isReference() &&
-      two.isReference()) {
+  if (env_.gcTypesEnabled() && one.isReference() && two.isReference()) {
     if (env_.isRefSubtypeOf(NonAnyToValType(two), NonAnyToValType(one))) {
       *result = one;
       return true;
     }
 
     if (env_.isRefSubtypeOf(NonAnyToValType(one), NonAnyToValType(two))) {
       *result = two;
       return true;
@@ -704,18 +703,18 @@ inline bool OpIter<Policy>::popWithType(
     return fail("popping value from outside block");
   }
 
   TypeAndValue<Value> tv = valueStack_.popCopy();
 
   StackType observedType = tv.type();
   if (!(MOZ_LIKELY(observedType == expectedType) ||
         observedType == StackType::TVar || expectedType == StackType::TVar ||
-        (env_.gcTypesEnabled() == HasGcTypes::True &&
-         observedType.isReference() && expectedType.isReference() &&
+        (env_.gcTypesEnabled() && observedType.isReference() &&
+         expectedType.isReference() &&
          env_.isRefSubtypeOf(NonAnyToValType(observedType),
                              NonAnyToValType(expectedType))))) {
     return typeMismatch(observedType, expectedType);
   }
 
   *value = tv.value();
   return true;
 }
@@ -763,17 +762,17 @@ inline bool OpIter<Policy>::topWithType(
 
   TypeAndValue<Value>& tv = valueStack_.back();
 
   StackType observed = tv.type();
   StackType expected = StackType(expectedType);
 
   if (!MOZ_UNLIKELY(observed == expected)) {
     if (observed == StackType::TVar ||
-        (env_.gcTypesEnabled() == HasGcTypes::True && observed.isReference() &&
+        (env_.gcTypesEnabled() && observed.isReference() &&
          expected.isReference() &&
          env_.isRefSubtypeOf(NonAnyToValType(observed), expectedType))) {
       tv.typeRef() = expected;
     } else {
       return typeMismatch(observed, expected);
     }
   }
 
@@ -839,22 +838,21 @@ inline bool OpIter<Policy>::readBlockTyp
     case uint8_t(ExprType::Void):
     case uint8_t(ExprType::I32):
     case uint8_t(ExprType::I64):
     case uint8_t(ExprType::F32):
     case uint8_t(ExprType::F64):
       known = true;
       break;
     case uint8_t(ExprType::Ref):
-      known = env_.gcTypesEnabled() == HasGcTypes::True &&
-              uncheckedRefTypeIndex < MaxTypes &&
+      known = env_.gcTypesEnabled() && uncheckedRefTypeIndex < MaxTypes &&
               uncheckedRefTypeIndex < env_.types.length();
       break;
     case uint8_t(ExprType::AnyRef):
-      known = env_.gcTypesEnabled() == HasGcTypes::True;
+      known = env_.gcTypesEnabled();
       break;
     case uint8_t(ExprType::Limit):
       break;
   }
 
   if (!known) {
     return fail("invalid inline block type");
   }
@@ -2085,17 +2083,17 @@ inline bool OpIter<Policy>::readTableGet
   }
 
   if (*tableIndex >= env_.tables.length()) {
     return fail("table index out of range for table.get");
   }
   if (env_.tables[*tableIndex].kind != TableKind::AnyRef) {
     return fail("table.get only on tables of anyref");
   }
-  if (env_.gcTypesEnabled() == HasGcTypes::False) {
+  if (!env_.gcTypesEnabled()) {
     return fail("anyref support not enabled");
   }
 
   infalliblePush(ValType::AnyRef);
   return true;
 }
 
 template <typename Policy>
@@ -2127,17 +2125,17 @@ inline bool OpIter<Policy>::readTableGro
   }
 
   if (*tableIndex >= env_.tables.length()) {
     return fail("table index out of range for table.grow");
   }
   if (env_.tables[*tableIndex].kind != TableKind::AnyRef) {
     return fail("table.grow only on tables of anyref");
   }
-  if (env_.gcTypesEnabled() == HasGcTypes::False) {
+  if (!env_.gcTypesEnabled()) {
     return fail("anyref support not enabled");
   }
 
   infalliblePush(ValType::I32);
   return true;
 }
 
 template <typename Policy>
@@ -2169,17 +2167,17 @@ inline bool OpIter<Policy>::readTableSet
   }
 
   if (*tableIndex >= env_.tables.length()) {
     return fail("table index out of range for table.set");
   }
   if (env_.tables[*tableIndex].kind != TableKind::AnyRef) {
     return fail("table.set only on tables of anyref");
   }
-  if (env_.gcTypesEnabled() == HasGcTypes::False) {
+  if (!env_.gcTypesEnabled()) {
     return fail("anyref support not enabled");
   }
 
   return true;
 }
 
 template <typename Policy>
 inline bool OpIter<Policy>::readTableSize(uint32_t* tableIndex) {
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -282,17 +282,16 @@ static void CallFuncExport(MacroAssemble
 }
 
 // Generate a stub that enters wasm from a C++ caller via the native ABI. The
 // signature of the entry point is Module::ExportFuncPtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
 // must map from the ABI of ExportFuncPtr to the export's signature's ABI.
 static bool GenerateInterpEntry(MacroAssembler& masm, const FuncExport& fe,
                                 const Maybe<ImmPtr>& funcPtr,
-                                HasGcTypes gcTypesConfigured,
                                 Offsets* offsets) {
   AssertExpectedSP(masm);
   masm.haltingAlign(CodeAlignment);
 
   offsets->begin = masm.currentOffset();
 
   // Save the return address if it wasn't already saved by the call insn.
 #ifdef JS_USE_LINK_REGISTER
@@ -507,17 +506,17 @@ static void GenerateJitEntryThrow(MacroA
 // Generate a stub that enters wasm from a jit code caller via the jit ABI.
 //
 // ARM64 note: This does not save the PseudoStackPointer so we must be sure to
 // recompute it on every return path, be it normal return or exception return.
 // The JIT code we return to assumes it is correct.
 
 static bool GenerateJitEntry(MacroAssembler& masm, size_t funcExportIndex,
                              const FuncExport& fe, const Maybe<ImmPtr>& funcPtr,
-                             HasGcTypes gcTypesConfigured, Offsets* offsets) {
+                             Offsets* offsets) {
   AssertExpectedSP(masm);
 
   RegisterOrSP sp = masm.getStackPointer();
 
   GenerateJitEntryPrologue(masm, offsets);
 
   // The jit caller has set up the following stack layout (sp grows to the
   // left):
@@ -1888,36 +1887,34 @@ static bool GenerateDebugTrapStub(MacroA
 
   GenerateExitEpilogue(masm, 0, ExitReason::Fixed::DebugTrap, offsets);
 
   return FinishOffsets(masm, offsets);
 }
 
 bool wasm::GenerateEntryStubs(MacroAssembler& masm, size_t funcExportIndex,
                               const FuncExport& fe, const Maybe<ImmPtr>& callee,
-                              bool isAsmJS, HasGcTypes gcTypesConfigured,
-                              CodeRangeVector* codeRanges) {
+                              bool isAsmJS, CodeRangeVector* codeRanges) {
   MOZ_ASSERT(!callee == fe.hasEagerStubs());
   MOZ_ASSERT_IF(isAsmJS, fe.hasEagerStubs());
 
   Offsets offsets;
-  if (!GenerateInterpEntry(masm, fe, callee, gcTypesConfigured, &offsets)) {
+  if (!GenerateInterpEntry(masm, fe, callee, &offsets)) {
     return false;
   }
   if (!codeRanges->emplaceBack(CodeRange::InterpEntry, fe.funcIndex(),
                                offsets)) {
     return false;
   }
 
   if (isAsmJS || fe.funcType().temporarilyUnsupportedAnyRef()) {
     return true;
   }
 
-  if (!GenerateJitEntry(masm, funcExportIndex, fe, callee, gcTypesConfigured,
-                        &offsets)) {
+  if (!GenerateJitEntry(masm, funcExportIndex, fe, callee, &offsets)) {
     return false;
   }
   if (!codeRanges->emplaceBack(CodeRange::JitEntry, fe.funcIndex(), offsets)) {
     return false;
   }
 
   return true;
 }
@@ -1968,17 +1965,17 @@ bool wasm::GenerateStubs(const ModuleEnv
 
   Maybe<ImmPtr> noAbsolute;
   for (size_t i = 0; i < exports.length(); i++) {
     const FuncExport& fe = exports[i];
     if (!fe.hasEagerStubs()) {
       continue;
     }
     if (!GenerateEntryStubs(masm, i, fe, noAbsolute, env.isAsmJS(),
-                            env.gcTypesConfigured, &code->codeRanges)) {
+                            &code->codeRanges)) {
       return false;
     }
   }
 
   JitSpew(JitSpew_Codegen, "# Emitting wasm exit stubs");
 
   Offsets offsets;
 
--- a/js/src/wasm/WasmStubs.h
+++ b/js/src/wasm/WasmStubs.h
@@ -36,17 +36,16 @@ extern bool GenerateImportFunctions(cons
 extern bool GenerateStubs(const ModuleEnvironment& env,
                           const FuncImportVector& imports,
                           const FuncExportVector& exports, CompiledCode* code);
 
 extern bool GenerateEntryStubs(jit::MacroAssembler& masm,
                                size_t funcExportIndex,
                                const FuncExport& funcExport,
                                const Maybe<jit::ImmPtr>& callee, bool isAsmJS,
-                               HasGcTypes gcTypesConfigured,
                                CodeRangeVector* codeRanges);
 
 extern void GenerateTrapExitMachineState(jit::MachineState* machine,
                                          size_t* numWords);
 
 // A value that is written into the trap exit frame, which is useful for
 // cross-checking during garbage collection.
 static constexpr uintptr_t TrapExitDummyValue = 1337;
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -661,18 +661,16 @@ class Tiers {
 };
 
 // A Module can either be asm.js or wasm.
 
 enum ModuleKind { Wasm, AsmJS };
 
 enum class Shareable { False, True };
 
-enum class HasGcTypes { False, True };
-
 // The LitVal class represents a single WebAssembly value of a given value
 // type, mostly for the purpose of numeric literals and initializers. A LitVal
 // does not directly map to a JS value since there is not (currently) a precise
 // representation of i64 values. A LitVal may contain non-canonical NaNs since,
 // within WebAssembly, floats are not canonicalized. Canonicalization must
 // happen at the JS boundary.
 
 class LitVal {
--- a/js/src/wasm/WasmValidate.cpp
+++ b/js/src/wasm/WasmValidate.cpp
@@ -390,38 +390,38 @@ bool wasm::EncodeLocalEntries(Encoder& e
       return false;
     }
   }
 
   return true;
 }
 
 static bool DecodeValType(Decoder& d, ModuleKind kind, uint32_t numTypes,
-                          HasGcTypes gcTypesEnabled, ValType* type) {
+                          bool gcTypesEnabled, ValType* type) {
   uint8_t uncheckedCode;
   uint32_t uncheckedRefTypeIndex;
   if (!d.readValType(&uncheckedCode, &uncheckedRefTypeIndex)) {
     return false;
   }
 
   switch (uncheckedCode) {
     case uint8_t(ValType::I32):
     case uint8_t(ValType::F32):
     case uint8_t(ValType::F64):
     case uint8_t(ValType::I64):
       *type = ValType(ValType::Code(uncheckedCode));
       return true;
     case uint8_t(ValType::AnyRef):
-      if (gcTypesEnabled == HasGcTypes::False) {
+      if (!gcTypesEnabled) {
         return d.fail("reference types not enabled");
       }
       *type = ValType(ValType::Code(uncheckedCode));
       return true;
     case uint8_t(ValType::Ref): {
-      if (gcTypesEnabled == HasGcTypes::False) {
+      if (!gcTypesEnabled) {
         return d.fail("reference types not enabled");
       }
       if (uncheckedRefTypeIndex >= numTypes) {
         return d.fail("ref index out of range");
       }
       // We further validate ref types in the caller.
       *type = ValType(ValType::Code(uncheckedCode), uncheckedRefTypeIndex);
       return true;
@@ -436,18 +436,17 @@ static bool ValidateRefType(Decoder& d, 
                             ValType type) {
   if (type.isRef() && !types[type.refTypeIndex()].isStructType()) {
     return d.fail("ref does not reference a struct type");
   }
   return true;
 }
 
 bool wasm::DecodeLocalEntries(Decoder& d, ModuleKind kind,
-                              const TypeDefVector& types,
-                              HasGcTypes gcTypesEnabled,
+                              const TypeDefVector& types, bool gcTypesEnabled,
                               ValTypeVector* locals) {
   uint32_t numLocalEntries;
   if (!d.readVarU32(&numLocalEntries)) {
     return d.fail("failed to read number of local entries");
   }
 
   for (uint32_t i = 0; i < numLocalEntries; i++) {
     uint32_t count;
@@ -933,70 +932,70 @@ static bool DecodeFunctionBodyExprs(cons
           }
           case uint16_t(MiscOp::TableSize): {
             uint32_t unusedTableIndex;
             CHECK(iter.readTableSize(&unusedTableIndex));
           }
 #endif
 #ifdef ENABLE_WASM_GC
           case uint16_t(MiscOp::StructNew): {
-            if (env.gcTypesEnabled() == HasGcTypes::False) {
+            if (!env.gcTypesEnabled()) {
               return iter.unrecognizedOpcode(&op);
             }
             uint32_t unusedUint;
             ValidatingOpIter::ValueVector unusedArgs;
             CHECK(iter.readStructNew(&unusedUint, &unusedArgs));
           }
           case uint16_t(MiscOp::StructGet): {
-            if (env.gcTypesEnabled() == HasGcTypes::False) {
+            if (!env.gcTypesEnabled()) {
               return iter.unrecognizedOpcode(&op);
             }
             uint32_t unusedUint1, unusedUint2;
             CHECK(iter.readStructGet(&unusedUint1, &unusedUint2, &nothing));
           }
           case uint16_t(MiscOp::StructSet): {
-            if (env.gcTypesEnabled() == HasGcTypes::False) {
+            if (!env.gcTypesEnabled()) {
               return iter.unrecognizedOpcode(&op);
             }
             uint32_t unusedUint1, unusedUint2;
             CHECK(iter.readStructSet(&unusedUint1, &unusedUint2, &nothing,
                                      &nothing));
           }
           case uint16_t(MiscOp::StructNarrow): {
-            if (env.gcTypesEnabled() == HasGcTypes::False) {
+            if (!env.gcTypesEnabled()) {
               return iter.unrecognizedOpcode(&op);
             }
             ValType unusedTy, unusedTy2;
             CHECK(iter.readStructNarrow(&unusedTy, &unusedTy2, &nothing));
           }
 #endif
           default:
             return iter.unrecognizedOpcode(&op);
         }
         break;
       }
 #ifdef ENABLE_WASM_GC
       case uint16_t(Op::RefEq): {
-        if (env.gcTypesEnabled() == HasGcTypes::False) {
+        if (!env.gcTypesEnabled()) {
           return iter.unrecognizedOpcode(&op);
         }
         CHECK(iter.readComparison(ValType::AnyRef, &nothing, &nothing));
         break;
       }
 #endif
 #ifdef ENABLE_WASM_REFTYPES
       case uint16_t(Op::RefNull): {
-        if (env.gcTypesEnabled() == HasGcTypes::False) {
+        if (!env.gcTypesEnabled()) {
           return iter.unrecognizedOpcode(&op);
         }
         CHECK(iter.readRefNull());
         break;
       }
       case uint16_t(Op::RefIsNull): {
-        if (env.gcTypesEnabled() == HasGcTypes::False) {
+        if (!env.gcTypesEnabled()) {
           return iter.unrecognizedOpcode(&op);
         }
         CHECK(iter.readConversion(ValType::AnyRef, ValType::I32, &nothing));
         break;
       }
 #endif
       case uint16_t(Op::ThreadPrefix): {
         switch (op.b1) {
@@ -1320,17 +1319,17 @@ static bool DecodeFuncType(Decoder& d, M
   env->types[typeIndex] = TypeDef(FuncType(std::move(args), result));
   (*typeState)[typeIndex] = TypeState::Func;
 
   return true;
 }
 
 static bool DecodeStructType(Decoder& d, ModuleEnvironment* env,
                              TypeStateVector* typeState, uint32_t typeIndex) {
-  if (env->gcTypesEnabled() == HasGcTypes::False) {
+  if (!env->gcTypesEnabled()) {
     return d.fail("Structure types not enabled");
   }
 
   uint32_t numFields;
   if (!d.readVarU32(&numFields)) {
     return d.fail("Bad number of fields");
   }
 
@@ -1446,17 +1445,17 @@ static bool DecodeGCFeatureOptInSection(
     case 2:
       break;
     default:
       return d.fail(
           "The specified Wasm GC feature version is unknown.\n"
           "The current version is 2.");
   }
 
-  env->gcFeatureOptIn = HasGcTypes::True;
+  env->gcFeatureOptIn = true;
   return d.finishSection(*range, "gcfeatureoptin");
 }
 #endif
 
 static bool DecodeTypeSection(Decoder& d, ModuleEnvironment* env) {
   MaybeSectionRange range;
   if (!d.startSection(SectionId::Type, env, &range, "type")) {
     return false;
@@ -1602,29 +1601,29 @@ static bool DecodeLimits(Decoder& d, Lim
     limits->shared = (flags & uint8_t(MemoryTableFlags::IsShared))
                          ? Shareable::True
                          : Shareable::False;
   }
 
   return true;
 }
 
-static bool DecodeTableTypeAndLimits(Decoder& d, HasGcTypes gcTypesEnabled,
+static bool DecodeTableTypeAndLimits(Decoder& d, bool gcTypesEnabled,
                                      TableDescVector* tables) {
   uint8_t elementType;
   if (!d.readFixedU8(&elementType)) {
     return d.fail("expected table element type");
   }
 
   TableKind tableKind;
   if (elementType == uint8_t(TypeCode::AnyFunc)) {
     tableKind = TableKind::AnyFunction;
 #ifdef ENABLE_WASM_GENERALIZED_TABLES
   } else if (elementType == uint8_t(TypeCode::AnyRef)) {
-    if (gcTypesEnabled == HasGcTypes::False) {
+    if (!gcTypesEnabled) {
       return d.fail("reference types not enabled");
     }
     tableKind = TableKind::AnyRef;
 #endif
   } else {
 #ifdef ENABLE_WASM_GENERALIZED_TABLES
     return d.fail("expected 'anyfunc' or 'anyref' element type");
 #else
@@ -1667,17 +1666,17 @@ static bool GlobalIsJSCompatible(Decoder
     default:
       return d.fail("unexpected variable type in global import/export");
   }
 
   return true;
 }
 
 static bool DecodeGlobalType(Decoder& d, const TypeDefVector& types,
-                             HasGcTypes gcTypesEnabled, ValType* type,
+                             bool gcTypesEnabled, ValType* type,
                              bool* isMutable) {
   if (!DecodeValType(d, ModuleKind::Wasm, types.length(), gcTypesEnabled,
                      type)) {
     return false;
   }
   if (!ValidateRefType(d, types, *type)) {
     return false;
   }
@@ -1988,17 +1987,17 @@ static bool DecodeInitializerExpression(
       double f64;
       if (!d.readFixedF64(&f64)) {
         return d.fail("failed to read initializer f64 expression");
       }
       *init = InitExpr(LitVal(f64));
       break;
     }
     case uint16_t(Op::RefNull): {
-      if (env->gcTypesEnabled() == HasGcTypes::False) {
+      if (!env->gcTypesEnabled()) {
         return d.fail("unexpected initializer expression");
       }
       if (!expected.isReference()) {
         return d.fail(
             "type mismatch: initializer type and expected type don't match");
       }
       if (expected == ValType::AnyRef) {
         *init = InitExpr(LitVal(AnyRef::null()));
@@ -2017,18 +2016,17 @@ static bool DecodeInitializerExpression(
       if (i >= globals.length()) {
         return d.fail("global index out of range in initializer expression");
       }
       if (!globals[i].isImport() || globals[i].isMutable()) {
         return d.fail(
             "initializer expression must reference a global immutable import");
       }
       if (expected.isReference()) {
-        if (!(env->gcTypesEnabled() == HasGcTypes::True &&
-              globals[i].type().isReference() &&
+        if (!(env->gcTypesEnabled() && globals[i].type().isReference() &&
               env->isRefSubtypeOf(globals[i].type(), expected))) {
           return d.fail(
               "type mismatch: initializer type and expected type don't match");
         }
         *init = InitExpr(i, expected);
       } else {
         *init = InitExpr(i, globals[i].type());
       }
@@ -2417,19 +2415,19 @@ bool wasm::DecodeModuleEnvironment(Decod
   if (!DecodePreamble(d)) {
     return false;
   }
 
 #ifdef ENABLE_WASM_REFTYPES
   if (!DecodeGCFeatureOptInSection(d, env)) {
     return false;
   }
-  HasGcTypes gcFeatureOptIn = env->gcFeatureOptIn;
+  bool gcFeatureOptIn = env->gcFeatureOptIn;
 #else
-  HasGcTypes gcFeatureOptIn = HasGcTypes::False;
+  bool gcFeatureOptIn = false;
 #endif
 
   env->compilerEnv->computeParameters(d, gcFeatureOptIn);
 
   if (!DecodeTypeSection(d, env)) {
     return false;
   }
 
@@ -2788,20 +2786,19 @@ bool wasm::DecodeModuleTail(Decoder& d, 
 
 // Validate algorithm.
 
 bool wasm::Validate(JSContext* cx, const ShareableBytes& bytecode,
                     UniqueChars* error) {
   Decoder d(bytecode.bytes, 0, error);
 
 #ifdef ENABLE_WASM_REFTYPES
-  HasGcTypes gcTypesConfigured =
-      HasReftypesSupport(cx) ? HasGcTypes::True : HasGcTypes::False;
+  bool gcTypesConfigured = HasReftypesSupport(cx);
 #else
-  HasGcTypes gcTypesConfigured = HasGcTypes::False;
+  bool gcTypesConfigured = false;
 #endif
 
   CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
                                   OptimizedBackend::Ion, DebugEnabled::False,
                                   gcTypesConfigured);
   ModuleEnvironment env(
       gcTypesConfigured, &compilerEnv,
       cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()
--- a/js/src/wasm/WasmValidate.h
+++ b/js/src/wasm/WasmValidate.h
@@ -63,39 +63,39 @@ struct CompilerEnvironment {
     const CompileArgs* args_;
 
     // Value in the other two states.
     struct {
       CompileMode mode_;
       Tier tier_;
       OptimizedBackend optimizedBackend_;
       DebugEnabled debug_;
-      HasGcTypes gcTypes_;
+      bool gcTypes_;
     };
   };
 
  public:
   // Retain a reference to the CompileArgs.  A subsequent computeParameters()
   // will compute all parameters from the CompileArgs and additional values.
   explicit CompilerEnvironment(const CompileArgs& args);
 
   // Save the provided values for mode, tier, and debug, and the initial value
   // for gcTypes.  A subsequent computeParameters() will compute the final
   // value of gcTypes.
   CompilerEnvironment(CompileMode mode, Tier tier,
                       OptimizedBackend optimizedBackend,
-                      DebugEnabled debugEnabled, HasGcTypes gcTypesConfigured);
+                      DebugEnabled debugEnabled, bool gcTypesConfigured);
 
   // Compute any remaining compilation parameters.
-  void computeParameters(Decoder& d, HasGcTypes gcFeatureOptIn);
+  void computeParameters(Decoder& d, bool gcFeatureOptIn);
 
   // Compute any remaining compilation parameters.  Only use this method if
   // the CompilerEnvironment was created with values for mode, tier, and
   // debug.
-  void computeParameters(HasGcTypes gcFeatureOptIn);
+  void computeParameters(bool gcFeatureOptIn);
 
   bool isComputed() const { return state_ == Computed; }
   CompileMode mode() const {
     MOZ_ASSERT(isComputed());
     return mode_;
   }
   Tier tier() const {
     MOZ_ASSERT(isComputed());
@@ -104,17 +104,17 @@ struct CompilerEnvironment {
   OptimizedBackend optimizedBackend() const {
     MOZ_ASSERT(isComputed());
     return optimizedBackend_;
   }
   DebugEnabled debug() const {
     MOZ_ASSERT(isComputed());
     return debug_;
   }
-  HasGcTypes gcTypes() const {
+  bool gcTypes() const {
     MOZ_ASSERT(isComputed());
     return gcTypes_;
   }
 };
 
 // ModuleEnvironment contains all the state necessary to process or render
 // functions, and all of the state necessary to validate aspects of the
 // functions that do not require looking forwards in the bytecode stream.
@@ -127,37 +127,29 @@ struct CompilerEnvironment {
 // ModuleGenerator thread and background compile threads. All the threads
 // are given a read-only view of the ModuleEnvironment, thus preventing race
 // conditions.
 
 struct ModuleEnvironment {
   // Constant parameters for the entire compilation:
   const ModuleKind kind;
   const Shareable sharedMemoryEnabled;
-  // `gcTypesConfigured` reflects the value of the flags --wasm-gc and
-  // javascript.options.wasm_gc.  These flags will disappear eventually, thus
-  // allowing the removal of this variable and its replacement everywhere by
-  // the value HasGcTypes::True.
-  //
-  // For now, the value is used to control whether we emit code to suppress GC
-  // while wasm activations are on the stack.
-  const HasGcTypes gcTypesConfigured;
   CompilerEnvironment* const compilerEnv;
 
   // Module fields decoded from the module environment (or initialized while
   // validating an asm.js module) and immutable during compilation:
 #ifdef ENABLE_WASM_REFTYPES
   // `gcFeatureOptIn` reflects the presence in a module of a GcFeatureOptIn
   // section.  This variable will be removed eventually, allowing it to be
-  // replaced everywhere by the value HasGcTypes::True.
+  // replaced everywhere by the value true.
   //
   // The flag is used in the value of gcTypesEnabled(), which controls whether
   // ref types and struct types and associated instructions are accepted
   // during validation.
-  HasGcTypes gcFeatureOptIn;
+  bool gcFeatureOptIn;
 #endif
   MemoryUsage memoryUsage;
   uint32_t minMemoryLength;
   Maybe<uint32_t> maxMemoryLength;
   uint32_t numStructTypes;
   TypeDefVector types;
   FuncTypeWithIdPtrVector funcTypes;
   Uint32Vector funcImportGlobalDataOffsets;
@@ -172,26 +164,25 @@ struct ModuleEnvironment {
 
   // Fields decoded as part of the wasm module tail:
   DataSegmentEnvVector dataSegments;
   CustomSectionEnvVector customSections;
   Maybe<uint32_t> nameCustomSectionIndex;
   Maybe<Name> moduleName;
   NameVector funcNames;
 
-  explicit ModuleEnvironment(HasGcTypes gcTypesConfigured,
+  explicit ModuleEnvironment(bool gcTypesConfigured,
                              CompilerEnvironment* compilerEnv,
                              Shareable sharedMemoryEnabled,
                              ModuleKind kind = ModuleKind::Wasm)
       : kind(kind),
         sharedMemoryEnabled(sharedMemoryEnabled),
-        gcTypesConfigured(gcTypesConfigured),
         compilerEnv(compilerEnv),
 #ifdef ENABLE_WASM_REFTYPES
-        gcFeatureOptIn(HasGcTypes::False),
+        gcFeatureOptIn(false),
 #endif
         memoryUsage(MemoryUsage::None),
         minMemoryLength(0),
         numStructTypes(0) {
   }
 
   Tier tier() const { return compilerEnv->tier(); }
   OptimizedBackend optimizedBackend() const {
@@ -201,30 +192,30 @@ struct ModuleEnvironment {
   DebugEnabled debug() const { return compilerEnv->debug(); }
   size_t numTables() const { return tables.length(); }
   size_t numTypes() const { return types.length(); }
   size_t numFuncs() const { return funcTypes.length(); }
   size_t numFuncImports() const { return funcImportGlobalDataOffsets.length(); }
   size_t numFuncDefs() const {
     return funcTypes.length() - funcImportGlobalDataOffsets.length();
   }
-  HasGcTypes gcTypesEnabled() const { return compilerEnv->gcTypes(); }
+  bool gcTypesEnabled() const { return compilerEnv->gcTypes(); }
   bool usesMemory() const { return memoryUsage != MemoryUsage::None; }
   bool usesSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
   bool isAsmJS() const { return kind == ModuleKind::AsmJS; }
   bool debugEnabled() const {
     return compilerEnv->debug() == DebugEnabled::True;
   }
   bool funcIsImport(uint32_t funcIndex) const {
     return funcIndex < funcImportGlobalDataOffsets.length();
   }
   bool isRefSubtypeOf(ValType one, ValType two) const {
     MOZ_ASSERT(one.isReference());
     MOZ_ASSERT(two.isReference());
-    MOZ_ASSERT(gcTypesEnabled() == HasGcTypes::True);
+    MOZ_ASSERT(gcTypesEnabled());
     return one == two || two == ValType::AnyRef || one == ValType::NullRef ||
            (one.isRef() && two.isRef() && isStructPrefixOf(two, one));
   }
 
  private:
   bool isStructPrefixOf(ValType a, ValType b) const {
     const StructType& other = types[a.refTypeIndex()].structType();
     return types[b.refTypeIndex()].structType().hasPrefix(other);
@@ -787,17 +778,17 @@ MOZ_MUST_USE bool EncodeLocalEntries(Enc
 
 MOZ_MUST_USE bool DecodeValidatedLocalEntries(Decoder& d,
                                               ValTypeVector* locals);
 
 // This validates the entries.
 
 MOZ_MUST_USE bool DecodeLocalEntries(Decoder& d, ModuleKind kind,
                                      const TypeDefVector& types,
-                                     HasGcTypes gcTypesEnabled,
+                                     bool gcTypesEnabled,
                                      ValTypeVector* locals);
 
 // Returns whether the given [begin, end) prefix of a module's bytecode starts a
 // code section and, if so, returns the SectionRange of that code section.
 // Note that, even if this function returns 'false', [begin, end) may actually
 // be a valid module in the special case when there are no function defs and the
 // code section is not present. Such modules can be valid so the caller must
 // handle this special case.
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -860,17 +860,17 @@ static void ReloadPrefsCallback(const ch
   JS::ContextOptionsRef(cx)
       .setBaseline(useBaseline)
       .setIon(useIon)
       .setAsmJS(useAsmJS)
       .setWasm(useWasm)
       .setWasmIon(useWasmIon)
       .setWasmBaseline(useWasmBaseline)
 #ifdef ENABLE_WASM_CRANELIFT
-      .setWasmForceCranelift(useWasmCranelift)
+      .setWasmCranelift(useWasmCranelift)
 #endif
 #ifdef ENABLE_WASM_REFTYPES
       .setWasmGc(useWasmGc)
 #endif
       .setWasmVerbose(useWasmVerbose)
       .setThrowOnAsmJSValidationFailure(throwOnAsmJSValidationFailure)
       .setNativeRegExp(useNativeRegExp)
       .setAsyncStack(useAsyncStack)
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -451,16 +451,29 @@ VARCACHE_PREF(
 // Enable content type normalization of XHR uploads via MIME Sniffing standard
 // Disabled for now in bz1499136
 VARCACHE_PREF(
   "dom.xhr.standard_content_type_normalization",
    dom_xhr_standard_content_type_normalization,
   RelaxedAtomicBool, false
 )
 
+// Block multiple external protocol URLs in iframes per single event.
+#ifdef NIGHTLY_BUILD
+#define PREF_VALUE true
+#else
+#define PREF_VALUE false
+#endif
+VARCACHE_PREF(
+  "dom.block_external_protocol_in_iframes",
+   dom_block_external_protocol_in_iframes,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
 // Block multiple window.open() per single event.
 VARCACHE_PREF(
   "dom.block_multiple_popups",
    dom_block_multiple_popups,
   bool, true
 )
 
 // For area and anchor elements with target=_blank and no rel set to
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1026,20 +1026,16 @@ void HttpChannelChild::MaybeDivertOnStop
 
 void HttpChannelChild::OnStopRequest(
     const nsresult& channelStatus, const ResourceTimingStruct& timing,
     const nsHttpHeaderArray& aResponseTrailers) {
   LOG(("HttpChannelChild::OnStopRequest [this=%p status=%" PRIx32 "]\n", this,
        static_cast<uint32_t>(channelStatus)));
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (mOnStopRequestCalled && !mIPCOpen) {
-    return;
-  }
-
   if (mDivertingToParent) {
     MOZ_RELEASE_ASSERT(
         !mFlushedForDiversion,
         "Should not be processing any more callbacks from parent!");
 
     SendDivertOnStopRequest(channelStatus);
     return;
   }
@@ -1186,42 +1182,39 @@ void HttpChannelChild::CollectOMTTelemet
 void HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest,
                                        nsresult aChannelStatus,
                                        nsISupports* aContext) {
   AUTO_PROFILER_LABEL("HttpChannelChild::DoOnStopRequest", NETWORK);
   LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mIsPending);
 
-  auto checkForBlockedContent = [&]() {
-    // NB: We use aChannelStatus here instead of mStatus because if there was an
-    // nsCORSListenerProxy on this request, it will override the tracking
-    // protection's return value.
-    if (aChannelStatus == NS_ERROR_TRACKING_URI ||
-        aChannelStatus == NS_ERROR_MALWARE_URI ||
-        aChannelStatus == NS_ERROR_UNWANTED_URI ||
-        aChannelStatus == NS_ERROR_BLOCKED_URI ||
-        aChannelStatus == NS_ERROR_HARMFUL_URI ||
-        aChannelStatus == NS_ERROR_PHISHING_URI) {
-      nsCString list, provider, fullhash;
-
-      nsresult rv = GetMatchedList(list);
-      NS_ENSURE_SUCCESS_VOID(rv);
-
-      rv = GetMatchedProvider(provider);
-      NS_ENSURE_SUCCESS_VOID(rv);
-
-      rv = GetMatchedFullHash(fullhash);
-      NS_ENSURE_SUCCESS_VOID(rv);
-
-      UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list,
-                                             provider, fullhash);
-    }
-  };
-  checkForBlockedContent();
+  // NB: We use aChannelStatus here instead of mStatus because if there was an
+  // nsCORSListenerProxy on this request, it will override the tracking
+  // protection's return value.
+  if (aChannelStatus == NS_ERROR_TRACKING_URI ||
+      aChannelStatus == NS_ERROR_MALWARE_URI ||
+      aChannelStatus == NS_ERROR_UNWANTED_URI ||
+      aChannelStatus == NS_ERROR_BLOCKED_URI ||
+      aChannelStatus == NS_ERROR_HARMFUL_URI ||
+      aChannelStatus == NS_ERROR_PHISHING_URI) {
+    nsCString list, provider, fullhash;
+
+    nsresult rv = GetMatchedList(list);
+    NS_ENSURE_SUCCESS_VOID(rv);
+
+    rv = GetMatchedProvider(provider);
+    NS_ENSURE_SUCCESS_VOID(rv);
+
+    rv = GetMatchedFullHash(fullhash);
+    NS_ENSURE_SUCCESS_VOID(rv);
+
+    UrlClassifierCommon::SetBlockedContent(this, aChannelStatus, list, provider,
+                                           fullhash);
+  }
 
   MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
 
   // In theory mListener should not be null, but in practice sometimes it is.
   MOZ_ASSERT(mListener);
   if (mListener) {
     mListener->OnStopRequest(aRequest, aContext, mStatus);
   }
@@ -3789,25 +3782,16 @@ mozilla::ipc::IPCResult HttpChannelChild
 
 void HttpChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
   MOZ_ASSERT(NS_IsMainThread());
 
   // OnStartRequest might be dropped if IPDL is destroyed abnormally
   // and BackgroundChild might have pending IPC messages.
   // Clean up BackgroundChild at this time to prevent memleak.
   if (aWhy != Deletion) {
-    // The actor tree might get destroyed before we get the OnStopRequest.
-    // So if we didn't get it, we send it here in order to prevent any leaks.
-    // Ocasionally we will get the OnStopRequest message after this, in which
-    // case we just ignore it as we've already cleared the listener.
-    if (!mOnStopRequestCalled && mListener) {
-      DoPreOnStopRequest(NS_ERROR_ABORT);
-      DoOnStopRequest(this, NS_ERROR_ABORT, mListenerContext);
-    }
-
     CleanupBackgroundChannel();
   }
 }
 
 mozilla::ipc::IPCResult HttpChannelChild::RecvLogBlockedCORSRequest(
     const nsString& aMessage, const nsCString& aCategory) {
   Unused << LogBlockedCORSRequest(aMessage, aCategory);
   return IPC_OK();
--- a/testing/web-platform/meta/css/css-fonts/font-display/__dir__.ini
+++ b/testing/web-platform/meta/css/css-fonts/font-display/__dir__.ini
@@ -1,1 +1,1 @@
-leak-threshold: [tab:409600]
+leak-threshold: [tab:358400]
--- a/testing/web-platform/meta/encrypted-media/__dir__.ini
+++ b/testing/web-platform/meta/encrypted-media/__dir__.ini
@@ -1,3 +1,5 @@
+disabled:
+  if asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
 prefs: [dom.security.featurePolicy.enabled:true, dom.security.featurePolicy.header.enabled:true, dom.security.featurePolicy.webidl.enabled:true]
 lsan-allowed: [Alloc, CreateCDMProxy, MakeUnique, Malloc, NewPage, Realloc, mozilla::EMEDecryptor::EMEDecryptor, mozilla::SchedulerGroup::CreateEventTargetFor, mozilla::dom::MediaKeys::CreateCDMProxy, mozilla::dom::nsIContentChild::GetConstructedEventTarget]
 leak-threshold: [default:51200]
--- a/testing/web-platform/meta/encrypted-media/clearkey-events.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-events.https.html.ini
@@ -1,5 +1,3 @@
 [clearkey-events.https.html]
   expected:
     if os == "android": TIMEOUT
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
--- a/testing/web-platform/meta/encrypted-media/clearkey-generate-request-disallowed-input.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-generate-request-disallowed-input.https.html.ini
@@ -1,6 +1,3 @@
 [clearkey-generate-request-disallowed-input.https.html]
   expected:
     if os == "android": TIMEOUT
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
-    
--- a/testing/web-platform/meta/encrypted-media/clearkey-invalid-license.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-invalid-license.https.html.ini
@@ -1,7 +1,5 @@
 [clearkey-invalid-license.https.html]
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
   [Update with invalid Clear Key license]
     expected:
       if os == "android": FAIL
 
--- a/testing/web-platform/meta/encrypted-media/clearkey-keystatuses-multiple-sessions.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-keystatuses-multiple-sessions.https.html.ini
@@ -1,6 +1,3 @@
 [clearkey-keystatuses-multiple-sessions.https.html]
   expected:
     if os == "android": TIMEOUT
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
-
--- a/testing/web-platform/meta/encrypted-media/clearkey-mp4-playback-temporary-clear-encrypted-segmented.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-mp4-playback-temporary-clear-encrypted-segmented.https.html.ini
@@ -1,7 +1,5 @@
 [clearkey-mp4-playback-temporary-clear-encrypted-segmented.https.html]
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
   [org.w3.clearkey, temporary, mp4, playback, encrypted and clear sources in separate segments]
     expected:
       if os == "android": FAIL
 
--- a/testing/web-platform/meta/encrypted-media/clearkey-mp4-playback-temporary-multikey-sequential-readyState.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-mp4-playback-temporary-multikey-sequential-readyState.https.html.ini
@@ -1,7 +1,5 @@
 [clearkey-mp4-playback-temporary-multikey-sequential-readyState.https.html]
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
   [org.w3.clearkey, successful playback, temporary, mp4, multiple keys, sequential, readyState]
     expected:
       if os == "android": FAIL
 
--- a/testing/web-platform/meta/encrypted-media/clearkey-mp4-setmediakeys-again-after-resetting-src.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-mp4-setmediakeys-again-after-resetting-src.https.html.ini
@@ -1,7 +1,5 @@
 [clearkey-mp4-setmediakeys-again-after-resetting-src.https.html]
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
   [org.w3.clearkey, setmediakeys again after resetting src]
     expected:
       if os == "android": FAIL
 
--- a/testing/web-platform/meta/encrypted-media/clearkey-mp4-setmediakeys-at-same-time.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-mp4-setmediakeys-at-same-time.https.html.ini
@@ -1,6 +1,3 @@
 [clearkey-mp4-setmediakeys-at-same-time.https.html]
   expected:
     if os == "android": TIMEOUT
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
-
--- a/testing/web-platform/meta/encrypted-media/clearkey-mp4-setmediakeys-multiple-times-with-different-mediakeys.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-mp4-setmediakeys-multiple-times-with-different-mediakeys.https.html.ini
@@ -1,7 +1,5 @@
 [clearkey-mp4-setmediakeys-multiple-times-with-different-mediakeys.https.html]
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
   [org.w3.clearkey, setmediakeys multiple times with different mediakeys]
     expected:
       if os == "android": FAIL
 
--- a/testing/web-platform/meta/encrypted-media/clearkey-mp4-setmediakeys-multiple-times-with-the-same-mediakeys.https.html.ini
+++ b/testing/web-platform/meta/encrypted-media/clearkey-mp4-setmediakeys-multiple-times-with-the-same-mediakeys.https.html.ini
@@ -1,7 +1,5 @@
 [clearkey-mp4-setmediakeys-multiple-times-with-the-same-mediakeys.https.html]
-  disabled:
-    if os == "linux" and asan: https://bugzilla.mozilla.org/show_bug.cgi?id=1522213
   [org.w3.clearkey, setmediakeys multiple times with the same mediakeys]
     expected:
       if os == "android": FAIL