Merge autoland to mozilla-central. a=merge
authorCsoregi Natalia <ncsoregi@mozilla.com>
Tue, 24 May 2022 00:32:06 +0300
changeset 618586 db8bb8422d1fdfb2cd32d6ec3a60ec0743f93aed
parent 618585 db09323f385c5264723b6dd3b529b9588b2f6b1b (current diff)
parent 618569 c9594b2132f6714b9f5a84017821375def2b6ca6 (diff)
child 618587 847e1c9dccd2bf2bf73ae2081bb4ab41bc175da5
push id163352
push userncsoregi@mozilla.com
push dateMon, 23 May 2022 21:41:58 +0000
treeherderautoland@847e1c9dccd2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone102.0a1
first release with
nightly linux32
db8bb8422d1f / 102.0a1 / 20220523213527 / files
nightly linux64
db8bb8422d1f / 102.0a1 / 20220523213527 / files
nightly mac
db8bb8422d1f / 102.0a1 / 20220523213527 / files
nightly win32
db8bb8422d1f / 102.0a1 / 20220523213527 / files
nightly win64
db8bb8422d1f / 102.0a1 / 20220523213527 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
taskcluster/scripts/misc/verify-updatebot.py
--- a/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
@@ -295,26 +295,18 @@ add_task(async function test_csscode_cle
 
     info("Close tab and wait for content script port to be disconnected");
     BrowserTestUtils.removeTab(tab);
     await extension.awaitMessage("port-disconnected");
   });
 
   // Look for nsIDOMWindowUtils.removeSheet and
   // nsIDOMWindowUtils.removeSheetUsingURIString errors.
-  messages = messages.filter(
-    m =>
-      m.errorMessage &&
-      m.errorMessage.includes(
-        "(NS_ERROR_FAILURE) [nsIDOMWindowUtils.removeSheet"
-      )
-  );
-
   AddonTestUtils.checkMessages(
     messages,
     {
-      forbidden: [/nsIDOMWindowUtils.removeSheet/],
+      forbidden: [{ errorMessage: /nsIDOMWindowUtils.removeSheet/ }],
     },
     "Expect no remoteSheet errors"
   );
 
   await extension.unload();
 });
--- a/browser/components/extensions/test/xpcshell/test_ext_chrome_settings_overrides_update.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_chrome_settings_overrides_update.js
@@ -17,19 +17,16 @@ AddonTestUtils.init(this);
 AddonTestUtils.overrideCertDB();
 
 AddonTestUtils.createAppInfo(
   "xpcshell@tests.mozilla.org",
   "XPCShell",
   "1",
   "42"
 );
-// Override ExtensionXPCShellUtils.jsm's overriding of the pref as the
-// search service needs it.
-Services.prefs.clearUserPref("services.settings.default_bucket");
 
 // Similar to TestUtils.topicObserved, but returns a deferred promise that
 // can be resolved
 function topicObservable(topic, checkFn) {
   let deferred = PromiseUtils.defer();
   function observer(subject, topic, data) {
     try {
       if (checkFn && !checkFn(subject, data)) {
--- a/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search.js
@@ -19,19 +19,16 @@ const URLTYPE_SUGGEST_JSON = "applicatio
 
 AddonTestUtils.init(this);
 AddonTestUtils.createAppInfo(
   "xpcshell@tests.mozilla.org",
   "XPCShell",
   "42",
   "42"
 );
-// Override ExtensionXPCShellUtils.jsm's overriding of the pref as the
-// search service needs it.
-Services.prefs.clearUserPref("services.settings.default_bucket");
 
 add_task(async function setup() {
   await AddonTestUtils.promiseStartupManager();
   await Services.search.init();
 });
 
 add_task(async function test_extension_adding_engine() {
   let ext1 = ExtensionTestUtils.loadExtension({
--- a/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search_mozParam.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_search_mozParam.js
@@ -17,19 +17,16 @@ const { sinon } = ChromeUtils.import("re
 AddonTestUtils.init(this);
 AddonTestUtils.overrideCertDB();
 AddonTestUtils.createAppInfo(
   "xpcshell@tests.mozilla.org",
   "XPCShell",
   "42",
   "42"
 );
-// Override ExtensionXPCShellUtils.jsm's overriding of the pref as the
-// search service needs it.
-Services.prefs.clearUserPref("services.settings.default_bucket");
 
 let { promiseShutdownManager, promiseStartupManager } = AddonTestUtils;
 
 // Note: these lists should be kept in sync with the lists in
 // browser/components/extensions/test/xpcshell/data/test/manifest.json
 // These params are conditional based on how search is initiated.
 const mozParams = [
   {
--- a/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_shutdown.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_settings_overrides_shutdown.js
@@ -17,19 +17,16 @@ ChromeUtils.defineModuleGetter(
 AddonTestUtils.init(this);
 AddonTestUtils.overrideCertDB();
 AddonTestUtils.createAppInfo(
   "xpcshell@tests.mozilla.org",
   "XPCShell",
   "42",
   "42"
 );
-// Override ExtensionXPCShellUtils.jsm's overriding of the pref as the
-// search service needs it.
-Services.prefs.clearUserPref("services.settings.default_bucket");
 
 add_task(async function shutdown_during_search_provider_startup() {
   await AddonTestUtils.promiseStartupManager();
 
   let extension = ExtensionTestUtils.loadExtension({
     useAddonManager: "permanent",
     manifest: {
       chrome_settings_overrides: {
--- a/browser/components/extensions/test/xpcshell/test_ext_urlbar.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_urlbar.js
@@ -20,20 +20,16 @@ AddonTestUtils.createAppInfo(
   "xpcshell@tests.mozilla.org",
   "XPCShell",
   "1",
   "42"
 );
 SearchTestUtils.init(this);
 SearchTestUtils.initXPCShellAddonManager(this, "system");
 
-// Override ExtensionXPCShellUtils.jsm's overriding of the pref as the
-// search service needs it.
-Services.prefs.clearUserPref("services.settings.default_bucket");
-
 function promiseUninstallCompleted(extensionId) {
   return new Promise(resolve => {
     // eslint-disable-next-line mozilla/balanced-listeners
     ExtensionParent.apiManager.on("uninstall-complete", (type, { id }) => {
       if (id === extensionId) {
         executeSoon(resolve);
       }
     });
--- a/browser/components/newtab/docs/v2-system-addon/remote_cfr.md
+++ b/browser/components/newtab/docs/v2-system-addon/remote_cfr.md
@@ -56,20 +56,18 @@ Until [support for the DEV environment](
 
 > These are critical preferences, you should use a dedicated Firefox profile for development.
 
 ```javascript
   Services.prefs.setCharPref("services.settings.loglevel", "debug");
   Services.prefs.setCharPref("services.settings.server", "https://settings.dev.mozaws.net/v1");
   // Dev collections are signed with the STAGE infrastructure, use STAGE's hash:
   Services.prefs.setCharPref("security.content.signature.root_hash" "3C:01:44:6A:BE:90:36:CE:A9:A0:9A:CA:A3:A5:20:AC:62:8F:20:A7:AE:32:CE:86:1C:B2:EF:B7:0F:A0:C7:45");
-  // Prevent packaged dumps to interfere.
-  Services.prefs.setBoolPref("services.settings.load_dump", false);
-  // The changes are not approved yet, point the client to «preview»
-  Services.prefs.setCharPref("services.settings.default_bucket", "main-preview");
+  // Pull data from the preview bucket.
+  RemoteSettings.enablePreviewMode(true);
 ```
 
 **3. Set ASRouter CFR pref to use Remote Settings provider and enable asrouter devtools.**
 
 ```javascript
 Services.prefs.setStringPref("browser.newtabpage.activity-stream.asrouter.providers.cfr", JSON.stringify({"id":"cfr-remote","enabled":true,"type":"remote-settings","bucket":"cfr"}));
 Services.prefs.setBoolPref("browser.newtabpage.activity-stream.asrouter.devtoolsEnabled", true);
 ```
--- a/devtools/client/webconsole/test/browser/browser_webconsole_warning_group_content_blocking.js
+++ b/devtools/client/webconsole/test/browser/browser_webconsole_warning_group_content_blocking.js
@@ -174,16 +174,21 @@ add_task(cleanUp);
  *                             It should contain "<URL>".
  */
 async function testStorageAccessBlockedGrouping(groupLabel) {
   const { hud, win, tab } = await openNewWindowAndConsole(TEST_URI);
   const now = Date.now();
 
   await clearOutput(hud);
 
+  // Bug 1763367 - Filter out message like:
+  //  Cookie “name=value” has been rejected as third-party.
+  // that appear in a random order.
+  await setFilterState(hud, { text: "-has been rejected" });
+
   const getWarningMessage = url => groupLabel.replace("<URL>", url);
 
   const onStorageAccessBlockedMessage = waitForMessage(
     hud,
     getWarningMessage(`${TRACKER_IMG}?1&${now}`),
     ".warn"
   );
   emitStorageAccessBlockedMessage(tab, `${TRACKER_IMG}?1&${now}`);
--- a/devtools/client/webconsole/test/browser/browser_webconsole_warning_group_multiples.js
+++ b/devtools/client/webconsole/test/browser/browser_webconsole_warning_group_multiples.js
@@ -39,16 +39,21 @@ pushPref("privacy.trackingprotection.ena
 pushPref("devtools.webconsole.groupWarningMessages", true);
 
 add_task(async function testContentBlockingMessage() {
   await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS_REJECT_FOREIGN);
   await pushPref("devtools.webconsole.persistlog", true);
 
   const hud = await openNewTabAndConsole(TEST_URI);
 
+  // Bug 1763367 - Filter out message like:
+  //  Cookie “name=value” has been rejected as third-party.
+  // that appear in a random order.
+  await setFilterState(hud, { text: "-has been rejected" });
+
   info(
     "Log a tracking protection message to check a single message isn't grouped"
   );
   let onContentBlockedMessage = waitForMessage(
     hud,
     CONTENT_BLOCKED_URL,
     ".warn"
   );
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -386,27 +386,28 @@ static Maybe<nsRect> ComputeTheIntersect
       }
       target = containerFrame;
     } else {
       const auto& disp = *containerFrame->StyleDisplay();
       auto clipAxes = containerFrame->ShouldApplyOverflowClipping(&disp);
       // 3.2 TODO: Apply clip-path.
       if (clipAxes != PhysicalAxes::None) {
         // 3.1 Map intersectionRect to the coordinate space of container.
-        nsRect intersectionRectRelativeToContainer =
+        const nsRect intersectionRectRelativeToContainer =
             nsLayoutUtils::TransformFrameRectToAncestor(
                 target, intersectionRect.value(), containerFrame);
-        OverflowAreas::ApplyOverflowClippingOnRect(
+        const nsRect clipRect = OverflowAreas::GetOverflowClipRect(
             intersectionRectRelativeToContainer,
             containerFrame->GetRectRelativeToSelf(), clipAxes,
             containerFrame->OverflowClipMargin(clipAxes));
-        if (intersectionRectRelativeToContainer.IsEmpty()) {
+        intersectionRect = EdgeInclusiveIntersection(
+            intersectionRectRelativeToContainer, clipRect);
+        if (!intersectionRect) {
           return Nothing();
         }
-        intersectionRect = Some(intersectionRectRelativeToContainer);
         target = containerFrame;
       }
     }
     containerFrame =
         nsLayoutUtils::GetCrossDocParentFrameInProcess(containerFrame);
   }
   MOZ_ASSERT(intersectionRect);
 
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4262,27 +4262,24 @@ TextMetrics* CanvasRenderingContext2D::D
   aError = NS_OK;
   return nullptr;
 }
 
 gfxFontGroup* CanvasRenderingContext2D::GetCurrentFontStyle() {
   // Use lazy (re)initialization for the fontGroup since it's rather expensive.
 
   RefPtr<PresShell> presShell = GetPresShell();
-  nsPresContext* pc = presShell ? presShell->GetPresContext() : nullptr;
-  gfxTextPerfMetrics* tp = nullptr;
-  if (pc) {
-    tp = pc->GetTextPerfMetrics();
-  }
+  nsPresContext* presContext =
+      presShell ? presShell->GetPresContext() : nullptr;
 
   // If we have a cached fontGroup, check that it is valid for the current
   // prescontext; if not, we need to discard and re-create it.
   RefPtr<gfxFontGroup>& fontGroup = CurrentState().fontGroup;
   if (fontGroup) {
-    if (fontGroup->GetTextPerfMetrics() != tp) {
+    if (fontGroup->GetPresContext() != presContext) {
       fontGroup = nullptr;
     }
   }
 
   if (!fontGroup) {
     ErrorResult err;
     constexpr auto kDefaultFontStyle = "10px sans-serif"_ns;
     const float kDefaultFontSize = 10.0;
@@ -4300,17 +4297,18 @@ gfxFontGroup* CanvasRenderingContext2D::
       gfxFontStyle style;
       style.size = kDefaultFontSize;
       int32_t perDevPixel, perCSSPixel;
       GetAppUnitsValues(&perDevPixel, &perCSSPixel);
       gfxFloat devToCssSize = gfxFloat(perDevPixel) / gfxFloat(perCSSPixel);
       const auto* sans =
           Servo_FontFamily_Generic(StyleGenericFontFamily::SansSerif);
       fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(
-          pc, sans->families, &style, language, explicitLanguage, tp, nullptr,
+          presContext, sans->families, &style, language, explicitLanguage,
+          presContext ? presContext->GetTextPerfMetrics() : nullptr, nullptr,
           devToCssSize);
       if (fontGroup) {
         CurrentState().font = kDefaultFontStyle;
       } else {
         NS_ERROR("Default canvas font is invalid");
       }
     }
   } else {
--- a/dom/html/test/forms/test_input_datetime_tabindex.html
+++ b/dom/html/test/forms/test_input_datetime_tabindex.html
@@ -11,26 +11,28 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a>
 <p id="display"></p>
 <div id="content">
   <input id="time1" type="time" tabindex="0">
   <input id="time2" type="time" tabindex="-1">
   <input id="time3" type="time" tabindex="0">
+  <input id="time4" type="time" disabled>
   <input id="date1" type="date" tabindex="0">
   <input id="date2" type="date" tabindex="-1">
   <input id="date3" type="date" tabindex="0">
+  <input id="date4" type="date" disabled>
   <input id="datetime-local1" type="datetime-local" tabindex="0">
   <input id="datetime-local2" type="datetime-local" tabindex="-1">
   <input id="datetime-local3" type="datetime-local" tabindex="0">
+  <input id="datetime-local4" type="datetime-local" disabled>
 </div>
 <pre id="test">
-<script type="application/javascript">
-
+<script>
 /**
  * Test for Bug 1288591.
  * This test checks whether date/time input types tabindex attribute works
  * correctly.
  **/
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function() {
   test();
@@ -45,16 +47,17 @@ function checkInnerTextboxTabindex(input
   }
 
 }
 
 function testTabindex(type) {
   let input1 = document.getElementById(type + "1");
   let input2 = document.getElementById(type + "2");
   let input3 = document.getElementById(type + "3");
+  let input4 = document.getElementById(type + "4");
 
   input1.focus();
   is(document.activeElement, input1,
      "input element with tabindex=0 is focusable");
 
   let fieldCount = type == "datetime-local" ? 6 : 3;
 
   // Advance through inner fields.
@@ -71,24 +74,30 @@ function testTabindex(type) {
 
   input2.focus();
   is(document.activeElement, input2,
      "input element with tabindex=-1 is still focusable");
 
   checkInnerTextboxTabindex(input1, 0);
   checkInnerTextboxTabindex(input2, -1);
   checkInnerTextboxTabindex(input3, 0);
+  checkInnerTextboxTabindex(input4, -1);
 
   // Changing the tabindex attribute dynamically.
   input3.setAttribute("tabindex", "-1");
+
   synthesizeKey("KEY_Tab"); // need only one TAB since input2 is not tabbable
+
   isnot(document.activeElement, input3,
         "element with tabindex changed to -1 should not be tabbable");
+  isnot(document.activeElement, input4,
+        "disabled element should not be tabbable");
 
   checkInnerTextboxTabindex(input3, -1);
+  checkInnerTextboxTabindex(input4, -1);
 }
 
 function test() {
   for (let inputType of ["time", "date", "datetime-local"]) {
     testTabindex(inputType);
   }
 }
 
--- a/dom/serviceworkers/ServiceWorkerRegistrar.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrar.cpp
@@ -1307,58 +1307,63 @@ void ServiceWorkerRegistrar::ProfileStop
   MOZ_ASSERT(NS_IsMainThread());
 
   MonitorAutoLock lock(mMonitor);
 
   if (!mProfileDir) {
     nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                          getter_AddRefs(mProfileDir));
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
+      // If we do not have a profile directory, we are somehow screwed.
+      MOZ_DIAGNOSTIC_ASSERT(
+          false,
+          "NS_GetSpecialDirectory for NS_APP_USER_PROFILE_50_DIR failed!");
     }
   }
 
+  // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
+  // issued by the ServiceWorkerManagerService, so the appropriate place to
+  // trigger shutdown is on that thread.
+  //
+  // However, it's quite possible that the PBackground thread was not brought
+  // into existence for xpcshell tests.  We don't cause it to be created
+  // ourselves for any reason, for example.
+  //
+  // In this scenario, we know that:
+  // - We will receive exactly one call to ourself from BlockShutdown() and
+  //   BlockShutdown() will be called (at most) once.
+  // - The only way our Shutdown() method gets called is via
+  //   BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
+  //   invoked, which only happens if we get to that send below here that we
+  //   can't get to.
+  // - All Shutdown() does is set mShuttingDown=true (essential for
+  //   invariants) and invoke MaybeScheduleShutdownCompleted().
+  // - Since there is no PBackground thread, mSaveDataRunnableDispatched must
+  //   be false because only MaybeScheduleSaveData() set it and it only runs
+  //   on the background thread, so it cannot have run.  And so we would
+  //   expect MaybeScheduleShutdownCompleted() to schedule an invocation of
+  //   ShutdownCompleted on the main thread.
   PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
-  if (!child) {
-    // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
-    // issued by the ServiceWorkerManagerService, so the appropriate place to
-    // trigger shutdown is on that thread.
-    //
-    // However, it's quite possible that the PBackground thread was not brought
-    // into existence for xpcshell tests.  We don't cause it to be created
-    // ourselves for any reason, for example.
-    //
-    // In this scenario, we know that:
-    // - We will receive exactly one call to ourself from BlockShutdown() and
-    //   BlockShutdown() will be called (at most) once.
-    // - The only way our Shutdown() method gets called is via
-    //   BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
-    //   invoked, which only happens if we get to that send below here that we
-    //   can't get to.
-    // - All Shutdown() does is set mShuttingDown=true (essential for
-    //   invariants) and invoke MaybeScheduleShutdownCompleted().
-    // - Since there is no PBackground thread, mSaveDataRunnableDispatched must
-    //   be false because only MaybeScheduleSaveData() set it and it only runs
-    //   on the background thread, so it cannot have run.  And so we would
-    //   expect MaybeScheduleShutdownCompleted() to schedule an invocation of
-    //   ShutdownCompleted on the main thread.
-    //
-    // So it's appropriate for us to set mShuttingDown=true (as Shutdown would
-    // do) and directly invoke ShutdownCompleted() (as Shutdown would indirectly
-    // do via MaybeScheduleShutdownCompleted).
-    mShuttingDown = true;
-    ShutdownCompleted();
-    return;
+  if (mProfileDir && child) {
+    if (child->SendShutdownServiceWorkerRegistrar()) {
+      // Normal shutdown sequence has been initiated, go home.
+      return;
+    }
+    // If we get here, the PBackground thread has probably gone nuts and we
+    // want to know it.
+    MOZ_DIAGNOSTIC_ASSERT(
+        false, "Unable to send the ShutdownServiceWorkerRegistrar message.");
   }
 
-  if (!child->SendShutdownServiceWorkerRegistrar()) {
-    // If we get here, the PBackground thread has probably gone nuts and we
-    // want to know it. We could try to mitigate as above for xpcshell.
-    MOZ_CRASH("Unable to send the ShutdownServiceWorkerRegistrar message.");
-  }
+  // On any error it's appropriate to set mShuttingDown=true (as Shutdown
+  // would do) and directly invoke ShutdownCompleted() (as Shutdown would
+  // indirectly do via MaybeScheduleShutdownCompleted) in order to unblock
+  // shutdown.
+  mShuttingDown = true;
+  ShutdownCompleted();
 }
 
 // Async shutdown blocker methods
 
 NS_IMETHODIMP
 ServiceWorkerRegistrar::BlockShutdown(nsIAsyncShutdownClient* aClient) {
   ProfileStopped();
   return NS_OK;
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -299,16 +299,23 @@ gfxPlatformFontList::gfxPlatformFontList
     Preferences::RegisterCallback(FontWhitelistPrefChanged,
                                   kFontSystemWhitelistPref);
   }
 
   RegisterStrongMemoryReporter(new MemoryReporter());
 }
 
 gfxPlatformFontList::~gfxPlatformFontList() {
+  // Ensure there isn't still an InitFontList thread running, as we can't
+  // destroy the gfxPlatformFontList out from under it.
+  if (sInitFontListThread && !IsInitFontListThread()) {
+    PR_JoinThread(sInitFontListThread);
+    sInitFontListThread = nullptr;
+  }
+
   mSharedCmaps.Clear();
   ClearLangGroupPrefFontsLocked();
 
   NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
 
   Preferences::UnregisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -936,16 +936,20 @@ class gfxFontGroup final : public gfxTex
 
   // Returns the first font in the font-group that has an OpenType MATH table,
   // or null if no such font is available. The GetMathConstant methods may be
   // called on the returned font.
   gfxFont* GetFirstMathFont();
 
   const gfxFontStyle* GetStyle() const { return &mStyle; }
 
+  // Get the presContext for which this fontGroup was constructed. This may be
+  // null! (In the case of canvas not connected to a document.)
+  nsPresContext* GetPresContext() const { return mPresContext; }
+
   /**
    * The listed characters should be treated as invisible and zero-width
    * when creating textruns.
    */
   static bool IsInvalidChar(uint8_t ch);
   static bool IsInvalidChar(char16_t ch);
 
   /**
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -1172,29 +1172,27 @@ bool js::RegExpMatcher(JSContext* cx, un
   return RegExpMatcherImpl(cx, regexp, string, lastIndex, args.rval());
 }
 
 /*
  * Separate interface for use by the JITs.
  * This code cannot re-enter JIT code.
  */
 bool js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp,
-                          HandleString input, int32_t maybeLastIndex,
+                          HandleString input, int32_t lastIndex,
                           MatchPairs* maybeMatches, MutableHandleValue output) {
+  MOZ_ASSERT(lastIndex >= 0 && size_t(lastIndex) <= input->length());
+
   // RegExp execution was successful only if the pairs have actually been
   // filled in. Note that IC code always passes a nullptr maybeMatches.
   if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch) {
     RootedRegExpShared shared(cx, regexp->as<RegExpObject>().getShared());
     return CreateRegExpMatchResult(cx, shared, input, *maybeMatches, output);
   }
-
-  // |maybeLastIndex| only contains a valid value when the RegExp execution
-  // was not successful.
-  MOZ_ASSERT(maybeLastIndex >= 0);
-  return RegExpMatcherImpl(cx, regexp, input, maybeLastIndex, output);
+  return RegExpMatcherImpl(cx, regexp, input, lastIndex, output);
 }
 
 /*
  * ES 2017 draft rev 6a13789aa9e7c6de4e96b7d3e24d9e6eba6584ad 21.2.5.2.2
  * steps 3, 9-25, except 12.a.i, 12.c.i.1, 15.
  * This code is inlined in CodeGenerator.cpp generateRegExpSearcherStub,
  * changes to this code need to get reflected in there too.
  */
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -37,17 +37,17 @@ JSObject* InitRegExpClass(JSContext* cx,
                                            const MatchPairs& matches,
                                            MutableHandleValue rval);
 
 [[nodiscard]] extern bool RegExpMatcher(JSContext* cx, unsigned argc,
                                         Value* vp);
 
 [[nodiscard]] extern bool RegExpMatcherRaw(JSContext* cx, HandleObject regexp,
                                            HandleString input,
-                                           int32_t maybeLastIndex,
+                                           int32_t lastIndex,
                                            MatchPairs* maybeMatches,
                                            MutableHandleValue output);
 
 [[nodiscard]] extern bool RegExpSearcher(JSContext* cx, unsigned argc,
                                          Value* vp);
 
 [[nodiscard]] extern bool RegExpSearcherRaw(JSContext* cx, HandleObject regexp,
                                             HandleString input,
--- a/js/src/irregexp/RegExpNativeMacroAssembler.cpp
+++ b/js/src/irregexp/RegExpNativeMacroAssembler.cpp
@@ -51,23 +51,26 @@ SMRegExpMacroAssembler::SMRegExpMacroAss
       mode_(mode),
       num_registers_(num_capture_registers),
       num_capture_registers_(num_capture_registers) {
   // Each capture has a start and an end register
   MOZ_ASSERT(num_capture_registers_ % 2 == 0);
 
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
 
-  temp0_ = regs.takeAny();
-  temp1_ = regs.takeAny();
-  temp2_ = regs.takeAny();
   input_end_pointer_ = regs.takeAny();
   current_character_ = regs.takeAny();
   current_position_ = regs.takeAny();
   backtrack_stack_pointer_ = regs.takeAny();
+  temp0_ = regs.takeAny();
+  temp1_ = regs.takeAny();
+  if (!regs.empty()) {
+    // Not enough registers on x86.
+    temp2_ = regs.takeAny();
+  }
   savedRegisters_ = js::jit::SavedNonVolatileRegisters(regs);
 
   masm_.jump(&entry_label_);  // We'll generate the entry code later
   masm_.bind(&start_label_);  // and continue from here.
 }
 
 int SMRegExpMacroAssembler::stack_limit_slack() {
   return RegExpStack::kStackLimitSlack;
@@ -293,17 +296,19 @@ void SMRegExpMacroAssembler::CheckNotBac
     // We call a helper function for case-insensitive non-latin1 strings.
 
     // Save volatile regs. temp1_, temp2_, and current_character_
     // don't need to be saved.  current_position_ needs to be saved
     // even if it's non-volatile, because we modify it to use as an argument.
     LiveGeneralRegisterSet volatileRegs(GeneralRegisterSet::Volatile());
     volatileRegs.addUnchecked(current_position_);
     volatileRegs.takeUnchecked(temp1_);
-    volatileRegs.takeUnchecked(temp2_);
+    if (temp2_ != js::jit::InvalidReg) {
+      volatileRegs.takeUnchecked(temp2_);
+    }
     volatileRegs.takeUnchecked(current_character_);
     masm_.PushRegsInMask(volatileRegs);
 
     // Parameters are
     //   Address captured - Address of captured substring's start.
     //   Address current - Address of current character position.
     //   size_t byte_length - length of capture (in bytes)
 
@@ -355,80 +360,98 @@ void SMRegExpMacroAssembler::CheckNotBac
   if (read_backward) {
     // Offset by length when matching backwards.
     masm_.subPtr(temp0_, current_position_);
   }
 
   // Compute end of match string
   masm_.addPtr(current_position_, temp0_);
 
+  Register nextCaptureChar = temp1_;
+  Register nextMatchChar = temp2_;
+
+  if (temp2_ == js::jit::InvalidReg) {
+    masm_.push(backtrack_stack_pointer_);
+    nextMatchChar = backtrack_stack_pointer_;
+  }
+
   js::jit::Label success;
   js::jit::Label fail;
   js::jit::Label loop;
   masm_.bind(&loop);
 
   // Load next character from each string.
   if (mode_ == LATIN1) {
-    masm_.load8ZeroExtend(Address(current_character_, 0), temp1_);
-    masm_.load8ZeroExtend(Address(current_position_, 0), temp2_);
+    masm_.load8ZeroExtend(Address(current_character_, 0), nextCaptureChar);
+    masm_.load8ZeroExtend(Address(current_position_, 0), nextMatchChar);
   } else {
-    masm_.load16ZeroExtend(Address(current_character_, 0), temp1_);
-    masm_.load16ZeroExtend(Address(current_position_, 0), temp2_);
+    masm_.load16ZeroExtend(Address(current_character_, 0), nextCaptureChar);
+    masm_.load16ZeroExtend(Address(current_position_, 0), nextMatchChar);
   }
 
   if (ignore_case) {
     MOZ_ASSERT(mode_ == LATIN1);
     // Try exact match.
     js::jit::Label loop_increment;
-    masm_.branch32(Assembler::Equal, temp1_, temp2_, &loop_increment);
+    masm_.branch32(Assembler::Equal, nextCaptureChar, nextMatchChar,
+                   &loop_increment);
 
     // Mismatch. Try case-insensitive match.
     // Force the capture character to lower case (by setting bit 0x20)
     // then check to see if it is a letter.
     js::jit::Label convert_match;
-    masm_.or32(Imm32(0x20), temp1_);
+    masm_.or32(Imm32(0x20), nextCaptureChar);
 
     // Check if it is in [a,z].
-    masm_.computeEffectiveAddress(Address(temp1_, -'a'), temp2_);
-    masm_.branch32(Assembler::BelowOrEqual, temp2_, Imm32('z' - 'a'),
+    masm_.computeEffectiveAddress(Address(nextCaptureChar, -'a'),
+                                  nextMatchChar);
+    masm_.branch32(Assembler::BelowOrEqual, nextMatchChar, Imm32('z' - 'a'),
                    &convert_match);
     // Check for values in range [224,254].
     // Exclude 247 (U+00F7 DIVISION SIGN).
-    masm_.sub32(Imm32(224 - 'a'), temp2_);
-    masm_.branch32(Assembler::Above, temp2_, Imm32(254 - 224), &fail);
-    masm_.branch32(Assembler::Equal, temp2_, Imm32(247 - 224), &fail);
+    masm_.sub32(Imm32(224 - 'a'), nextMatchChar);
+    masm_.branch32(Assembler::Above, nextMatchChar, Imm32(254 - 224), &fail);
+    masm_.branch32(Assembler::Equal, nextMatchChar, Imm32(247 - 224), &fail);
 
     // Capture character is lower case. Convert match character
     // to lower case and compare.
     masm_.bind(&convert_match);
-    masm_.load8ZeroExtend(Address(current_position_, 0), temp2_);
-    masm_.or32(Imm32(0x20), temp2_);
-    masm_.branch32(Assembler::NotEqual, temp1_, temp2_, &fail);
+    masm_.load8ZeroExtend(Address(current_position_, 0), nextMatchChar);
+    masm_.or32(Imm32(0x20), nextMatchChar);
+    masm_.branch32(Assembler::NotEqual, nextCaptureChar, nextMatchChar, &fail);
 
     masm_.bind(&loop_increment);
   } else {
     // Fail if characters do not match.
-    masm_.branch32(Assembler::NotEqual, temp1_, temp2_, &fail);
+    masm_.branch32(Assembler::NotEqual, nextCaptureChar, nextMatchChar, &fail);
   }
 
   // Increment pointers into match and capture strings.
   masm_.addPtr(Imm32(char_size()), current_character_);
   masm_.addPtr(Imm32(char_size()), current_position_);
 
   // Loop if we have not reached the end of the match string.
   masm_.branchPtr(Assembler::Below, current_position_, temp0_, &loop);
   masm_.jump(&success);
 
   // If we fail, restore current_position_ and branch.
   masm_.bind(&fail);
+  if (temp2_ == js::jit::InvalidReg) {
+    // Restore backtrack_stack_pointer_ when it was used as a temp register.
+    masm_.pop(backtrack_stack_pointer_);
+  }
   masm_.pop(current_position_);
   JumpOrBacktrack(on_no_match);
 
   masm_.bind(&success);
 
+  if (temp2_ == js::jit::InvalidReg) {
+    // Restore backtrack_stack_pointer_ when it was used as a temp register.
+    masm_.pop(backtrack_stack_pointer_);
+  }
   // Drop saved value of current_position_
   masm_.addToStackPtr(Imm32(sizeof(uintptr_t)));
 
   // current_position_ is a pointer. Convert it back to an offset.
   masm_.subPtr(input_end_pointer_, current_position_);
   if (read_backward) {
     // Subtract match length if we matched backward
     masm_.addPtr(register_location(start_reg), current_position_);
@@ -943,25 +966,29 @@ void SMRegExpMacroAssembler::initFrameAn
   // and the address of the InputOutputData is in temp0_.
   Register ioDataReg = temp0_;
 
   Register matchesReg = temp1_;
   masm_.loadPtr(Address(ioDataReg, offsetof(InputOutputData, matches)),
                 matchesReg);
 
   // Initialize output registers
-  masm_.loadPtr(Address(matchesReg, MatchPairs::offsetOfPairs()), temp2_);
-  masm_.storePtr(temp2_, matches());
-  masm_.load32(Address(matchesReg, MatchPairs::offsetOfPairCount()), temp2_);
-  masm_.store32(temp2_, numMatches());
+  // Use |backtrack_stack_pointer_| as an additional temp register. This is safe
+  // because we haven't yet written any data to |backtrack_stack_pointer_|.
+  Register extraTemp = backtrack_stack_pointer_;
+
+  masm_.loadPtr(Address(matchesReg, MatchPairs::offsetOfPairs()), extraTemp);
+  masm_.storePtr(extraTemp, matches());
+  masm_.load32(Address(matchesReg, MatchPairs::offsetOfPairCount()), extraTemp);
+  masm_.store32(extraTemp, numMatches());
 
 #ifdef DEBUG
   // Bounds-check numMatches.
   js::jit::Label enoughRegisters;
-  masm_.branchPtr(Assembler::GreaterThanOrEqual, temp2_,
+  masm_.branchPtr(Assembler::GreaterThanOrEqual, extraTemp,
                   ImmWord(num_capture_registers_ / 2), &enoughRegisters);
   masm_.assumeUnreachable("Not enough output pairs for RegExp");
   masm_.bind(&enoughRegisters);
 #endif
 
   // Load input start pointer.
   masm_.loadPtr(Address(ioDataReg, offsetof(InputOutputData, inputStart)),
                 current_position_);
@@ -993,17 +1020,17 @@ void SMRegExpMacroAssembler::initFrameAn
   masm_.jump(&start_regexp);
 
   masm_.bind(&load_previous_character);
   LoadCurrentCharacterUnchecked(-1, 1);
   masm_.bind(&start_regexp);
 
   // Initialize captured registers with inputStart - 1
   MOZ_ASSERT(num_capture_registers_ > 0);
-  Register inputStartMinusOneReg = temp2_;
+  Register inputStartMinusOneReg = temp0_;
   masm_.loadPtr(inputStart(), inputStartMinusOneReg);
   masm_.subPtr(Imm32(char_size()), inputStartMinusOneReg);
   if (num_capture_registers_ > 8) {
     masm_.movePtr(ImmWord(register_offset(0)), temp1_);
     js::jit::Label init_loop;
     masm_.bind(&init_loop);
     masm_.storePtr(inputStartMinusOneReg, BaseIndex(masm_.getStackPointer(),
                                                     temp1_, js::jit::TimesOne));
@@ -1042,17 +1069,21 @@ void SMRegExpMacroAssembler::successHand
   // Pos (1-byte): [-6 ][-5 ][-4 ][-3 ][-2 ][-1 ][ 0 ] // IS = -6
   // Pos (2-byte): [-12][-10][-8 ][-6 ][-4 ][-2 ][ 0 ] // IS = -12
   //
   // To convert a position to an index, we subtract InputStart, and
   // divide the result by char_size.
   Register matchesReg = temp1_;
   masm_.loadPtr(matches(), matchesReg);
 
-  Register inputStartReg = temp2_;
+  // Use |backtrack_stack_pointer_| as an additional temp register. This is safe
+  // because we don't read from |backtrack_stack_pointer_| after this point.
+  Register extraTemp = backtrack_stack_pointer_;
+
+  Register inputStartReg = extraTemp;
   masm_.loadPtr(inputStart(), inputStartReg);
 
   for (int i = 0; i < num_capture_registers_; i++) {
     masm_.loadPtr(register_location(i), temp0_);
     masm_.subPtr(inputStartReg, temp0_);
     if (mode_ == UC16) {
       masm_.rshiftPtrArithmetic(Imm32(1), temp0_);
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/setter-is-native.js
@@ -0,0 +1,15 @@
+// Make sure we use an IC call.
+setJitCompilerOption("ion.forceinlineCaches", 1);
+
+// Assume |eval| is always a native function.
+var obj = Object.defineProperty({}, "prop", {
+  set: eval
+});
+
+var p;
+for (let i = 0; i < 1000; ++i) {
+  // Call the native setter (eval).
+  obj.prop = `p = ${i}`;
+
+  assertEq(p, i);
+}
--- a/js/src/jit/BaselineCodeGen.cpp
+++ b/js/src/jit/BaselineCodeGen.cpp
@@ -514,17 +514,17 @@ bool BaselineCodeGen<Handler>::emitOutOf
   masm.bind(&postBarrierSlot_);
 
   saveInterpreterPCReg();
 
   Register objReg = R2.scratchReg();
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
   regs.take(R0);
   regs.take(objReg);
-  regs.take(BaselineFrameReg);
+  regs.takeUnchecked(BaselineFrameReg);
   Register scratch = regs.takeAny();
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64)
   // On ARM, save the link register before calling.  It contains the return
   // address.  The |masm.ret()| later will pop this into |pc| to return.
   masm.push(lr);
 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
   masm.push(ra);
 #elif defined(JS_CODEGEN_LOONG64)
@@ -1447,17 +1447,17 @@ bool BaselineCompilerCodeGen::emitWarmUp
 
     // Restore the stack pointer so that the return address is on top of
     // the stack.
     masm.addToStackPtr(Imm32(frame.frameSize()));
 
 #ifdef DEBUG
     // Get a scratch register that's not osrDataReg or OsrFrameReg.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(BaselineFrameReg);
+    regs.takeUnchecked(BaselineFrameReg);
     regs.take(osrDataReg);
     regs.take(OsrFrameReg);
 
     Register scratchReg = regs.takeAny();
 
     // If profiler instrumentation is on, ensure that lastProfilingFrame is
     // the frame currently being OSR-ed
     {
@@ -3684,17 +3684,17 @@ bool BaselineCompilerCodeGen::emit_SetAl
 
   masm.bind(&skipBarrier);
   return true;
 }
 
 template <>
 bool BaselineInterpreterCodeGen::emit_SetAliasedVar() {
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-  regs.take(BaselineFrameReg);
+  regs.takeUnchecked(BaselineFrameReg);
   regs.take(R2);
   if (HasInterpreterPCReg()) {
     regs.take(InterpreterPCReg);
   }
 
   Register env = regs.takeAny();
   Register scratch1 = regs.takeAny();
   Register scratch2 = regs.takeAny();
@@ -5275,17 +5275,17 @@ template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_EndIter() {
   // Pop iterator value.
   frame.pop();
 
   // Pop the iterator object to close in R0.
   frame.popRegsAndSync(1);
 
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-  regs.take(BaselineFrameReg);
+  regs.takeUnchecked(BaselineFrameReg);
   if (HasInterpreterPCReg()) {
     regs.take(InterpreterPCReg);
   }
 
   Register obj = R0.scratchReg();
   regs.take(obj);
   masm.unboxObject(R0, obj);
 
@@ -5770,17 +5770,17 @@ bool BaselineCodeGen<Handler>::emitEnter
 }
 
 template <typename Handler>
 bool BaselineCodeGen<Handler>::emit_Resume() {
   frame.syncStack(0);
   masm.assertStackAlignment(sizeof(Value), 0);
 
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-  regs.take(BaselineFrameReg);
+  regs.takeUnchecked(BaselineFrameReg);
   if (HasInterpreterPCReg()) {
     regs.take(InterpreterPCReg);
   }
 
   saveInterpreterPCReg();
 
   // Load generator object.
   Register genObj = regs.takeAny();
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -120,17 +120,17 @@ AllocatableGeneralRegisterSet BaselineIC
   MOZ_ASSERT(!regs.has(BaselineSecondScratchReg));
 #elif defined(JS_CODEGEN_ARM64)
   MOZ_ASSERT(!regs.has(PseudoStackPointer));
   MOZ_ASSERT(!regs.has(RealStackPointer));
   MOZ_ASSERT(!regs.has(ICTailCallReg));
 #else
   MOZ_ASSERT(!regs.has(BaselineStackReg));
 #endif
-  regs.take(BaselineFrameReg);
+  regs.takeUnchecked(BaselineFrameReg);
   regs.take(ICStubReg);
 
   switch (numInputs) {
     case 0:
       break;
     case 1:
       regs.take(R0);
       break;
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -1077,16 +1077,17 @@ class MOZ_RAII AutoScratchRegisterMaybeO
     }
   }
 
   AutoScratchRegisterMaybeOutputType(
       const AutoScratchRegisterMaybeOutputType&) = delete;
 
   void operator=(const AutoScratchRegisterMaybeOutputType&) = delete;
 
+  Register get() const { return scratchReg_; }
   operator Register() const { return scratchReg_; }
 };
 
 // AutoCallVM is a wrapper class that unifies methods shared by
 // IonCacheIRCompiler and BaselineCacheIRCompiler that perform a callVM, but
 // require stub specific functionality before performing the VM call.
 //
 // Expected Usage:
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2525,17 +2525,21 @@ JitCode* JitRealm::generateRegExpMatcher
   AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
   regs.take(input);
   regs.take(regexp);
   regs.take(lastIndex);
 
   Register temp1 = regs.takeAny();
   Register temp2 = regs.takeAny();
   Register temp3 = regs.takeAny();
-  Register temp4 = regs.takeAny();
+  Register maybeTemp4 = InvalidReg;
+  if (!regs.empty()) {
+    // There are not enough registers on x86.
+    maybeTemp4 = regs.takeAny();
+  }
   Register maybeTemp5 = InvalidReg;
   if (!regs.empty()) {
     // There are not enough registers on x86.
     maybeTemp5 = regs.takeAny();
   }
 
   ArrayObject* templateObject =
       cx->realm()->regExps.getOrCreateMatchResultTemplateObject(cx);
@@ -2649,50 +2653,91 @@ JitCode* JitRealm::generateRegExpMatcher
                 "MatchPair consists of two int32 values representing the start"
                 "and the end offset of the match");
 
   Address pairCountAddress =
       RegExpPairCountAddress(masm, inputOutputDataStartOffset);
 
   size_t pairsVectorStartOffset =
       RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
-  Address firstMatchPairStartAddress(
-      masm.getStackPointer(),
-      pairsVectorStartOffset + offsetof(MatchPair, start));
 
   // Incremented by one below for each match pair.
   Register matchIndex = temp2;
   masm.move32(Imm32(0), matchIndex);
 
   // The element in which to store the result of the current match.
   size_t elementsOffset = NativeObject::offsetOfFixedElements();
   BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset);
 
   // The current match pair's "start" and "limit" member.
   BaseIndex matchPairStart(masm.getStackPointer(), matchIndex, TimesEight,
                            pairsVectorStartOffset + offsetof(MatchPair, start));
   BaseIndex matchPairLimit(masm.getStackPointer(), matchIndex, TimesEight,
                            pairsVectorStartOffset + offsetof(MatchPair, limit));
 
+  Label* depStrFailure = &oolEntry;
+  Label restoreRegExpAndLastIndex;
+
+  Register temp4;
+  if (maybeTemp4 == InvalidReg) {
+    depStrFailure = &restoreRegExpAndLastIndex;
+
+    // We don't have enough registers for a fourth temporary. Reuse |regexp|
+    // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
+    masm.push(regexp);
+    temp4 = regexp;
+
+    // Adjust offsets for the push.
+    MOZ_ASSERT(pairCountAddress.base == masm.getStackPointer());
+    MOZ_ASSERT(matchPairStart.base == masm.getStackPointer());
+    MOZ_ASSERT(matchPairLimit.base == masm.getStackPointer());
+    pairCountAddress.offset += sizeof(void*);
+    matchPairStart.offset += sizeof(void*);
+    matchPairLimit.offset += sizeof(void*);
+  } else {
+    temp4 = maybeTemp4;
+  }
+
   Register temp5;
   if (maybeTemp5 == InvalidReg) {
-    // We don't have enough registers for a fifth temporary. Reuse
-    // |lastIndex| as a temporary. We don't need to restore its value,
-    // because |lastIndex| is no longer used after a successful match.
-    // (Neither here nor in the OOL path, cf. js::RegExpMatcherRaw.)
+    depStrFailure = &restoreRegExpAndLastIndex;
+
+    // We don't have enough registers for a fifth temporary. Reuse |lastIndex|
+    // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
+    masm.push(lastIndex);
     temp5 = lastIndex;
+
+    // Adjust offsets for the push.
+    MOZ_ASSERT(pairCountAddress.base == masm.getStackPointer());
+    MOZ_ASSERT(matchPairStart.base == masm.getStackPointer());
+    MOZ_ASSERT(matchPairLimit.base == masm.getStackPointer());
+    pairCountAddress.offset += sizeof(void*);
+    matchPairStart.offset += sizeof(void*);
+    matchPairLimit.offset += sizeof(void*);
   } else {
     temp5 = maybeTemp5;
   }
 
+  auto maybeRestoreRegExpAndLastIndex = [&]() {
+    // NOTE: pairCountAddress, matchPairStart, and matchPairLimit offsets
+    // aren't restored, because we don't read them after the loop.
+
+    if (maybeTemp5 == InvalidReg) {
+      masm.pop(lastIndex);
+    }
+    if (maybeTemp4 == InvalidReg) {
+      masm.pop(regexp);
+    }
+  };
+
   // Loop to construct the match strings. There are two different loops,
   // depending on whether the input is a Two-Byte or a Latin-1 string.
   CreateDependentString depStrs[]{
-      {CharEncoding::TwoByte, temp3, temp4, temp5, &oolEntry},
-      {CharEncoding::Latin1, temp3, temp4, temp5, &oolEntry},
+      {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure},
+      {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure},
   };
 
   {
     Label isLatin1, done;
     masm.branchLatin1String(input, &isLatin1);
 
     for (auto& depStr : depStrs) {
       if (depStr.encoding() == CharEncoding::Latin1) {
@@ -2730,25 +2775,31 @@ JitCode* JitRealm::generateRegExpMatcher
 
 #ifdef DEBUG
     masm.assumeUnreachable("The match string loop doesn't fall through.");
 #endif
 
     masm.bind(&done);
   }
 
+  maybeRestoreRegExpAndLastIndex();
+
   // Fill in the rest of the output object.
   masm.store32(
       matchIndex,
       Address(object,
               elementsOffset + ObjectElements::offsetOfInitializedLength()));
   masm.store32(
       matchIndex,
       Address(object, elementsOffset + ObjectElements::offsetOfLength()));
 
+  Address firstMatchPairStartAddress(
+      masm.getStackPointer(),
+      pairsVectorStartOffset + offsetof(MatchPair, start));
+
   masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
 
   masm.load32(firstMatchPairStartAddress, temp3);
   masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
 
   // No post barrier needed (address is within nursery object.)
   masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
 
@@ -2765,16 +2816,20 @@ JitCode* JitRealm::generateRegExpMatcher
     depStr.generateFallback(masm);
   }
 
   // Fallback path for createGCObject.
   masm.bind(&matchResultFallback);
   CreateMatchResultFallback(masm, object, temp2, temp3, templateObj, &oolEntry);
   masm.jump(&matchResultJoin);
 
+  // Fall-through to the ool entry after restoring the registers.
+  masm.bind(&restoreRegExpAndLastIndex);
+  maybeRestoreRegExpAndLastIndex();
+
   // Use an undefined value to signal to the caller that the OOL stub needs to
   // be called.
   masm.bind(&oolEntry);
   masm.moveValue(UndefinedValue(), result);
   masm.ret();
 
   Linker linker(masm);
   JitCode* code = linker.newCode(cx, CodeKind::Other);
@@ -16940,24 +16995,23 @@ void CodeGenerator::emitIonToWasmCallBas
             break;
           case wasm::RefType::TypeIndex:
             MOZ_CRASH("unexpected return type when calling from ion to wasm");
         }
         break;
     }
   }
 
-  bool profilingEnabled = isProfilerInstrumentationEnabled();
   WasmInstanceObject* instObj = lir->mir()->instanceObject();
 
   Register scratch = ToRegister(lir->temp());
 
   uint32_t callOffset;
   GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs,
-                            profilingEnabled, scratch, &callOffset);
+                            scratch, &callOffset);
 
   // Add the instance object to the constant pool, so it is transferred to
   // the owning IonScript and so that it gets traced as long as the IonScript
   // lives.
 
   uint32_t unused;
   masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused));
 
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -924,17 +924,17 @@ bool IonCacheIRCompiler::emitCallNativeG
   AutoOutputRegister output(*this);
 
   ValueOperand receiver = allocator.useValueRegister(masm, receiverId);
 
   JSFunction* target = &objectStubField(getterOffset)->as<JSFunction>();
   MOZ_ASSERT(target->isNativeFun());
 
   AutoScratchRegisterMaybeOutput argJSContext(allocator, masm, output);
-  AutoScratchRegister argUintN(allocator, masm);
+  AutoScratchRegisterMaybeOutputType argUintN(allocator, masm, output);
   AutoScratchRegister argVp(allocator, masm);
   AutoScratchRegister scratch(allocator, masm);
 
   allocator.discardStack(masm);
 
   // Native functions have the signature:
   //  bool (*)(JSContext*, unsigned, Value* vp)
   // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
@@ -1046,17 +1046,17 @@ bool IonCacheIRCompiler::emitProxyGetRes
   AutoOutputRegister output(*this);
 
   Register obj = allocator.useRegister(masm, objId);
   jsid id = idStubField(idOffset);
 
   // ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id,
   //                  MutableHandleValue vp)
   AutoScratchRegisterMaybeOutput argJSContext(allocator, masm, output);
-  AutoScratchRegister argProxy(allocator, masm);
+  AutoScratchRegisterMaybeOutputType argProxy(allocator, masm, output);
   AutoScratchRegister argId(allocator, masm);
   AutoScratchRegister argVp(allocator, masm);
   AutoScratchRegister scratch(allocator, masm);
 
   allocator.discardStack(masm);
 
   // Push stubCode for marking.
   pushStubCodePointer();
@@ -1421,17 +1421,22 @@ bool IonCacheIRCompiler::emitCallNativeS
   Register receiver = allocator.useRegister(masm, receiverId);
   JSFunction* target = &objectStubField(setterOffset)->as<JSFunction>();
   MOZ_ASSERT(target->isNativeFun());
   ConstantOrRegister val = allocator.useConstantOrRegister(masm, rhsId);
 
   AutoScratchRegister argJSContext(allocator, masm);
   AutoScratchRegister argVp(allocator, masm);
   AutoScratchRegister argUintN(allocator, masm);
+#ifndef JS_CODEGEN_X86
   AutoScratchRegister scratch(allocator, masm);
+#else
+  // Not enough registers on x86.
+  Register scratch = argUintN;
+#endif
 
   allocator.discardStack(masm);
 
   // Set up the call:
   //  bool (*)(JSContext*, unsigned, Value* vp)
   // vp[0] is callee/outparam
   // vp[1] is |this|
   // vp[2] is the value
@@ -1456,16 +1461,20 @@ bool IonCacheIRCompiler::emitCallNativeS
   masm.enterFakeExitFrame(argJSContext, scratch, ExitFrameType::IonOOLNative);
 
   if (!sameRealm) {
     masm.switchToRealm(target->realm(), scratch);
   }
 
   // Make the call.
   masm.setupUnalignedABICall(scratch);
+#ifdef JS_CODEGEN_X86
+  // Reload argUintN because it was clobbered.
+  masm.move32(Imm32(1), argUintN);
+#endif
   masm.passABIArg(argJSContext);
   masm.passABIArg(argUintN);
   masm.passABIArg(argVp);
   masm.callWithABI(DynamicFunction<JSNative>(target->native()), MoveOp::GENERAL,
                    CheckUnsafeCallWithABI::DontCheckHasExitFrame);
 
   // Test for failure.
   masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -6536,32 +6536,26 @@ void LIRGenerator::visitArrayState(MArra
 
 void LIRGenerator::visitIonToWasmCall(MIonToWasmCall* ins) {
   // The instruction needs a temp register:
   // - that's not the FramePointer, since wasm is going to use it in the
   // function.
   // - that's not aliasing an input register.
   LDefinition scratch = tempFixed(ABINonArgReg0);
 
-  // Also prevent register allocation from using wasm's FramePointer, in
-  // non-profiling mode.
-  LDefinition fp = gen->isProfilerInstrumentationEnabled()
-                       ? LDefinition::BogusTemp()
-                       : tempFixed(FramePointer);
-
   // Note that since this is a LIR call instruction, regalloc will prevent
   // the use*AtStart below from reusing any of the temporaries.
 
   LInstruction* lir;
   if (ins->type() == MIRType::Value) {
-    lir = allocateVariadic<LIonToWasmCallV>(ins->numOperands(), scratch, fp);
+    lir = allocateVariadic<LIonToWasmCallV>(ins->numOperands(), scratch);
   } else if (ins->type() == MIRType::Int64) {
-    lir = allocateVariadic<LIonToWasmCallI64>(ins->numOperands(), scratch, fp);
+    lir = allocateVariadic<LIonToWasmCallI64>(ins->numOperands(), scratch);
   } else {
-    lir = allocateVariadic<LIonToWasmCall>(ins->numOperands(), scratch, fp);
+    lir = allocateVariadic<LIonToWasmCall>(ins->numOperands(), scratch);
   }
   if (!lir) {
     abort(AbortReason::Alloc, "OOM: LIRGenerator::visitIonToWasmCall");
     return;
   }
 
   ABIArgGenerator abi;
   for (unsigned i = 0; i < ins->numOperands(); i++) {
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -2079,17 +2079,17 @@ void MacroAssembler::generateBailoutTail
     setupUnalignedABICall(temp);
     passABIArg(bailoutInfo);
     callWithABI<Fn, FinishBailoutToBaseline>(
         MoveOp::GENERAL, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
     branchIfFalseBool(ReturnReg, exceptionLabel());
 
     // Restore values where they need to be and resume execution.
     AllocatableGeneralRegisterSet enterRegs(GeneralRegisterSet::All());
-    enterRegs.take(BaselineFrameReg);
+    enterRegs.takeUnchecked(BaselineFrameReg);
     Register jitcodeReg = enterRegs.takeAny();
 
     pop(jitcodeReg);
     pop(BaselineFrameReg);
 
     // Discard exit frame.
     addToStackPtr(Imm32(ExitFrameLayout::SizeWithFooter()));
 
--- a/js/src/jit/RegisterAllocator.h
+++ b/js/src/jit/RegisterAllocator.h
@@ -209,25 +209,16 @@ class InstructionDataMap {
   LNode*& operator[](CodePosition pos) { return operator[](pos.ins()); }
   LNode* const& operator[](CodePosition pos) const {
     return operator[](pos.ins());
   }
   LNode*& operator[](uint32_t ins) { return insData_[ins]; }
   LNode* const& operator[](uint32_t ins) const { return insData_[ins]; }
 };
 
-inline void TakeJitRegisters(bool isProfiling, AllocatableRegisterSet* set) {
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) || \
-    defined(JS_CODEGEN_ARM64)
-  if (isProfiling) {
-    set->take(AnyRegister(FramePointer));
-  }
-#endif
-}
-
 // Common superclass for register allocators.
 class RegisterAllocator {
   void operator=(const RegisterAllocator&) = delete;
   RegisterAllocator(const RegisterAllocator&) = delete;
 
  protected:
   // Context
   MIRGenerator* mir;
@@ -239,20 +230,19 @@ class RegisterAllocator {
 
   // Computed data
   InstructionDataMap insData;
   Vector<CodePosition, 12, SystemAllocPolicy> entryPositions;
   Vector<CodePosition, 12, SystemAllocPolicy> exitPositions;
 
   RegisterAllocator(MIRGenerator* mir, LIRGenerator* lir, LIRGraph& graph)
       : mir(mir), lir(lir), graph(graph), allRegisters_(RegisterSet::All()) {
+    MOZ_ASSERT(!allRegisters_.has(FramePointer));
     if (mir->compilingWasm()) {
       takeWasmRegisters(allRegisters_);
-    } else {
-      TakeJitRegisters(mir->instrumentedProfiling(), &allRegisters_);
     }
   }
 
   [[nodiscard]] bool init();
 
   TempAllocator& alloc() const { return mir->alloc(); }
 
   CodePosition outputOf(const LNode* ins) const {
@@ -314,17 +304,17 @@ class RegisterAllocator {
  public:
   template <typename TakeableSet>
   static void takeWasmRegisters(TakeableSet& regs) {
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_ARM) ||      \
     defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_MIPS32) || \
     defined(JS_CODEGEN_MIPS64) || defined(JS_CODEGEN_LOONG64)
     regs.take(HeapReg);
 #endif
-    regs.take(FramePointer);
+    MOZ_ASSERT(!regs.has(FramePointer));
   }
 };
 
 static inline AnyRegister GetFixedRegister(const LDefinition* def,
                                            const LUse* use) {
   return def->isFloatReg()
              ? AnyRegister(FloatRegister::FromCode(use->registerCode()))
              : AnyRegister(Register::FromCode(use->registerCode()));
--- a/js/src/jit/WarpOracle.cpp
+++ b/js/src/jit/WarpOracle.cpp
@@ -413,24 +413,23 @@ AbortReasonOr<WarpScriptSnapshot*> WarpS
         if (IsAsmJSModule(fun)) {
           return abort(AbortReason::Disable, "asm.js module function lambda");
         }
         break;
       }
 
       case JSOp::GetElemSuper: {
 #if defined(JS_CODEGEN_X86)
-        // x86 does not have enough registers if profiling is enabled.
-        if (mirGen_.instrumentedProfiling()) {
-          return abort(AbortReason::Disable,
-                       "GetElemSuper with profiling is not supported on x86");
-        }
-#endif
+        // x86 does not have enough registers.
+        return abort(AbortReason::Disable,
+                     "GetElemSuper is not supported on x86");
+#else
         MOZ_TRY(maybeInlineIC(opSnapshots, loc));
         break;
+#endif
       }
 
       case JSOp::Rest: {
         if (Shape* shape =
                 script_->global().maybeArrayShapeWithDefaultProto()) {
           if (!AddOpSnapshot<WarpRest>(alloc_, opSnapshots, offset, shape)) {
             return abort(AbortReason::Alloc);
           }
--- a/js/src/jit/arm/Architecture-arm.h
+++ b/js/src/jit/arm/Architecture-arm.h
@@ -50,26 +50,25 @@ static const uint32_t BAILOUT_TABLE_ENTR
 
 class Registers {
  public:
   enum RegisterID {
     r0 = 0,
     r1,
     r2,
     r3,
-    S0 = r3,
     r4,
     r5,
     r6,
     r7,
     r8,
-    S1 = r8,
     r9,
     r10,
     r11,
+    fp = r11,
     r12,
     ip = r12,
     r13,
     sp = r13,
     r14,
     lr = r14,
     r15,
     pc = r15,
@@ -128,17 +127,17 @@ class Registers {
   static const SetType WrapperMask = VolatileMask |          // = arguments
                                      (1 << Registers::r4) |  // = outReg
                                      (1 << Registers::r5);   // = argBase
 
   static const SetType SingleByteRegs = VolatileMask | NonVolatileMask;
 
   static const SetType NonAllocatableMask =
       (1 << Registers::sp) | (1 << Registers::r12) |  // r12 = ip = scratch
-      (1 << Registers::lr) | (1 << Registers::pc);
+      (1 << Registers::lr) | (1 << Registers::pc) | (1 << Registers::fp);
 
   // Registers returned from a JS -> JS call.
   static const SetType JSCallMask = (1 << Registers::r2) | (1 << Registers::r3);
 
   // Registers returned from a JS -> C call.
   static const SetType CallMask =
       (1 << Registers::r0) |
       (1 << Registers::r1);  // Used for double-size returns.
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -214,19 +214,19 @@ void JitRuntime::generateEnterJIT(JSCont
   masm.transferReg(r9);   // [sp',8]  = callee token
   masm.transferReg(r10);  // [sp',12]  = actual arguments
   masm.finishDataTransfer();
 
   Label returnLabel;
   {
     // Handle Interpreter -> Baseline OSR.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    MOZ_ASSERT(!regs.has(r11));
     regs.take(JSReturnOperand);
     regs.takeUnchecked(OsrFrameReg);
-    regs.take(r11);
     regs.take(ReturnReg);
 
     const Address slot_numStackValues(r11,
                                       offsetof(EnterJITStack, numStackValues));
 
     Label notOsr;
     masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, &notOsr);
 
--- a/js/src/jit/arm64/Architecture-arm64.h
+++ b/js/src/jit/arm64/Architecture-arm64.h
@@ -198,17 +198,18 @@ class Registers {
       (1 << Registers::x28) | (1 << Registers::x29) | (1 << Registers::x30);
 
   static const SetType SingleByteRegs = VolatileMask | NonVolatileMask;
 
   static const SetType NonAllocatableMask =
       (1 << Registers::x28) |  // PseudoStackPointer.
       (1 << Registers::ip0) |  // First scratch register.
       (1 << Registers::ip1) |  // Second scratch register.
-      (1 << Registers::tls) | (1 << Registers::lr) | (1 << Registers::sp);
+      (1 << Registers::tls) | (1 << Registers::lr) | (1 << Registers::sp) |
+      (1 << Registers::fp);
 
   static const SetType WrapperMask = VolatileMask;
 
   // Registers returned from a JS -> JS call.
   static const SetType JSCallMask = (1 << Registers::x2);
 
   // Registers returned from a JS -> C call.
   static const SetType CallMask = (1 << Registers::x0);
--- a/js/src/jit/loong64/Architecture-loong64.h
+++ b/js/src/jit/loong64/Architecture-loong64.h
@@ -183,17 +183,18 @@ class Registers {
 
   static const SetType SingleByteRegs = VolatileMask | NonVolatileMask;
 
   static const SetType NonAllocatableMask =
       (1 << Registers::zero) |  // Always be zero.
       (1 << Registers::t7) |    // First scratch register.
       (1 << Registers::t8) |    // Second scratch register.
       (1 << Registers::rx) |    // Reserved Register.
-      (1 << Registers::ra) | (1 << Registers::tp) | (1 << Registers::sp);
+      (1 << Registers::ra) | (1 << Registers::tp) | (1 << Registers::sp) |
+      (1 << Registers::fp);
 
   static const SetType WrapperMask = VolatileMask;
 
   // Registers returned from a JS -> JS call.
   static const SetType JSCallMask = (1 << Registers::a2);
 
   // Registers returned from a JS -> C call.
   static const SetType CallMask = (1 << Registers::a0);
--- a/js/src/jit/mips-shared/Architecture-mips-shared.h
+++ b/js/src/jit/mips-shared/Architecture-mips-shared.h
@@ -179,17 +179,17 @@ class Registers {
                                      (1 << Registers::t0) |  // = outReg
                                      (1 << Registers::t1);   // = argBase
 
   static const SetType NonAllocatableMask =
       (1 << Registers::zero) | (1 << Registers::at) |  // at = scratch
       (1 << Registers::t8) |                           // t8 = scratch
       (1 << Registers::t9) |                           // t9 = scratch
       (1 << Registers::k0) | (1 << Registers::k1) | (1 << Registers::gp) |
-      (1 << Registers::sp) | (1 << Registers::ra);
+      (1 << Registers::sp) | (1 << Registers::ra) | (1 << Registers::fp);
 
   // Registers returned from a JS -> JS call.
   static const SetType JSCallMask;
 
   // Registers returned from a JS -> C call.
   static const SetType SharedCallMask = (1 << Registers::v0);
   static const SetType CallMask;
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3604,53 +3604,49 @@ class LBigIntAsUintN32 : public LInstruc
   }
 
   const LAllocation* input() { return getOperand(0); }
   const LDefinition* temp() { return getTemp(0); }
   LInt64Definition temp64() { return getInt64Temp(1); }
 };
 
 template <size_t NumDefs>
-class LIonToWasmCallBase : public LVariadicInstruction<NumDefs, 2> {
-  using Base = LVariadicInstruction<NumDefs, 2>;
+class LIonToWasmCallBase : public LVariadicInstruction<NumDefs, 1> {
+  using Base = LVariadicInstruction<NumDefs, 1>;
 
  public:
   explicit LIonToWasmCallBase(LNode::Opcode classOpcode, uint32_t numOperands,
-                              const LDefinition& temp, const LDefinition& fp)
+                              const LDefinition& temp)
       : Base(classOpcode, numOperands) {
     this->setIsCall();
     this->setTemp(0, temp);
-    this->setTemp(1, fp);
   }
   MIonToWasmCall* mir() const { return this->mir_->toIonToWasmCall(); }
   const LDefinition* temp() { return this->getTemp(0); }
 };
 
 class LIonToWasmCall : public LIonToWasmCallBase<1> {
  public:
   LIR_HEADER(IonToWasmCall);
-  LIonToWasmCall(uint32_t numOperands, const LDefinition& temp,
-                 const LDefinition& fp)
-      : LIonToWasmCallBase<1>(classOpcode, numOperands, temp, fp) {}
+  LIonToWasmCall(uint32_t numOperands, const LDefinition& temp)
+      : LIonToWasmCallBase<1>(classOpcode, numOperands, temp) {}
 };
 
 class LIonToWasmCallV : public LIonToWasmCallBase<BOX_PIECES> {
  public:
   LIR_HEADER(IonToWasmCallV);
-  LIonToWasmCallV(uint32_t numOperands, const LDefinition& temp,
-                  const LDefinition& fp)
-      : LIonToWasmCallBase<BOX_PIECES>(classOpcode, numOperands, temp, fp) {}
+  LIonToWasmCallV(uint32_t numOperands, const LDefinition& temp)
+      : LIonToWasmCallBase<BOX_PIECES>(classOpcode, numOperands, temp) {}
 };
 
 class LIonToWasmCallI64 : public LIonToWasmCallBase<INT64_PIECES> {
  public:
   LIR_HEADER(IonToWasmCallI64);
-  LIonToWasmCallI64(uint32_t numOperands, const LDefinition& temp,
-                    const LDefinition& fp)
-      : LIonToWasmCallBase<INT64_PIECES>(classOpcode, numOperands, temp, fp) {}
+  LIonToWasmCallI64(uint32_t numOperands, const LDefinition& temp)
+      : LIonToWasmCallBase<INT64_PIECES>(classOpcode, numOperands, temp) {}
 };
 // Wasm SIMD.
 
 // (v128, v128, v128) -> v128 effect-free operation.
 // temp is FPR.
 class LWasmTernarySimd128 : public LInstructionHelper<1, 3, 1> {
   wasm::SimdOp op_;
 
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -214,18 +214,18 @@ void JitRuntime::generateEnterJIT(JSCont
   // Push the descriptor.
   masm.push(r14);
 
   CodeLabel returnLabel;
   CodeLabel oomReturnLabel;
   {
     // Handle Interpreter -> Baseline OSR.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    MOZ_ASSERT(!regs.has(rbp));
     regs.takeUnchecked(OsrFrameReg);
-    regs.take(rbp);
     regs.take(reg_code);
 
     // Ensure that |scratch| does not end up being JSReturnOperand.
     // Do takeUnchecked because on Win64/x64, reg_code (IntArgReg0) and
     // JSReturnOperand are the same (rcx).  See bug 849398.
     regs.takeUnchecked(JSReturnOperand);
     Register scratch = regs.takeAny();
 
--- a/js/src/jit/x86-shared/Architecture-x86-shared.h
+++ b/js/src/jit/x86-shared/Architecture-x86-shared.h
@@ -110,17 +110,18 @@ class Registers {
                                       (1 << X86Encoding::rdx);
 
   static const SetType WrapperMask = VolatileMask | (1 << X86Encoding::rbx);
 
   static const SetType SingleByteRegs =
       (1 << X86Encoding::rax) | (1 << X86Encoding::rcx) |
       (1 << X86Encoding::rdx) | (1 << X86Encoding::rbx);
 
-  static const SetType NonAllocatableMask = (1 << X86Encoding::rsp);
+  static const SetType NonAllocatableMask =
+      (1 << X86Encoding::rsp) | (1 << X86Encoding::rbp);
 
   // Registers returned from a JS -> JS call.
   static const SetType JSCallMask =
       (1 << X86Encoding::rcx) | (1 << X86Encoding::rdx);
 
   // Registers returned from a JS -> C call.
   static const SetType CallMask = (1 << X86Encoding::rax);
 
@@ -136,17 +137,18 @@ class Registers {
                                       (1 << X86Encoding::r10) |
                                       (1 << X86Encoding::r11);
 
   static const SetType WrapperMask = VolatileMask;
 
   static const SetType SingleByteRegs = AllMask & ~(1 << X86Encoding::rsp);
 
   static const SetType NonAllocatableMask =
-      (1 << X86Encoding::rsp) | (1 << X86Encoding::r11);  // This is ScratchReg.
+      (1 << X86Encoding::rsp) | (1 << X86Encoding::rbp) |
+      (1 << X86Encoding::r11);  // This is ScratchReg.
 
   // Registers returned from a JS -> JS call.
   static const SetType JSCallMask = (1 << X86Encoding::rcx);
 
   // Registers returned from a JS -> C call.
   static const SetType CallMask = (1 << X86Encoding::rax);
 
 #endif
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -163,19 +163,19 @@ void JitRuntime::generateEnterJIT(JSCont
   // Push the descriptor.
   masm.push(esi);
 
   CodeLabel returnLabel;
   CodeLabel oomReturnLabel;
   {
     // Handle Interpreter -> Baseline OSR.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    MOZ_ASSERT(!regs.has(ebp));
     regs.take(JSReturnOperand);
     regs.takeUnchecked(OsrFrameReg);
-    regs.take(ebp);
     regs.take(ReturnReg);
 
     Register scratch = regs.takeAny();
 
     Label notOsr;
     masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, &notOsr);
 
     Register numStackValues = regs.takeAny();
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -1439,36 +1439,24 @@ static bool GenerateJitEntry(MacroAssemb
   GenerateJitEntryThrow(masm, frameSize);
 
   return FinishOffsets(masm, offsets);
 }
 
 void wasm::GenerateDirectCallFromJit(MacroAssembler& masm, const FuncExport& fe,
                                      const Instance& inst,
                                      const JitCallStackArgVector& stackArgs,
-                                     bool profilingEnabled, Register scratch,
-                                     uint32_t* callOffset) {
+                                     Register scratch, uint32_t* callOffset) {
   MOZ_ASSERT(!IsCompilingWasm());
 
   size_t framePushedAtStart = masm.framePushed();
 
-  if (profilingEnabled) {
-    // FramePointer isn't volatile, manually preserve it because it will be
-    // clobbered below.
-    masm.Push(FramePointer);
-  } else {
-#ifdef DEBUG
-    // Ensure that the FramePointer is actually Ion-volatile. This might
-    // assert when bug 1426134 lands.
-    AllocatableRegisterSet set(RegisterSet::All());
-    TakeJitRegisters(/* profiling */ false, &set);
-    MOZ_ASSERT(set.has(FramePointer),
-               "replace the whole if branch by the then body when this fails");
-#endif
-  }
+  // FramePointer isn't volatile, manually preserve it because it will be
+  // clobbered below.
+  masm.Push(FramePointer);
 
   // Note, if code here pushes a reference value into the frame for its own
   // purposes (and not just as an argument to the callee) then the frame must be
   // traced in TraceJitExitFrame, see the case there for DirectWasmJitCall.  The
   // callee will trace values that are pushed as arguments, however.
 
   // Push a special frame descriptor that indicates the frame size so we can
   // directly iterate from the current JIT frame without an extra call.
@@ -1674,20 +1662,18 @@ void wasm::GenerateDirectCallFromJit(Mac
     }
   }
 
   GenPrintf(DebugChannel::Function, masm, "\n");
 
   // Free args + frame descriptor.
   masm.leaveExitFrame(bytesNeeded + ExitFrameLayout::Size());
 
-  // If we pushed it, free FramePointer.
-  if (profilingEnabled) {
-    masm.Pop(FramePointer);
-  }
+  // Free FramePointer.
+  masm.Pop(FramePointer);
 
   MOZ_ASSERT(framePushedAtStart == masm.framePushed());
 }
 
 static void StackCopy(MacroAssembler& masm, MIRType type, Register scratch,
                       Address src, Address dst) {
   if (type == MIRType::Int32) {
     masm.load32(src, scratch);
--- a/js/src/wasm/WasmStubs.h
+++ b/js/src/wasm/WasmStubs.h
@@ -352,17 +352,19 @@ using JitCallStackArgVector = Vector<Jit
 // inlined exit frame.
 // Assumes:
 // - all the registers have been preserved by the caller,
 // - all arguments passed in registers have been set up at the expected
 //   locations,
 // - all arguments passed on stack slot are alive as defined by a corresponding
 //   JitCallStackArg.
 
-extern void GenerateDirectCallFromJit(
-    jit::MacroAssembler& masm, const FuncExport& fe, const Instance& inst,
-    const JitCallStackArgVector& stackArgs, bool profilingEnabled,
-    jit::Register scratch, uint32_t* callOffset);
+extern void GenerateDirectCallFromJit(jit::MacroAssembler& masm,
+                                      const FuncExport& fe,
+                                      const Instance& inst,
+                                      const JitCallStackArgVector& stackArgs,
+                                      jit::Register scratch,
+                                      uint32_t* callOffset);
 
 }  // namespace wasm
 }  // namespace js
 
 #endif  // wasm_stubs_h
--- a/layout/generic/ReflowOutput.cpp
+++ b/layout/generic/ReflowOutput.cpp
@@ -7,33 +7,41 @@
 /* struct containing the output from nsIFrame::Reflow */
 
 #include "mozilla/ReflowOutput.h"
 #include "mozilla/ReflowInput.h"
 
 namespace mozilla {
 
 /* static */
-void OverflowAreas::ApplyOverflowClippingOnRect(nsRect& aOverflowRect,
-                                                const nsRect& aBounds,
-                                                PhysicalAxes aClipAxes,
-                                                const nsSize& aOverflowMargin) {
+nsRect OverflowAreas::GetOverflowClipRect(const nsRect& aRectToClip,
+                                          const nsRect& aBounds,
+                                          PhysicalAxes aClipAxes,
+                                          const nsSize& aOverflowMargin) {
   auto inflatedBounds = aBounds;
   inflatedBounds.Inflate(aOverflowMargin);
-
-  auto clip = aOverflowRect;
+  auto clip = aRectToClip;
   if (aClipAxes & PhysicalAxes::Vertical) {
     clip.y = inflatedBounds.y;
     clip.height = inflatedBounds.height;
   }
   if (aClipAxes & PhysicalAxes::Horizontal) {
     clip.x = inflatedBounds.x;
     clip.width = inflatedBounds.width;
   }
-  aOverflowRect = aOverflowRect.Intersect(clip);
+  return clip;
+}
+
+/* static */
+void OverflowAreas::ApplyOverflowClippingOnRect(nsRect& aOverflowRect,
+                                                const nsRect& aBounds,
+                                                PhysicalAxes aClipAxes,
+                                                const nsSize& aOverflowMargin) {
+  aOverflowRect = aOverflowRect.Intersect(
+      GetOverflowClipRect(aOverflowRect, aBounds, aClipAxes, aOverflowMargin));
 }
 
 void OverflowAreas::UnionWith(const OverflowAreas& aOther) {
   InkOverflow().UnionRect(InkOverflow(), aOther.InkOverflow());
   ScrollableOverflow().UnionRect(ScrollableOverflow(),
                                  aOther.ScrollableOverflow());
 }
 
--- a/layout/generic/ReflowOutput.h
+++ b/layout/generic/ReflowOutput.h
@@ -84,16 +84,23 @@ struct OverflowAreas {
   void ApplyClipping(const nsRect& aBounds, PhysicalAxes aClipAxes,
                      const nsSize& aOverflowMargin) {
     ApplyOverflowClippingOnRect(InkOverflow(), aBounds, aClipAxes,
                                 aOverflowMargin);
     ApplyOverflowClippingOnRect(ScrollableOverflow(), aBounds, aClipAxes,
                                 aOverflowMargin);
   }
 
+  // Gets the overflow clipping rect for a given element given a rect to clip,
+  // the frame bounds, a set of axes, and the overflow margin.
+  static nsRect GetOverflowClipRect(const nsRect& aRectToClip,
+                                    const nsRect& aBounds,
+                                    PhysicalAxes aClipAxes,
+                                    const nsSize& aOverflowMargin);
+
   // Applies the overflow clipping to a given overflow rect, given the frame
   // bounds, and the physical axes on which to apply the overflow clip.
   static void ApplyOverflowClippingOnRect(nsRect& aOverflowRect,
                                           const nsRect& aBounds,
                                           PhysicalAxes aClipAxes,
                                           const nsSize& aOverflowMargin);
 
  private:
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/AccessibilityTest.kt
@@ -1384,19 +1384,16 @@ class AccessibilityTest : BaseSessionTes
                 nodeId = getSourceId(event)
                 val node = createNodeInfo(nodeId)
                 assertThat("Label has text", node.text.toString(), equalTo("Some stuff "))
             }
         })
     }
 
     @Test fun testRemoteAccessibilityFocusIframe() {
-        // TODO: Bug 1758540
-        assumeThat(sessionRule.env.isFission, equalTo(false))
-
         testAccessibilityFocusIframe(REMOTE_IFRAME);
     }
 
     @Test fun testLocalAccessibilityFocusIframe() {
         testAccessibilityFocusIframe(LOCAL_IFRAME);
     }
 
     private fun testIframeTree(page: String) {
@@ -1433,19 +1430,16 @@ class AccessibilityTest : BaseSessionTes
         var nodeBounds = Rect()
         node.getBoundsInScreen(nodeBounds)
         assertThat("inner node in inner doc bounds", innerDocBounds.contains(nodeBounds), equalTo(true))
 
     }
 
     @Setting(key = Setting.Key.FULL_ACCESSIBILITY_TREE, value = "true")
     @Test fun testRemoteIframeTree() {
-        // TODO: Bug 1758540
-        assumeThat(sessionRule.env.isFission, equalTo(false))
-
         testIframeTree(REMOTE_IFRAME);
     }
 
     @Setting(key = Setting.Key.FULL_ACCESSIBILITY_TREE, value = "true")
     @Test fun testLocalIframeTree() {
         testIframeTree(LOCAL_IFRAME);
     }
 
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -216,17 +216,21 @@
   value: 1
 #endif
   mirror: always
 
 # Whether to cache the entire accessibility trees of all content processes in
 # the parent process.
 - name: accessibility.cache.enabled
   type: bool
-  value: false
+#ifdef ANDROID
+  value: true
+#else
+  value: false
+#endif
   mirror: once
 
 #---------------------------------------------------------------------------
 # Prefs starting with "alerts."
 #---------------------------------------------------------------------------
 
 # Whether to use platform-specific backends for showing desktop notifications.
 # If no such backend is available, or if the pref is false, then XUL
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -72,32 +72,24 @@ pref("security.pki.mitm_canary_issuer.en
 pref("security.pki.mitm_detected", false);
 
 // Intermediate CA Preloading settings
 #if !defined(MOZ_WIDGET_ANDROID)
   pref("security.remote_settings.intermediates.enabled", true);
 #else
   pref("security.remote_settings.intermediates.enabled", false);
 #endif
-pref("security.remote_settings.intermediates.bucket", "security-state");
-pref("security.remote_settings.intermediates.collection", "intermediates");
-pref("security.remote_settings.intermediates.checked", 0);
 pref("security.remote_settings.intermediates.downloads_per_poll", 5000);
 pref("security.remote_settings.intermediates.parallel_downloads", 8);
-pref("security.remote_settings.intermediates.signer", "onecrl.content-signature.mozilla.org");
 
 #if defined(EARLY_BETA_OR_EARLIER) && !defined(MOZ_WIDGET_ANDROID)
   pref("security.remote_settings.crlite_filters.enabled", true);
 #else
   pref("security.remote_settings.crlite_filters.enabled", false);
 #endif
-pref("security.remote_settings.crlite_filters.bucket", "security-state");
-pref("security.remote_settings.crlite_filters.collection", "cert-revocations");
-pref("security.remote_settings.crlite_filters.checked", 0);
-pref("security.remote_settings.crlite_filters.signer", "onecrl.content-signature.mozilla.org");
 
 pref("security.osreauthenticator.blank_password", false);
 pref("security.osreauthenticator.password_last_changed_lo", 0);
 pref("security.osreauthenticator.password_last_changed_hi", 0);
 
 pref("security.crash_tracking.js_load_1.prevCrashes", 0);
 pref("security.crash_tracking.js_load_1.maxCrashes", 0);
 
@@ -2058,30 +2050,22 @@ pref("security.notification_enable_delay
 #endif
 
 // Insecure Form Field Warning
 pref("security.insecure_field_warning.contextual.enabled", false);
 pref("security.insecure_field_warning.ignore_local_ip_address", true);
 
 // Remote settings preferences
 pref("services.settings.poll_interval", 86400); // 24H
-pref("services.settings.server", "https://firefox.settings.services.mozilla.com/v1");
-pref("services.settings.default_bucket", "main");
 
 // The percentage of clients who will report uptake telemetry as
 // events instead of just a histogram. This only applies on Release;
 // other channels always report events.
 pref("services.common.uptake.sampleRate", 1);   // 1%
 
-// Security state OneCRL.
-pref("services.settings.security.onecrl.bucket", "security-state");
-pref("services.settings.security.onecrl.collection", "onecrl");
-pref("services.settings.security.onecrl.signer", "onecrl.content-signature.mozilla.org");
-pref("services.settings.security.onecrl.checked", 0);
-
 pref("extensions.abuseReport.enabled", true);
 // Allow AMO to handoff reports to the Firefox integrated dialog.
 pref("extensions.abuseReport.amWebAPI.enabled", true);
 pref("extensions.abuseReport.url", "https://services.addons.mozilla.org/api/v4/abuse/report/addon/");
 pref("extensions.abuseReport.amoDetailsURL", "https://services.addons.mozilla.org/api/v4/addons/addon/");
 
 // Blocklist preferences
 pref("extensions.blocklist.enabled", true);
@@ -2091,29 +2075,16 @@ pref("extensions.blocklist.addonItemURL"
 // Controls what level the blocklist switches from warning about items to forcibly
 // blocking them.
 pref("extensions.blocklist.level", 2);
 // Whether event pages should be enabled for "manifest_version: 2" extensions.
 pref("extensions.eventPages.enabled", false);
 // Whether "manifest_version: 3" extensions should be allowed to install successfully.
 pref("extensions.manifestV3.enabled", false);
 
-// Blocklist via settings server (Kinto)
-pref("services.blocklist.bucket", "blocklists");
-pref("services.blocklist.addons.collection", "addons");
-pref("services.blocklist.addons.checked", 0);
-pref("services.blocklist.addons.signer", "remote-settings.content-signature.mozilla.org");
-pref("services.blocklist.addons-mlbf.checked", 0);
-pref("services.blocklist.plugins.collection", "plugins");
-pref("services.blocklist.plugins.checked", 0);
-pref("services.blocklist.plugins.signer", "remote-settings.content-signature.mozilla.org");
-pref("services.blocklist.gfx.collection", "gfx");
-pref("services.blocklist.gfx.checked", 0);
-pref("services.blocklist.gfx.signer", "remote-settings.content-signature.mozilla.org");
-
 // Modifier key prefs: default to Windows settings,
 // menu access key = alt, accelerator key = control.
 // Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js
 pref("ui.key.accelKey", 17);
 pref("ui.key.menuAccessKey", 18);
 
 // Middle-mouse handling
 pref("middlemouse.paste", false);
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -5735,16 +5735,22 @@ NS_IMETHODIMP
 HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch(bool* aIsMismatch) {
   // This should only be called in parent process.
   MOZ_ASSERT(XRE_IsParentProcess());
   *aIsMismatch = LoadHasCrossOriginOpenerPolicyMismatch();
   return NS_OK;
 }
 
 void HttpBaseChannel::MaybeFlushConsoleReports() {
+  // Flush if we have a known window ID.
+  if (mLoadInfo->GetInnerWindowID() > 0) {
+    FlushReportsToConsole(mLoadInfo->GetInnerWindowID());
+    return;
+  }
+
   // If this channel is part of a loadGroup, we can flush the console reports
   // immediately.
   nsCOMPtr<nsILoadGroup> loadGroup;
   nsresult rv = GetLoadGroup(getter_AddRefs(loadGroup));
   if (NS_SUCCEEDED(rv) && loadGroup) {
     FlushConsoleReports(loadGroup);
   }
 }
--- a/security/manager/ssl/RemoteSecuritySettings.jsm
+++ b/security/manager/ssl/RemoteSecuritySettings.jsm
@@ -10,47 +10,29 @@ const { RemoteSettings } = ChromeUtils.i
 );
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 const { X509 } = ChromeUtils.import("resource://gre/modules/psm/X509.jsm");
 
-const INTERMEDIATES_BUCKET_PREF =
-  "security.remote_settings.intermediates.bucket";
-const INTERMEDIATES_CHECKED_SECONDS_PREF =
-  "security.remote_settings.intermediates.checked";
-const INTERMEDIATES_COLLECTION_PREF =
-  "security.remote_settings.intermediates.collection";
+const SECURITY_STATE_BUCKET = "security-state";
+const SECURITY_STATE_SIGNER = "onecrl.content-signature.mozilla.org";
+
 const INTERMEDIATES_DL_PER_POLL_PREF =
   "security.remote_settings.intermediates.downloads_per_poll";
 const INTERMEDIATES_DL_PARALLEL_REQUESTS =
   "security.remote_settings.intermediates.parallel_downloads";
 const INTERMEDIATES_ENABLED_PREF =
   "security.remote_settings.intermediates.enabled";
-const INTERMEDIATES_SIGNER_PREF =
-  "security.remote_settings.intermediates.signer";
 const LOGLEVEL_PREF = "browser.policies.loglevel";
 
-const ONECRL_BUCKET_PREF = "services.settings.security.onecrl.bucket";
-const ONECRL_COLLECTION_PREF = "services.settings.security.onecrl.collection";
-const ONECRL_SIGNER_PREF = "services.settings.security.onecrl.signer";
-const ONECRL_CHECKED_PREF = "services.settings.security.onecrl.checked";
-
-const CRLITE_FILTERS_BUCKET_PREF =
-  "security.remote_settings.crlite_filters.bucket";
-const CRLITE_FILTERS_CHECKED_SECONDS_PREF =
-  "security.remote_settings.crlite_filters.checked";
-const CRLITE_FILTERS_COLLECTION_PREF =
-  "security.remote_settings.crlite_filters.collection";
 const CRLITE_FILTERS_ENABLED_PREF =
   "security.remote_settings.crlite_filters.enabled";
-const CRLITE_FILTERS_SIGNER_PREF =
-  "security.remote_settings.crlite_filters.signer";
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 XPCOMUtils.defineLazyGetter(this, "gTextDecoder", () => new TextDecoder());
 
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
   return new ConsoleAPI({
@@ -239,24 +221,20 @@ const updateCertBlocklist = async functi
 var RemoteSecuritySettings = {
   /**
    * Initialize the clients (cheap instantiation) and setup their sync event.
    * This static method is called from BrowserGlue.jsm soon after startup.
    *
    * @returns {Object} intantiated clients for security remote settings.
    */
   init() {
-    const OneCRLBlocklistClient = RemoteSettings(
-      Services.prefs.getCharPref(ONECRL_COLLECTION_PREF),
-      {
-        bucketNamePref: ONECRL_BUCKET_PREF,
-        lastCheckTimePref: ONECRL_CHECKED_PREF,
-        signerName: Services.prefs.getCharPref(ONECRL_SIGNER_PREF),
-      }
-    );
+    const OneCRLBlocklistClient = RemoteSettings("onecrl", {
+      bucketName: SECURITY_STATE_BUCKET,
+      signerName: SECURITY_STATE_SIGNER,
+    });
     OneCRLBlocklistClient.on("sync", updateCertBlocklist);
 
     let IntermediatePreloadsClient = new IntermediatePreloads();
     let CRLiteFiltersClient = new CRLiteFilters();
 
     this.OneCRLBlocklistClient = OneCRLBlocklistClient;
     this.IntermediatePreloadsClient = IntermediatePreloadsClient;
     this.CRLiteFiltersClient = CRLiteFiltersClient;
@@ -266,25 +244,21 @@ var RemoteSecuritySettings = {
       IntermediatePreloadsClient,
       CRLiteFiltersClient,
     };
   },
 };
 
 class IntermediatePreloads {
   constructor() {
-    this.client = RemoteSettings(
-      Services.prefs.getCharPref(INTERMEDIATES_COLLECTION_PREF),
-      {
-        bucketNamePref: INTERMEDIATES_BUCKET_PREF,
-        lastCheckTimePref: INTERMEDIATES_CHECKED_SECONDS_PREF,
-        signerName: Services.prefs.getCharPref(INTERMEDIATES_SIGNER_PREF),
-        localFields: ["cert_import_complete"],
-      }
-    );
+    this.client = RemoteSettings("intermediates", {
+      bucketName: SECURITY_STATE_BUCKET,
+      signerName: SECURITY_STATE_SIGNER,
+      localFields: ["cert_import_complete"],
+    });
 
     this.client.on("sync", this.onSync.bind(this));
     Services.obs.addObserver(
       this.onObservePollEnd.bind(this),
       "remote-settings:changes-poll-end"
     );
 
     log.debug("Intermediate Preloading: constructor");
@@ -502,25 +476,21 @@ class IntermediatePreloads {
 // Helper function to compare filters. One filter is "less than" another filter (i.e. it sorts
 // earlier) if its timestamp is farther in the past than the other.
 function compareFilters(filterA, filterB) {
   return filterA.effectiveTimestamp - filterB.effectiveTimestamp;
 }
 
 class CRLiteFilters {
   constructor() {
-    this.client = RemoteSettings(
-      Services.prefs.getCharPref(CRLITE_FILTERS_COLLECTION_PREF),
-      {
-        bucketNamePref: CRLITE_FILTERS_BUCKET_PREF,
-        lastCheckTimePref: CRLITE_FILTERS_CHECKED_SECONDS_PREF,
-        signerName: Services.prefs.getCharPref(CRLITE_FILTERS_SIGNER_PREF),
-        localFields: ["loaded_into_cert_storage"],
-      }
-    );
+    this.client = RemoteSettings("cert-revocations", {
+      bucketName: SECURITY_STATE_BUCKET,
+      signerName: SECURITY_STATE_SIGNER,
+      localFields: ["loaded_into_cert_storage"],
+    });
 
     Services.obs.addObserver(
       this.onObservePollEnd.bind(this),
       "remote-settings:changes-poll-end"
     );
   }
 
   async onObservePollEnd(subject, topic, data) {
--- a/services/settings/RemoteSettingsClient.jsm
+++ b/services/settings/RemoteSettingsClient.jsm
@@ -25,23 +25,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   UptakeTelemetry: "resource://services-common/uptake-telemetry.js",
   Utils: "resource://services-settings/Utils.jsm",
 });
 
 const TELEMETRY_COMPONENT = "remotesettings";
 
 XPCOMUtils.defineLazyGetter(this, "console", () => Utils.log);
 
-XPCOMUtils.defineLazyPreferenceGetter(
-  this,
-  "gLoadDump",
-  "services.settings.load_dump",
-  true
-);
-
 /**
  * cacheProxy returns an object Proxy that will memoize properties of the target.
  * @param {Object} target the object to wrap.
  * @returns {Proxy}
  */
 function cacheProxy(target) {
   const cache = new Map();
   return new Proxy(target, {
@@ -232,68 +225,64 @@ class RemoteSettingsClient extends Event
   }
   static get UnknownCollectionError() {
     return UnknownCollectionError;
   }
 
   constructor(
     collectionName,
     {
-      bucketName,
-      bucketNamePref,
+      bucketName = AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET,
       signerName,
       filterFunc,
       localFields = [],
       lastCheckTimePref,
-    }
+    } = {}
   ) {
     super(["sync"]); // emitted events
 
     this.collectionName = collectionName;
-    this.bucketName = bucketName;
+    // Client is constructed with the raw bucket name (eg. "main", "security-state", "blocklist")
+    // The `bucketName` will contain the `-preview` suffix if the preview mode is enabled.
+    this.bucketName = Utils.actualBucketName(bucketName);
     this.signerName = signerName;
     this.filterFunc = filterFunc;
     this.localFields = localFields;
     this._lastCheckTimePref = lastCheckTimePref;
     this._verifier = null;
     this._syncRunning = false;
 
     // This attribute allows signature verification to be disabled, when running tests
     // or when pulling data from a dev server.
     this.verifySignature = AppConstants.REMOTE_SETTINGS_VERIFY_SIGNATURE;
 
-    if (!bucketName) {
-      // TODO bug 1702759: Remove bucketNamePref.
-      // The bucket preference value can be changed (eg. `main` to `main-preview`) in order
-      // to preview the changes to be approved in a real client.
-      this.bucketNamePref = bucketNamePref;
-      XPCOMUtils.defineLazyPreferenceGetter(
-        this,
-        "bucketName",
-        this.bucketNamePref,
-        null,
-        () => {
-          this.db.identifier = this.identifier;
-        }
-      );
-    }
-
     XPCOMUtils.defineLazyGetter(
       this,
       "db",
       () => new Database(this.identifier)
     );
 
     XPCOMUtils.defineLazyGetter(
       this,
       "attachments",
       () => new AttachmentDownloader(this)
     );
   }
 
+  /**
+   * Internal method to refresh the client bucket name after the preview mode
+   * was toggled.
+   *
+   * See `RemoteSettings.enabledPreviewMode()`.
+   */
+  refreshBucketName() {
+    this.bucketName = Utils.actualBucketName(this.bucketName);
+    this.db.identifier = this.identifier;
+  }
+
   get identifier() {
     return `${this.bucketName}/${this.collectionName}`;
   }
 
   get lastCheckTimePref() {
     return (
       this._lastCheckTimePref ||
       `services.settings.${this.bucketName}.${this.collectionName}.last_check`
@@ -358,17 +347,17 @@ class RemoteSettingsClient extends Event
       let hasLocalData = lastModified !== null;
 
       if (syncIfEmpty && !hasLocalData) {
         // .get() was called before we had the chance to synchronize the local database.
         // We'll try to avoid returning an empty list.
         if (!this._importingPromise) {
           // Prevent parallel loading when .get() is called multiple times.
           this._importingPromise = (async () => {
-            const importedFromDump = gLoadDump
+            const importedFromDump = Utils.LOAD_DUMPS
               ? await this._importJSONDump()
               : -1;
             if (importedFromDump < 0) {
               // There is no JSON dump to load, force a synchronization from the server.
               // We don't want the "sync" event to be sent, since some consumers use `.get()`
               // in "sync" callbacks. See Bug 1761953
               console.debug(
                 `${this.identifier} Local DB is empty, pull data from server`
@@ -520,26 +509,25 @@ class RemoteSettingsClient extends Event
 
   /**
    * Synchronize the local database with the remote server, **only if necessary**.
    *
    * @param {int}    expectedTimestamp  the lastModified date (on the server) for the remote collection.
    *                                    This will be compared to the local timestamp, and will be used for
    *                                    cache busting if local data is out of date.
    * @param {Object} options            additional advanced options.
-   * @param {bool}   options.loadDump   load initial dump from disk on first sync (default: true, unless
-   *                                    `services.settings.load_dump` says otherwise).
+   * @param {bool}   options.loadDump   load initial dump from disk on first sync (default: true if server is prod)
    * @param {bool}   options.sendEvents send `"sync"` events (default: `true`)
    * @param {string} options.trigger    label to identify what triggered this sync (eg. ``"timer"``, default: `"manual"`)
    * @return {Promise}                  which rejects on sync or process failure.
    */
   async maybeSync(expectedTimestamp, options = {}) {
     // Should the clients try to load JSON dump? (mainly disabled in tests)
     const {
-      loadDump = gLoadDump,
+      loadDump = Utils.LOAD_DUMPS,
       trigger = "manual",
       sendEvents = true,
     } = options;
 
     // Make sure we don't run several synchronizations in parallel, mainly
     // in order to avoid race conditions in "sync" events listeners.
     if (this._syncRunning) {
       console.warn(`${this.identifier} sync already running`);
--- a/services/settings/Utils.jsm
+++ b/services/settings/Utils.jsm
@@ -40,70 +40,135 @@ XPCOMUtils.defineLazyGetter(this, "log",
   );
   return new ConsoleAPI({
     maxLogLevel: "warn",
     maxLogLevelPref: "services.settings.loglevel",
     prefix: "services.settings",
   });
 });
 
+XPCOMUtils.defineLazyGetter(this, "isRunningTests", () => {
+  const env = Cc["@mozilla.org/process/environment;1"].getService(
+    Ci.nsIEnvironment
+  );
+  if (env.get("MOZ_DISABLE_NONLOCAL_CONNECTIONS") === "1") {
+    // Allow to override the server URL if non-local connections are disabled,
+    // usually true when running tests.
+    return true;
+  }
+  return false;
+});
+
 // Overriding the server URL is normally disabled on Beta and Release channels,
 // except under some conditions.
 XPCOMUtils.defineLazyGetter(this, "allowServerURLOverride", () => {
   if (!AppConstants.RELEASE_OR_BETA) {
     // Always allow to override the server URL on Nightly/DevEdition.
     return true;
   }
 
-  if (AppConstants.MOZ_APP_NAME === "thunderbird") {
-    // Always allow to override the server URL for Thunderbird.
+  if (isRunningTests) {
     return true;
   }
 
   const env = Cc["@mozilla.org/process/environment;1"].getService(
     Ci.nsIEnvironment
   );
-  if (env.get("MOZ_DISABLE_NONLOCAL_CONNECTIONS") === "1") {
-    // Allow to override the server URL if non-local connections are disabled,
-    // usually true when running tests.
-    return true;
-  }
 
   if (env.get("MOZ_REMOTE_SETTINGS_DEVTOOLS") === "1") {
     // Allow to override the server URL when using remote settings devtools.
     return true;
   }
 
   return false;
 });
 
 XPCOMUtils.defineLazyPreferenceGetter(
   this,
   "gServerURL",
-  "services.settings.server"
+  "services.settings.server",
+  AppConstants.REMOTE_SETTINGS_SERVER_URL
+);
+
+XPCOMUtils.defineLazyPreferenceGetter(
+  this,
+  "gPreviewEnabled",
+  "services.settings.preview_enabled",
+  false
 );
 
 function _isUndefined(value) {
   return typeof value === "undefined";
 }
 
 var Utils = {
   get SERVER_URL() {
     return allowServerURLOverride
       ? gServerURL
-      : "https://firefox.settings.services.mozilla.com/v1";
+      : AppConstants.REMOTE_SETTINGS_SERVER_URL;
   },
 
   CHANGES_PATH: "/buckets/monitor/collections/changes/changeset",
 
   /**
    * Logger instance.
    */
   log,
 
+  get LOAD_DUMPS() {
+    // Load dumps only if pulling data from the production server, or in tests.
+    return (
+      this.SERVER_URL == AppConstants.REMOTE_SETTINGS_SERVER_URL ||
+      isRunningTests
+    );
+  },
+
+  get PREVIEW_MODE() {
+    // We want to offer the ability to set preview mode via a preference
+    // for consumers who want to pull from the preview bucket on startup.
+    if (_isUndefined(this._previewModeEnabled) && allowServerURLOverride) {
+      return gPreviewEnabled;
+    }
+    return !!this._previewModeEnabled;
+  },
+
+  /**
+   * Internal method to enable pulling data from preview buckets.
+   * @param enabled
+   */
+  enablePreviewMode(enabled) {
+    const bool2str = v =>
+      // eslint-disable-next-line no-nested-ternary
+      _isUndefined(v) ? "unset" : v ? "enabled" : "disabled";
+    this.log.debug(
+      `Preview mode: ${bool2str(this._previewModeEnabled)} -> ${bool2str(
+        enabled
+      )}`
+    );
+    this._previewModeEnabled = enabled;
+  },
+
+  /**
+   * Returns the actual bucket name to be used. When preview mode is enabled,
+   * this adds the *preview* suffix.
+   *
+   * See also `SharedUtils.loadJSONDump()` which strips the preview suffix to identify
+   * the packaged JSON file.
+   *
+   * @param bucketName the client bucket
+   * @returns the final client bucket depending whether preview mode is enabled.
+   */
+  actualBucketName(bucketName) {
+    let actual = bucketName.replace("-preview", "");
+    if (this.PREVIEW_MODE) {
+      actual += "-preview";
+    }
+    return actual;
+  },
+
   /**
    * Check if network is down.
    *
    * Note that if this returns false, it does not guarantee
    * that network is up.
    *
    * @return {bool} Whether network is down or not.
    */
--- a/services/settings/docs/index.rst
+++ b/services/settings/docs/index.rst
@@ -378,16 +378,27 @@ In order to enable verbose logging, set 
 
     Services.prefs.setCharPref("services.settings.loglevel", "debug");
 
 Remote Settings Dev Tools
 -------------------------
 
 The Remote Settings Dev Tools extension provides some tooling to inspect synchronization statuses, to change the remote server or to switch to *preview* mode in order to sign-off pending changes. `More information on the dedicated repository <https://github.com/mozilla/remote-settings-devtools>`_.
 
+Preview Mode
+------------
+
+Enable the preview mode in order to preview changes to be reviewed on the server. This can be achieved using the *Remote Settings Dev Tools*, or programmatically with:
+
+.. code-block:: javascript
+
+    RemoteSettings.enablePreviewMode(true);
+
+In order to pull preview data **on startup**, or in order to persist it across restarts, set ``services.settings.preview_enabled`` to ``true`` in the profile preferences (ie. ``user.js``).
+For release and ESR, for security reasons, you would have to run the application with the ``MOZ_REMOTE_SETTINGS_DEVTOOLS=1`` environment variable for the preference to be taken into account. Note that toggling the preference won't have any effect until restart.
 
 Trigger a synchronization manually
 ----------------------------------
 
 The synchronization of every known remote settings clients can be triggered manually with ``pollChanges()``:
 
 .. code-block:: js
 
--- a/services/settings/remote-settings.js
+++ b/services/settings/remote-settings.js
@@ -13,31 +13,30 @@ var EXPORTED_SYMBOLS = [
 ];
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
 XPCOMUtils.defineLazyModuleGetters(this, {
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
   UptakeTelemetry: "resource://services-common/uptake-telemetry.js",
   pushBroadcastService: "resource://gre/modules/PushBroadcastService.jsm",
   RemoteSettingsClient: "resource://services-settings/RemoteSettingsClient.jsm",
   SyncHistory: "resource://services-settings/SyncHistory.jsm",
   Utils: "resource://services-settings/Utils.jsm",
   FilterExpressions:
     "resource://gre/modules/components-utils/FilterExpressions.jsm",
   RemoteSettingsWorker: "resource://services-settings/RemoteSettingsWorker.jsm",
 });
 
 XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
-const PREF_SETTINGS_DEFAULT_BUCKET = "services.settings.default_bucket";
 const PREF_SETTINGS_BRANCH = "services.settings.";
-const PREF_SETTINGS_DEFAULT_SIGNER = "default_signer";
 const PREF_SETTINGS_SERVER_BACKOFF = "server.backoff";
 const PREF_SETTINGS_LAST_UPDATE = "last_update_seconds";
 const PREF_SETTINGS_LAST_ETAG = "last_etag";
 const PREF_SETTINGS_CLOCK_SKEW_SECONDS = "clock_skew_seconds";
 const PREF_SETTINGS_SYNC_HISTORY_SIZE = "sync_history_size";
 const PREF_SETTINGS_SYNC_HISTORY_ERROR_THRESHOLD =
   "sync_history_error_threshold";
 
@@ -95,17 +94,16 @@ async function jexlFilterFunc(entry, env
 }
 
 function remoteSettingsFunction() {
   const _clients = new Map();
   let _invalidatePolling = false;
 
   // If not explicitly specified, use the default signer.
   const defaultOptions = {
-    bucketNamePref: PREF_SETTINGS_DEFAULT_BUCKET,
     signerName: DEFAULT_SIGNER,
     filterFunc: jexlFilterFunc,
   };
 
   /**
    * RemoteSettings constructor.
    *
    * @param {String} collectionName The remote settings identifier
@@ -141,28 +139,29 @@ function remoteSettingsFunction() {
     if (client && client.bucketName == bucketName) {
       return client;
     }
     // There was no client registered for this collection, but it's the main bucket,
     // therefore we can instantiate a client with the default options.
     // So if we have a local database or if we ship a JSON dump, then it means that
     // this client is known but it was not registered yet (eg. calling module not "imported" yet).
     if (
-      bucketName == Services.prefs.getCharPref(PREF_SETTINGS_DEFAULT_BUCKET)
+      bucketName ==
+      Utils.actualBucketName(AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET)
     ) {
       const c = new RemoteSettingsClient(collectionName, defaultOptions);
       const [dbExists, localDump] = await Promise.all([
         Utils.hasLocalData(c),
         Utils.hasLocalDump(bucketName, collectionName),
       ]);
       if (dbExists || localDump) {
         return c;
       }
     }
-    // Else, we cannot return a client insttance because we are not able to synchronize data in specific buckets.
+    // Else, we cannot return a client instance because we are not able to synchronize data in specific buckets.
     // Mainly because we cannot guess which `signerName` has to be used for example.
     // And we don't want to synchronize data for collections in the main bucket that are
     // completely unknown (ie. no database and no JSON dump).
     console.debug(`No known client for ${bucketName}/${collectionName}`);
     return null;
   }
 
   /**
@@ -429,16 +428,32 @@ function remoteSettingsFunction() {
       .store(currentEtag, status)
       .catch(error => Cu.reportError(error));
 
     console.info("Polling for changes done");
     Services.obs.notifyObservers(null, "remote-settings:changes-poll-end");
   };
 
   /**
+   * Enables or disables preview mode.
+   *
+   * When enabled, all existing and future clients will pull data from
+   * the `*-preview` buckets. This allows developers and QA to test their
+   * changes before publishing them for all clients.
+   */
+  remoteSettings.enablePreviewMode = enabled => {
+    // Set the flag for future clients.
+    Utils.enablePreviewMode(enabled);
+    // Enable it on existing clients.
+    for (const client of _clients.values()) {
+      client.refreshBucketName();
+    }
+  };
+
+  /**
    * Returns an object with polling status information and the list of
    * known remote settings collections.
    */
   remoteSettings.inspect = async () => {
     const {
       changes,
       currentEtag: serverTimestamp,
     } = await Utils.fetchLatestChanges(Utils.SERVER_URL);
@@ -467,18 +482,21 @@ function remoteSettingsFunction() {
     );
 
     return {
       serverURL: Utils.SERVER_URL,
       pollingEndpoint: Utils.SERVER_URL + Utils.CHANGES_PATH,
       serverTimestamp,
       localTimestamp: gPrefs.getCharPref(PREF_SETTINGS_LAST_ETAG, null),
       lastCheck: gPrefs.getIntPref(PREF_SETTINGS_LAST_UPDATE, 0),
-      mainBucket: Services.prefs.getCharPref(PREF_SETTINGS_DEFAULT_BUCKET),
+      mainBucket: Utils.actualBucketName(
+        AppConstants.REMOTE_SETTINGS_DEFAULT_BUCKET
+      ),
       defaultSigner: DEFAULT_SIGNER,
+      previewMode: Utils.PREVIEW_MODE,
       collections: collections.filter(c => !!c),
       history: {
         [TELEMETRY_SOURCE_SYNC]: await gSyncHistory.list(),
       },
     };
   };
 
   /**
--- a/services/settings/test/unit/test_remote_settings.js
+++ b/services/settings/test/unit/test_remote_settings.js
@@ -1,12 +1,10 @@
 /* import-globals-from ../../../common/tests/unit/head_helpers.js */
 
-const { Constructor: CC } = Components;
-
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { AppConstants } = ChromeUtils.import(
   "resource://gre/modules/AppConstants.jsm"
 );
 const { ObjectUtils } = ChromeUtils.import(
   "resource://gre/modules/ObjectUtils.jsm"
 );
 const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
@@ -30,28 +28,30 @@ const TELEMETRY_EVENTS_FILTERS = {
   method: "uptake",
 };
 
 let server;
 let client;
 let clientWithDump;
 
 async function clear_state() {
+  // Reset preview mode.
+  RemoteSettings.enablePreviewMode(undefined);
+  Services.prefs.clearUserPref("services.settings.preview_enabled");
+
   client.verifySignature = false;
   clientWithDump.verifySignature = false;
 
   // Clear local DB.
   await client.db.clear();
   // Reset event listeners.
   client._listeners.set("sync", []);
 
   await clientWithDump.db.clear();
 
-  Services.prefs.clearUserPref("services.settings.default_bucket");
-
   // Clear events snapshot.
   TelemetryTestUtils.assertEvents([], {}, { process: "dummy" });
 }
 
 function run_test() {
   // Set up an HTTP Server
   server = new HttpServer();
   server.start(-1);
@@ -261,26 +261,16 @@ add_task(
     // When collection has a dump in services/settings/dumps/{bucket}/{collection}.json
     const data = await clientWithDump.get();
     notEqual(data.length, 0);
     // No synchronization happened (responses are not mocked).
   }
 );
 add_task(clear_state);
 
-add_task(async function test_get_does_not_load_dump_when_pref_is_false() {
-  Services.prefs.setBoolPref("services.settings.load_dump", false);
-
-  const data = await clientWithDump.get();
-
-  equal(data.map(r => r.id).join(", "), "pt-BR, xx"); // No dump, 2 pulled from test server.
-  Services.prefs.clearUserPref("services.settings.load_dump");
-});
-add_task(clear_state);
-
 add_task(async function test_get_loads_dump_only_once_if_called_in_parallel() {
   const backup = clientWithDump._importJSONDump;
   let callCount = 0;
   clientWithDump._importJSONDump = async () => {
     callCount++;
     // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
     await new Promise(resolve => setTimeout(resolve, 100));
     return 42;
@@ -990,83 +980,120 @@ add_task(async function test_telemetry_r
     ],
     TELEMETRY_EVENTS_FILTERS
   );
 
   client.db.list = backup;
 });
 add_task(clear_state);
 
-add_task(async function test_bucketname_changes_when_bucket_pref_changes() {
+add_task(async function test_bucketname_changes_when_preview_mode_is_enabled() {
   equal(client.bucketName, "main");
 
-  Services.prefs.setCharPref(
-    "services.settings.default_bucket",
-    "main-preview"
-  );
+  RemoteSettings.enablePreviewMode(true);
 
   equal(client.bucketName, "main-preview");
 });
 add_task(clear_state);
 
-add_task(async function test_bucket_pref_ignored_when_bucketName_set() {
-  let clientWithBucket = RemoteSettings("coll", { bucketName: "buck" });
-  equal(clientWithBucket.collectionName, "coll");
-  equal(clientWithBucket.bucketName, "buck");
+add_task(
+  async function test_preview_mode_pref_affects_bucket_names_before_instantiated() {
+    Services.prefs.setBoolPref("services.settings.preview_enabled", true);
 
-  Services.prefs.setCharPref(
-    "services.settings.default_bucket",
-    "coll-preview"
-  );
+    let clientWithDefaultBucket = RemoteSettings("other");
+    let clientWithBucket = RemoteSettings("coll", { bucketName: "buck" });
 
-  equal(clientWithBucket.bucketName, "buck");
-});
+    equal(clientWithDefaultBucket.bucketName, "main-preview");
+    equal(clientWithBucket.bucketName, "buck-preview");
+  }
+);
 add_task(clear_state);
 
 add_task(
-  async function test_get_loads_default_records_from_a_local_dump_if_preview_collection() {
+  async function test_preview_enabled_pref_ignored_when_mode_is_set_explicitly() {
+    Services.prefs.setBoolPref("services.settings.preview_enabled", true);
+
+    let clientWithDefaultBucket = RemoteSettings("other");
+    let clientWithBucket = RemoteSettings("coll", { bucketName: "buck" });
+
+    equal(clientWithDefaultBucket.bucketName, "main-preview");
+    equal(clientWithBucket.bucketName, "buck-preview");
+
+    RemoteSettings.enablePreviewMode(false);
+
+    equal(clientWithDefaultBucket.bucketName, "main");
+    equal(clientWithBucket.bucketName, "buck");
+  }
+);
+add_task(clear_state);
+
+add_task(
+  async function test_get_loads_default_records_from_a_local_dump_when_preview_mode_is_enabled() {
     if (IS_ANDROID) {
       // Skip test: we don't ship remote settings dumps on Android (see package-manifest).
       return;
     }
-    Services.prefs.setCharPref(
-      "services.settings.default_bucket",
-      "main-preview"
-    );
+    RemoteSettings.enablePreviewMode(true);
     // When collection has a dump in services/settings/dumps/{bucket}/{collection}.json
     const data = await clientWithDump.get();
     notEqual(data.length, 0);
     // No synchronization happened (responses are not mocked).
   }
 );
 add_task(clear_state);
 
+add_task(async function test_local_db_distinguishes_preview_records() {
+  RemoteSettings.enablePreviewMode(true);
+  client.db.importChanges({}, Date.now(), [{ id: "record-1" }], {
+    clear: true,
+  });
+
+  RemoteSettings.enablePreviewMode(false);
+  client.db.importChanges({}, Date.now(), [{ id: "record-2" }], {
+    clear: true,
+  });
+
+  deepEqual(await client.get(), [{ id: "record-2" }]);
+});
+add_task(clear_state);
+
 add_task(
-  async function test_inspect_changes_the_list_when_bucket_pref_is_changed() {
+  async function test_inspect_changes_the_list_when_preview_mode_is_enabled() {
     if (IS_ANDROID) {
-      // Skip test: we don't ship remote settings dumps on Android (see package-manifest).
+      // Skip test: we don't ship remote settings dumps on Android (see package-manifest),
+      // and this test relies on the fact that clients are instantiated if a dump is packaged.
       return;
     }
+
     // Register a client only listed in -preview...
     RemoteSettings("crash-rate");
 
-    const { collections: before } = await RemoteSettings.inspect();
+    const {
+      collections: before,
+      previewMode: previewModeBefore,
+    } = await RemoteSettings.inspect();
+
+    Assert.ok(!previewModeBefore, "preview is not enabled");
 
     // These two collections are listed in the main bucket in monitor/changes (one with dump, one registered).
     deepEqual(before.map(c => c.collection).sort(), [
       "language-dictionaries",
       "password-fields",
     ]);
 
-    // Switch to main-preview bucket.
-    Services.prefs.setCharPref(
-      "services.settings.default_bucket",
-      "main-preview"
-    );
-    const { collections: after, mainBucket } = await RemoteSettings.inspect();
+    // Switch to preview mode.
+    RemoteSettings.enablePreviewMode(true);
+
+    const {
+      collections: after,
+      mainBucket,
+      previewMode,
+    } = await RemoteSettings.inspect();
+
+    Assert.ok(previewMode, "preview is enabled");
 
     // These two collections are listed in the main bucket in monitor/changes (both are registered).
     deepEqual(after.map(c => c.collection).sort(), [
       "crash-rate",
       "password-fields",
     ]);
     equal(mainBucket, "main-preview");
   }
--- a/services/settings/test/unit/test_remote_settings_jexl_filters.js
+++ b/services/settings/test/unit/test_remote_settings_jexl_filters.js
@@ -127,22 +127,22 @@ add_task(async function test_support_of_
   await createRecords([
     {
       willMatch: true,
       filter_expression: '"services.settings.last_etag"|preferenceValue == 42',
     },
     {
       willMatch: true,
       filter_expression:
-        '"services.settings.default_bucket"|preferenceExists == true',
+        '"services.settings.poll_interval"|preferenceExists == true',
     },
     {
       willMatch: true,
       filter_expression:
-        '"services.settings.default_bucket"|preferenceIsUserSet == false',
+        '"services.settings.poll_interval"|preferenceIsUserSet == false',
     },
     {
       willMatch: true,
       filter_expression:
         '"services.settings.last_etag"|preferenceIsUserSet == true',
     },
   ]);
 
--- a/services/settings/test/unit/test_remote_settings_poll.js
+++ b/services/settings/test/unit/test_remote_settings_poll.js
@@ -170,19 +170,18 @@ add_task(async function test_check_succe
         collection: "test-collection",
       },
     ])
   );
 
   // add a test kinto client that will respond to lastModified information
   // for a collection called 'test-collection'.
   // Let's use a bucket that is not the default one (`test-bucket`).
-  Services.prefs.setCharPref("services.settings.test_bucket", "test-bucket");
   const c = RemoteSettings("test-collection", {
-    bucketNamePref: "services.settings.test_bucket",
+    bucketName: "test-bucket",
   });
   let maybeSyncCalled = false;
   c.maybeSync = () => {
     maybeSyncCalled = true;
   };
 
   // Ensure that the remote-settings:changes-poll-end notification works
   let notificationObserved = false;
@@ -1147,25 +1146,24 @@ add_task(async function test_syncs_clien
       },
     ])
   );
 
   // This simulates what remote-settings would do when initializing a local database.
   // We don't want to instantiate a client using the RemoteSettings() API
   // since we want to test «unknown» clients that have a local database.
   new RemoteSettingsClient("addons", {
-    bucketNamePref: "services.blocklist.bucket", // bucketName = "blocklists"
+    bucketName: "blocklists",
   }).db.importChanges({}, 42);
-  new RemoteSettingsClient("recipes", {
-    bucketNamePref: "services.settings.default_bucket", // bucketName = "main"
-  }).db.importChanges({}, 43);
+  new RemoteSettingsClient("recipes").db.importChanges({}, 43);
 
   let error;
   try {
     await RemoteSettings.pollChanges();
+    Assert.ok(false, "pollChange() should throw when pulling recipes");
   } catch (e) {
     error = e;
   }
 
   // The `main/some-unknown` should be skipped because it has no local database.
   // The `blocklists/addons` should be skipped because it is not the main bucket.
   // The `recipes` has a local database, and should cause a network error because the test
   // does not setup the server to receive the requests of `maybeSync()`.
new file mode 100644
--- /dev/null
+++ b/services/settings/test/unit/test_remote_settings_release_prefs.js
@@ -0,0 +1,207 @@
+"use strict";
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { AppConstants } = ChromeUtils.import(
+  "resource://gre/modules/AppConstants.jsm"
+);
+
+const UTILS_MODULE = "resource://services-settings/Utils.jsm";
+
+const env = Cc["@mozilla.org/process/environment;1"].getService(
+  Ci.nsIEnvironment
+);
+
+function clear_state() {
+  env.set("MOZ_REMOTE_SETTINGS_DEVTOOLS", "0");
+  Services.prefs.clearUserPref("services.settings.server");
+  Services.prefs.clearUserPref("services.settings.preview_enabled");
+  Cu.unload(UTILS_MODULE);
+  Assert.ok(!Cu.isModuleLoaded(UTILS_MODULE), "Utils was unloaded before test");
+}
+
+add_setup(async function() {
+  // Set this env vars in order to test the code path where the
+  // server URL can only be overridden from Dev Tools.
+  // See `isRunningTests` in `services/settings/Utils.jsm`.
+  const before = env.get("MOZ_DISABLE_NONLOCAL_CONNECTIONS");
+  env.set("MOZ_DISABLE_NONLOCAL_CONNECTIONS", "0");
+
+  registerCleanupFunction(() => {
+    clear_state();
+    env.set("MOZ_DISABLE_NONLOCAL_CONNECTIONS", before);
+  });
+});
+
+add_task(clear_state);
+
+add_task(
+  {
+    skip_if: () => !AppConstants.RELEASE_OR_BETA,
+  },
+  async function test_server_url_cannot_be_toggled_in_release() {
+    Services.prefs.setCharPref(
+      "services.settings.server",
+      "http://localhost:8888/v1"
+    );
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.equal(
+      Utils.SERVER_URL,
+      AppConstants.REMOTE_SETTINGS_SERVER_URL,
+      "Server url pref was not read in release"
+    );
+  }
+);
+
+add_task(
+  {
+    skip_if: () => AppConstants.RELEASE_OR_BETA,
+  },
+  async function test_server_url_cannot_be_toggled_in_dev_nightly() {
+    Services.prefs.setCharPref(
+      "services.settings.server",
+      "http://localhost:8888/v1"
+    );
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.notEqual(
+      Utils.SERVER_URL,
+      AppConstants.REMOTE_SETTINGS_SERVER_URL,
+      "Server url pref was read in nightly/dev"
+    );
+  }
+);
+add_task(clear_state);
+
+add_task(
+  {
+    skip_if: () => !AppConstants.RELEASE_OR_BETA,
+  },
+  async function test_preview_mode_cannot_be_toggled_in_release() {
+    Services.prefs.setBoolPref("services.settings.preview_enabled", true);
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.ok(!Utils.PREVIEW_MODE, "Preview mode pref was not read in release");
+  }
+);
+add_task(clear_state);
+
+add_task(
+  {
+    skip_if: () => AppConstants.RELEASE_OR_BETA,
+  },
+  async function test_preview_mode_cannot_be_toggled_in_dev_nightly() {
+    Services.prefs.setBoolPref("services.settings.preview_enabled", true);
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.ok(Utils.PREVIEW_MODE, "Preview mode pref is read in dev/nightly");
+  }
+);
+add_task(clear_state);
+
+add_task(
+  {
+    skip_if: () => !AppConstants.RELEASE_OR_BETA,
+  },
+  async function test_load_dumps_will_always_be_loaded_in_release() {
+    Services.prefs.setCharPref(
+      "services.settings.server",
+      "http://localhost:8888/v1"
+    );
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.equal(
+      Utils.SERVER_URL,
+      AppConstants.REMOTE_SETTINGS_SERVER_URL,
+      "Server url pref was not read"
+    );
+    Assert.ok(Utils.LOAD_DUMPS, "Dumps will always be loaded");
+  }
+);
+
+add_task(
+  {
+    skip_if: () => AppConstants.RELEASE_OR_BETA,
+  },
+  async function test_load_dumps_can_be_disabled_in_dev_nightly() {
+    Services.prefs.setCharPref(
+      "services.settings.server",
+      "http://localhost:8888/v1"
+    );
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.notEqual(
+      Utils.SERVER_URL,
+      AppConstants.REMOTE_SETTINGS_SERVER_URL,
+      "Server url pref was read"
+    );
+    Assert.ok(!Utils.LOAD_DUMPS, "Dumps are not loaded if server is not prod");
+  }
+);
+add_task(clear_state);
+
+add_task(
+  async function test_server_url_can_be_changed_in_all_versions_if_running_for_devtools() {
+    env.set("MOZ_REMOTE_SETTINGS_DEVTOOLS", "1");
+    Services.prefs.setCharPref(
+      "services.settings.server",
+      "http://localhost:8888/v1"
+    );
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.notEqual(
+      Utils.SERVER_URL,
+      AppConstants.REMOTE_SETTINGS_SERVER_URL,
+      "Server url pref was read"
+    );
+  }
+);
+add_task(clear_state);
+
+add_task(
+  async function test_preview_mode_can_be_changed_in_all_versions_if_running_for_devtools() {
+    env.set("MOZ_REMOTE_SETTINGS_DEVTOOLS", "1");
+    Services.prefs.setBoolPref("services.settings.preview_enabled", true);
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.ok(Utils.PREVIEW_MODE, "Preview mode pref was read");
+  }
+);
+add_task(clear_state);
+
+add_task(
+  async function test_dumps_are_not_loaded_if_server_is_not_prod_if_running_for_devtools() {
+    env.set("MOZ_REMOTE_SETTINGS_DEVTOOLS", "1");
+    Services.prefs.setCharPref(
+      "services.settings.server",
+      "http://localhost:8888/v1"
+    );
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+    Assert.ok(!Utils.LOAD_DUMPS, "Dumps won't be loaded");
+  }
+);
+add_task(clear_state);
+
+add_task(
+  async function test_dumps_are_loaded_if_server_is_prod_if_running_for_devtools() {
+    env.set("MOZ_REMOTE_SETTINGS_DEVTOOLS", "1");
+    Services.prefs.setCharPref(
+      "services.settings.server",
+      AppConstants.REMOTE_SETTINGS_SERVER_URL
+    );
+
+    const { Utils } = ChromeUtils.import(UTILS_MODULE);
+
+    Assert.ok(Utils.LOAD_DUMPS, "dumps are loaded if prod");
+  }
+);
+add_task(clear_state);
--- a/services/settings/test/unit/test_remote_settings_worker.js
+++ b/services/settings/test/unit/test_remote_settings_worker.js
@@ -40,19 +40,17 @@ add_task(async function test_canonicaljs
   );
 });
 
 add_task(async function test_import_json_dump_into_idb() {
   if (IS_ANDROID) {
     // Skip test: we don't ship remote settings dumps on Android (see package-manifest).
     return;
   }
-  const client = new RemoteSettingsClient("language-dictionaries", {
-    bucketNamePref: "services.settings.default_bucket",
-  });
+  const client = new RemoteSettingsClient("language-dictionaries");
   const before = await client.get({ syncIfEmpty: false });
   Assert.equal(before.length, 0);
 
   await RemoteSettingsWorker.importJSONDump("main", "language-dictionaries");
 
   const after = await client.get({ syncIfEmpty: false });
   Assert.ok(after.length > 0);
   let lastModifiedStamp = await client.getLastModified();
--- a/services/settings/test/unit/test_shutdown_handling.js
+++ b/services/settings/test/unit/test_shutdown_handling.js
@@ -94,19 +94,17 @@ add_task(async function test_shutdown_im
     rejection = e;
   });
   ok(rejection, "Directly aborted promise should also have rejected.");
   // Now clear the shutdown flag and rejection error:
   Database._cancelShutdown();
 });
 
 add_task(async function test_shutdown_worker() {
-  let client = new RemoteSettingsClient("language-dictionaries", {
-    bucketNamePref: "services.settings.default_bucket",
-  });
+  let client = new RemoteSettingsClient("language-dictionaries");
   const before = await client.get({ syncIfEmpty: false });
   Assert.equal(before.length, 0);
 
   let records = [{}];
   let importPromise = RemoteSettingsWorker._execute(
     "_test_only_import",
     ["main", "language-dictionaries", records, 0],
     { mustComplete: true }
--- a/services/settings/test/unit/xpcshell.ini
+++ b/services/settings/test/unit/xpcshell.ini
@@ -1,22 +1,23 @@
 [DEFAULT]
 head = ../../../common/tests/unit/head_global.js ../../../common/tests/unit/head_helpers.js
 firefox-appdir = browser
 tags = remote-settings
 support-files =
   test_remote_settings_signatures/**
-skip-if = appname == "thunderbird" # Bug 1662758 - these tests don't pass with a different default_bucket.
+skip-if = appname == "thunderbird" # Bug 1662758 - these tests don't pass if default bucket isn't "main".
 
 [test_attachments_downloader.js]
 support-files = test_attachments_downloader/**
 [test_remote_settings.js]
 [test_remote_settings_dump_lastmodified.js]
 [test_remote_settings_offline.js]
 [test_remote_settings_poll.js]
 [test_remote_settings_worker.js]
 [test_remote_settings_jexl_filters.js]
+[test_remote_settings_release_prefs.js]
 [test_remote_settings_signatures.js]
 [test_remote_settings_sync_history.js]
 [test_remote_settings_utils.js]
 [test_remote_settings_utils_telemetry.js]
 skip-if = os == "android" # bug 1739463
 [test_shutdown_handling.js]
--- a/taskcluster/ci/config.yml
+++ b/taskcluster/ci/config.yml
@@ -358,28 +358,16 @@ merge-automation:
             version-files:
                 - filename: "browser/config/version_display.txt"
                   new-suffix: 'esr'
             replacements: []
             merge-old-head: false
             base-tag: "FIREFOX_ESR_{major_version}_BASE"
             from-repo: 'https://hg.mozilla.org/releases/mozilla-release'
             from-branch: 'release'
-            to-repo: 'https://hg.mozilla.org/releases/mozilla-esr91'
-            to-branch: 'esr91'
-        release-to-esr102:
-            fetch-version-from: "browser/config/version.txt"
-            version-files:
-                - filename: "browser/config/version_display.txt"
-                  new-suffix: 'esr'
-            replacements: []
-            merge-old-head: false
-            base-tag: "FIREFOX_ESR_{major_version}_BASE"
-            from-repo: 'https://hg.mozilla.org/releases/mozilla-release'
-            from-branch: 'release'
             to-repo: 'https://hg.mozilla.org/releases/mozilla-esr102'
             to-branch: 'esr102'
         bump-central:
             fetch-version-from: "browser/config/version.txt"
             version-files:
                 - filename: "config/milestone.txt"
                   version-bump: "major"
                   new-suffix: 'a1'
--- a/taskcluster/ci/updatebot/kind.yml
+++ b/taskcluster/ci/updatebot/kind.yml
@@ -9,42 +9,16 @@ kind-dependencies:
     - toolchain
 
 transforms:
     - gecko_taskgraph.transforms.release_notifications:transforms
     - gecko_taskgraph.transforms.job:transforms
     - gecko_taskgraph.transforms.task:transforms
 
 jobs:
-    verify:
-        description: Verify Job for Updatebot
-        run-on-projects: ['autoland']
-        treeherder:
-            kind: other
-            platform: updatebot/all
-            symbol: verify
-            tier: 1
-        worker-type: b-linux
-        worker:
-            docker-image: {in-tree: debian11-amd64-build}
-            max-run-time: 3600
-            env:
-                PYTHONUNBUFFERED: "1"
-        run:
-            using: run-task
-            cwd: '{checkout}'
-            command: ./mach python taskcluster/scripts/misc/verify-updatebot.py
-        notifications:
-            subject: 'updatebot verify job failed'
-            message: 'updatebot verify job failed'
-            status-types:
-                - on-failed
-                - on-exception
-            emails: ["tom@mozilla.com", "jewilde@mozilla.com"]
-
     cron:
         description: Cron Job for Updatebot
         run-on-projects: []
         treeherder:
             kind: other
             platform: updatebot/all
             symbol: cron
             tier: 1
deleted file mode 100755
--- a/taskcluster/scripts/misc/verify-updatebot.py
+++ /dev/null
@@ -1,420 +0,0 @@
-#!/usr/bin/env python3
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, # You can obtain one at http://mozilla.org/MPL/2.0/.
-
-"""
-The purpose of this job is to run on autoland and ensure that any commits
-made by the Updatebot bot account are reproducible.  Patches that aren't
-reproducible indicate some sort of error in this script, or represent
-concerns about the integrity of the patch made by Updatebot.
-
-More simply: If this job fails, any patches by Updatebot SHOULD NOT land
-             because they may represent a security indicent.
-"""
-
-from __future__ import absolute_import, print_function
-
-import re
-import os
-import sys
-import requests
-import subprocess
-
-RE_BUG = re.compile("Bug (\d+)")
-RE_COMMITMSG = re.compile("Update (.+) to ([^\s]+)(.*)")
-RE_PATCHMSG = re.compile("Apply mozilla patches for")
-
-
-class Revision:
-    def __init__(self, line):
-        self.node = None
-        self.author = None
-        self.desc = None
-        self.bug = None
-
-        line = line.strip()
-        if not line:
-            return
-
-        components = line.split(" | ")
-        self.node, self.author, self.desc = components[0:3]
-        try:
-            self.bug = RE_BUG.search(self.desc).groups(0)[0]
-        except Exception:
-            pass
-
-    def __str__(self):
-        bug_str = " (No Bug)" if not self.bug else " (Bug %s)" % self.bug
-        return self.node + " by " + self.author + bug_str
-
-
-def find_moz_yaml_from_commit(rev):
-    files_changed = subprocess.check_output(["hg", "status", "--change", rev.node])
-    files_changed = files_changed.decode("utf-8").split("\n")
-
-    moz_yaml_file = None
-    for f in files_changed:
-        if "moz.yaml" in f:
-            if moz_yaml_file:
-                msg = (
-                    "Already had a moz.yaml file (%s) and then we found another? (%s)"
-                    % (moz_yaml_file, f)
-                )
-                raise Exception(msg)
-            moz_yaml_file = f[2:]
-    return moz_yaml_file
-
-
-def get_patch_diff(rev):
-    commitdiff = (
-        subprocess.check_output(["hg", "export", rev.node]).decode("utf-8").split("\n")
-    )
-    start_index = 0
-    for i in range(len(commitdiff)):
-        if "diff --git" in commitdiff[i]:
-            start_index = i
-            break
-    patch_diff = commitdiff[start_index:]
-    return patch_diff
-
-
-def compare_diffs(patch_diff, recreated_diff):
-    this_failure = False
-    if len(recreated_diff) != len(patch_diff):
-        print(
-            "  The recreated diff is %i lines long and the original diff is %i lines long."
-            % (len(recreated_diff), len(patch_diff))
-        )
-        this_failure = True
-
-    for i in range(min(len(recreated_diff), len(patch_diff))):
-        if recreated_diff[i] != patch_diff[i]:
-            if not this_failure:
-                print(
-                    "  Identified a difference between patches, starting on line %i."
-                    % i
-                )
-            this_failure = True
-    return this_failure
-
-
-# ================================================================================================
-# Find all commits we are hopefully landing in this push
-
-assert os.environ.get("GECKO_HEAD_REV"), "No revision head in the environment"
-assert os.environ.get("GECKO_HEAD_REPOSITORY"), "No repository in the environment"
-url = "%s/json-pushes?changeset=%s&version=2" % (
-    os.environ.get("GECKO_HEAD_REPOSITORY"),
-    os.environ.get("GECKO_HEAD_REV"),
-)
-
-print("Requesting revisions from", url)
-try:
-    response = requests.get(url)
-    revisions_json = response.json()
-except Exception:
-    print("Got an error, re-requesting revisions from", url)
-    response = requests.get(url)
-    revisions_json = response.json()
-
-assert len(revisions_json["pushes"]) >= 1, "Did not see a push in the autoland API"
-pushid = list(revisions_json["pushes"].keys())[0]
-rev_ids = revisions_json["pushes"][pushid]["changesets"]
-
-revisions = []
-for rev_id in rev_ids:
-    rev_detail = subprocess.check_output(
-        [
-            "hg",
-            "log",
-            "--template",
-            "{node} | {author} | {desc|firstline}\n",
-            "-r",
-            rev_id,
-        ]
-    )
-    revisions.append(rev_detail.decode("utf-8"))
-
-if not revisions:
-    msg = """
-Don't see any non-public revisions. This indicates that the mercurial
-repositories may not be operating in an expected way, and a script
-that performs security checks may fail to perform them properly.
-
-Sheriffs: This _does not_ mean the changesets involved need to be backed
-out. You can continue with your usual operation. However; if this occurs
-during a mercurial upgrade, or a refactring of how we treat try/autoland
-or the landing process, it means that we need to revisit the operation
-of this script.
-
-For now, we ask that you file a bug blocking 1618282 indicating the task
-failed so we can investigate the circumstances that caused it to fail.
-    """
-    print(msg)
-    sys.exit(-1)
-
-# ================================================================================================
-# Find all the Updatebot Revisions (there might be multiple updatebot
-#      landings in a single push some day!)
-i = 1
-all_revisions = []
-updatebot_revisions = []
-print("There are %i revisions to be evaluated." % len(revisions))
-for r in revisions:
-    revision = Revision(r)
-    if not revision.node:
-        continue
-
-    all_revisions.append(revision)
-    print("  ", i, revision)
-    i += 1
-
-    if revision.author == "Updatebot <updatebot@mozilla.com>":
-        if not revision.bug:
-            # Check to see if this is a ./mach try run, and if so exempt it.
-            is_try = (
-                os.environ.get("GECKO_HEAD_REPOSITORY") == "https://hg.mozilla.org/try"
-            )
-
-            rev_detail = subprocess.check_output(
-                [
-                    "hg",
-                    "log",
-                    "--template",
-                    "{files}\n",
-                    "-r",
-                    revision.node,
-                ]
-            )
-            is_only_try_task = (
-                "try_task_config.json" == rev_detail.decode("utf-8").strip()
-            )
-
-            if is_try and is_only_try_task:
-                pass
-            else:
-                raise Exception(
-                    "Could not find a bug for revision %s (Description: %s)"
-                    % (revision.node, revision.desc)
-                )
-        else:
-            updatebot_revisions.append(revision)
-
-print("There are %i updatebot revisions to be evaluated." % len(updatebot_revisions))
-# ================================================================================================
-
-overall_failure = False
-
-# For each Updatebot Revision, find all the commits associated with that bug
-unique_updatebot_bugs = set([u.bug for u in updatebot_revisions])
-print("Unique Updatebot Bugs found: ", unique_updatebot_bugs)
-
-# Process each one
-for bug in unique_updatebot_bugs:
-    # Find all the commits associated with this bug.
-    # They should be ordered with the first commit as the first element and so on.
-    all_commits_for_this_bug = [r for r in all_revisions if r.bug == bug]
-    all_updatebot_commits_for_this_bug = [
-        r for r in updatebot_revisions if r.bug == bug
-    ]
-    assert (
-        len(all_updatebot_commits_for_this_bug) <= 2
-    ), "Saw more than two Updatebot commits for a single bug"
-    assert (
-        all_commits_for_this_bug[0] == all_updatebot_commits_for_this_bug[0]
-    ), "The first commit for this bug is not by Updatebot"
-    assert (
-        len(all_updatebot_commits_for_this_bug) == 1
-        or all_commits_for_this_bug[1] == all_updatebot_commits_for_this_bug[1]
-    ), "The second commit for this bug is not by Updatebot"
-
-    print("All commits for Bug %s: " % bug, [r.node for r in all_commits_for_this_bug])
-    print(
-        "All Updatebot commits for Bug %s: " % bug,
-        [r.node for r in all_updatebot_commits_for_this_bug],
-    )
-
-    try:
-        first_commit = all_commits_for_this_bug[0]
-        print("=" * 80)
-        print(
-            "Processing revision %s for Bug %s:" % (first_commit.node, first_commit.bug)
-        )
-
-        try:
-            library, target_revision = RE_COMMITMSG.search(first_commit.desc).groups(0)[
-                0:2
-            ]
-        except Exception:
-            print(
-                "Could not parse the bug description with RE_COMMITMSG for the revision: %s"
-                % first_commit.desc
-            )
-            overall_failure = True
-            continue
-
-        if library == "angle":
-            print("Currently cannot verify angle updates")
-            continue
-
-        moz_yaml_file = find_moz_yaml_from_commit(first_commit)
-        first_patch_diff = get_patch_diff(first_commit)
-
-        second_patch_diff = ""
-        if len(all_updatebot_commits_for_this_bug) > 1:
-            second_commit = all_commits_for_this_bug[1]
-            if not RE_PATCHMSG.search(second_commit.desc):
-                print(
-                    "Could not parse the bug description with RE_PATCHMSG for the revision: %s"
-                    % second_commit.desc
-                )
-                overall_failure = True
-                continue
-            second_patch_diff = get_patch_diff(second_commit)
-
-        # Okay, now go through in reverse order and backout all of the commits for this bug
-        all_commits_for_this_bug_reversed = all_commits_for_this_bug
-        all_commits_for_this_bug_reversed.reverse()
-        for c in all_commits_for_this_bug_reversed:
-            print("  Backing out", c.node)
-            # hg doesn't support the ability to commit a backout without prompting the
-            # user, but it does support not committing
-            subprocess.check_output(["hg", "backout", c.node, "--no-commit"])
-            subprocess.check_output(
-                [
-                    "hg",
-                    "--config",
-                    "ui.username=Updatebot Verifier <updatebot@mozilla.com>",
-                    "commit",
-                    "-m",
-                    "Backed out changeset %s" % c.node,
-                ]
-            )
-
-        # And now we re-do the updatebot commit
-        print("  Vendoring", moz_yaml_file)
-
-        cmd = ["./mach", "vendor", "--revision", target_revision, moz_yaml_file]
-        if len(all_updatebot_commits_for_this_bug) > 1:
-            cmd += ["--patch-mode", "none"]
-
-        ret = subprocess.call(cmd)
-        if ret:
-            print("  Vendoring returned code %i, but we're going to continue..." % ret)
-
-        # And now get the diff
-        first_recreated_diff = (
-            subprocess.check_output(["hg", "diff"]).decode("utf-8").split("\n")
-        )
-
-        # Now compare it, print if needed
-        first_patch_failure = compare_diffs(first_patch_diff, first_recreated_diff)
-        second_patch_failure = False
-
-        if len(all_updatebot_commits_for_this_bug) > 1:
-            # Now we need to commit it, and apply the patch.
-            subprocess.check_output(
-                [
-                    "hg",
-                    "--config",
-                    "ui.username=Updatebot Verifier <updatebot@mozilla.com>",
-                    "commit",
-                    "-m",
-                    "Commiting a vendoring of %s" % moz_yaml_file,
-                ]
-            )
-            # Patching
-            print("  Patching", moz_yaml_file)
-
-            cmd = [
-                "./mach",
-                "vendor",
-                "--revision",
-                target_revision,
-                moz_yaml_file,
-                "--patch-mode",
-                "only",
-            ]
-            ret = subprocess.call(cmd)
-            if ret:
-                print(
-                    "  Patching returned code %i, but we're going to continue..." % ret
-                )
-
-            # And now get the diff
-            second_recreated_diff = (
-                subprocess.check_output(["hg", "diff"]).decode("utf-8").split("\n")
-            )
-
-            # Now compare it, print if needed
-            second_patch_failure = compare_diffs(
-                second_patch_diff, second_recreated_diff
-            )
-
-            # Cleanup the working directory, clearing out the patch
-            subprocess.check_output(["hg", "revert", "."])
-            # Remove the vendoring patch
-            subprocess.check_output(
-                ["hg", "--config", "extensions.strip=", "strip", "tip"]
-            )
-        else:
-            # Cleanup so we can go to the next one
-            subprocess.check_output(["hg", "revert", "."])
-
-        # Remove any other backouts we did for this bug
-        subprocess.check_output(
-            [
-                "hg",
-                "--config",
-                "extensions.strip=",
-                "strip",
-                "tip~" + str(len(all_commits_for_this_bug) - 1),
-            ]
-        )
-
-        # Now process the outcome
-        if not first_patch_failure and not second_patch_failure:
-            print("  This revision was recreated successfully.")
-            continue
-
-        print("Vendoring:")
-        print("Original Diff:")
-        print("-" * 80)
-        for l in first_patch_diff:
-            print(l)
-        print("-" * 80)
-        print("Recreated Diff:")
-        print("-" * 80)
-        for l in first_recreated_diff:
-            print(l)
-        print("-" * 80)
-
-        if len(all_updatebot_commits_for_this_bug) > 1:
-            print("Patching:")
-            print("Original Diff:")
-            print("-" * 80)
-            for l in second_patch_diff:
-                print(l)
-            print("-" * 80)
-            print("Recreated Diff:")
-            print("-" * 80)
-            for l in second_recreated_diff:
-                print(l)
-            print("-" * 80)
-
-        overall_failure = True
-    except subprocess.CalledProcessError as e:
-        print("Caught an exception when running:", e.cmd)
-        print("Return Code:", e.returncode)
-        print("-------")
-        print("stdout:")
-        print(e.stdout)
-        print("-------")
-        print("stderr:")
-        print(e.stderr)
-        print("----------------------------------------------")
-        overall_failure = True
-
-if overall_failure:
-    sys.exit(1)
--- a/testing/mozharness/mozharness/base/script.py
+++ b/testing/mozharness/mozharness/base/script.py
@@ -1538,17 +1538,17 @@ class ScriptMixin(PlatformMixin):
                 self.log(
                     "Can't run command %s in non-existent directory '%s'!"
                     % (command, cwd),
                     level=level,
                 )
                 return -1
             self.info("Running command: %s in %s" % (command, cwd))
         else:
-            self.info("Running command: %s" % command)
+            self.info("Running command: %s" % (command,))
         if isinstance(command, list) or isinstance(command, tuple):
             self.info("Copy/paste: %s" % subprocess.list2cmdline(command))
         shell = True
         if isinstance(command, list) or isinstance(command, tuple):
             shell = False
         if env is None:
             if partial_env:
                 self.info("Using partial env: %s" % pprint.pformat(partial_env))
@@ -1657,17 +1657,17 @@ class ScriptMixin(PlatformMixin):
                 )
                 _fail = True
             if parser.num_errors:
                 self.log("failures found while parsing output", level=error_level)
                 _fail = True
             if _fail:
                 self.return_code = fatal_exit_code
                 self.fatal(
-                    "Halting on failure while running %s" % command,
+                    "Halting on failure while running %s" % (command,),
                     exit_code=fatal_exit_code,
                 )
         if return_type == "num_errors":
             return parser.num_errors
         return returncode
 
     def get_output_from_command(
         self,
--- a/testing/web-platform/tests/intersection-observer/zero-area-element-visible.html
+++ b/testing/web-platform/tests/intersection-observer/zero-area-element-visible.html
@@ -9,19 +9,24 @@ pre, #log {
   position: absolute;
   top: 0;
   left: 200px;
 }
 #target {
   width: 0px;
   height: 0px;
 }
+#container {
+  overflow: clip;
+}
 </style>
 
-<div id='target'></div>
+<div id="container">
+  <div id='target'></div>
+</div>
 
 <script>
 var entries = [];
 
 runTestCycle(function() {
   var target = document.getElementById('target');
   assert_true(!!target, "target exists");
   var observer = new IntersectionObserver(function(changes) {
--- a/toolkit/components/extensions/test/xpcshell/test_ext_extension_page_navigated.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_extension_page_navigated.js
@@ -182,34 +182,33 @@ add_task(async function test_extension_p
   } else {
     equal(
       active,
       false,
       "extension page context is expected to be inactive while moved into the BFCache"
     );
   }
 
-  // If the extension page is expected to be in the BFCAche, then we expect to see
-  // a warning message logged for the ignored listener.
   if (typeof active === "boolean") {
-    const matchInfoMessage = /Ignored listener for inactive context .* path=webRequest.onBeforeRequest/;
-    ok(
-      messages.some(
-        msg => matchInfoMessage.test(msg.message) && msg.logLevel === msg.info
-      ),
-      "Expected info message for the ignored listener found"
-    );
-
-    // We should have not tried to deserialize the event data for the extension page
-    // that got moved into the bfcache (See Bug 1499129).
     AddonTestUtils.checkMessages(
-      (messages = messages.filter(msg => !matchInfoMessage.test(msg.message))),
+      messages,
       {
         forbidden: [
-          /StructureCloneHolder.deserialize: Argument 1 is not an object./,
+          // We should not have tried to deserialize the event data for the extension page
+          // that got moved into the BFCache (See Bug 1499129).
+          {
+            message: /StructureCloneHolder.deserialize: Argument 1 is not an object/,
+          },
+        ],
+        expected: [
+          // If the extension page is expected to be in the BFCache, then we expect to see
+          // a warning message logged for the ignored listener.
+          {
+            message: /Ignored listener for inactive context .* path=webRequest.onBeforeRequest/,
+          },
         ],
       },
       "Expect no StructureCloneHolder error due to trying to send the event to inactive context"
     );
   }
 
   await extPage.close();
   await extension.unload();
--- a/toolkit/components/extensions/test/xpcshell/webidl-api/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/webidl-api/xpcshell.ini
@@ -1,29 +1,27 @@
 [DEFAULT]
-head = ../head.js ../head_remote.js ../head_service_worker.js head_webidl_api.js 
+head = ../head.js ../head_remote.js ../head_service_worker.js head_webidl_api.js
 firefox-appdir = browser
 tags = webextensions webextensions-webidl-api
 
 prefs =
   # Enable support for the extension background service worker.
   extensions.backgroundServiceWorker.enabled=true
   # Enable Extensions API WebIDL bindings for extension windows.
   extensions.webidl-api.enabled=true
   # Enable ExtensionMockAPI WebIDL bindings used for unit tests
   # related to the API request forwarding and not tied to a particular
   # extension API.
   extensions.webidl-api.expose_mock_interface=true
-  # services.settings.server/default_bucket:
   # Make sure that loading the default settings for url-classifier-skip-urls
   # doesn't interfere with running our tests while IDB operations are in
-  # flight by overriding the default remote settings bucket pref name to
+  # flight by overriding the remote settings server URL to
   # ensure that the IDB database isn't created in the first place.
   services.settings.server=http://localhost:7777/remote-settings-dummy/v1
-  services.settings.default_bucket=nonexistent-bucket-foo
 
 # NOTE: these tests seems to be timing out because it takes too much time to
 # run all tests and then fully exiting the test.
 skip-if = os == "android" && verify
 
 [test_ext_webidl_api.js]
 [test_ext_webidl_api_event_callback.js]
 skip-if =
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-e10s.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-e10s.ini
@@ -4,24 +4,22 @@ tail =
 firefox-appdir = browser
 skip-if = appname == "thunderbird" || os == "android"
 dupe-manifest =
 support-files =
   data/**
   xpcshell-content.ini
 tags = webextensions webextensions-e10s
 
-# services.settings.server/default_bucket:
 # Make sure that loading the default settings for url-classifier-skip-urls
 # doesn't interfere with running our tests while IDB operations are in
-# flight by overriding the default remote settings bucket pref name to
+# flight by overriding the remote settings server URL to
 # ensure that the IDB database isn't created in the first place.
 prefs =
   services.settings.server=http://localhost:7777/remote-settings-dummy/v1
-  services.settings.default_bucket=nonexistent-bucket-foo
 
 [include:xpcshell-common-e10s.ini]
 skip-if =
   socketprocess_networking # Bug 1759035
 [include:xpcshell-content.ini]
 skip-if =
   socketprocess_networking && fission # Bug 1759035
 
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-legacy-ep.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-legacy-ep.ini
@@ -1,23 +1,21 @@
 [DEFAULT]
 head = head.js head_remote.js head_e10s.js head_legacy_ep.js
 tail =
 firefox-appdir = browser
 skip-if = appname == "thunderbird" || os == "android"
 dupe-manifest =
 
-# services.settings.server/default_bucket:
 # Make sure that loading the default settings for url-classifier-skip-urls
 # doesn't interfere with running our tests while IDB operations are in
-# flight by overriding the default remote settings bucket pref name to
+# flight by overriding the remote settings server URL to
 # ensure that the IDB database isn't created in the first place.
 prefs =
   services.settings.server=http://localhost:7777/remote-settings-dummy/v1
-  services.settings.default_bucket=nonexistent-bucket-foo
 
 # Bug 1646182: Test the legacy ExtensionPermission backend until we fully
 # migrate to rkv
 [test_ext_permissions.js]
 [test_ext_permissions_api.js]
 [test_ext_permissions_migrate.js]
 [test_ext_permissions_uninstall.js]
 [test_ext_proxy_config.js]
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-remote.ini
@@ -8,24 +8,22 @@ skip-if =
   os == "mac" && socketprocess_networking && fission # Bug 1759035
   # I would put linux here, but debug has too many chunks and only runs this manifest, so I need 1 test to pass
 dupe-manifest =
 support-files =
   data/**
   xpcshell-content.ini
 tags = webextensions remote-webextensions
 
-# services.settings.server/default_bucket:
 # Make sure that loading the default settings for url-classifier-skip-urls
 # doesn't interfere with running our tests while IDB operations are in
-# flight by overriding the default remote settings bucket pref name to
+# flight by overriding the remote settings server URL to
 # ensure that the IDB database isn't created in the first place.
 prefs =
   services.settings.server=http://localhost:7777/remote-settings-dummy/v1
-  services.settings.default_bucket=nonexistent-bucket-foo
 
 [include:xpcshell-common.ini]
 skip-if =
   os == "linux" && socketprocess_networking # Bug 1759035
 [include:xpcshell-common-e10s.ini]
 skip-if =
   os == "linux" && socketprocess_networking # Bug 1759035
 [include:xpcshell-content.ini]
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -2,24 +2,22 @@
 head = head.js head_telemetry.js head_sync.js head_storage.js
 firefox-appdir = browser
 dupe-manifest =
 support-files =
   data/**
   xpcshell-content.ini
 tags = webextensions in-process-webextensions
 
-# services.settings.server/default_bucket:
 # Make sure that loading the default settings for url-classifier-skip-urls
 # doesn't interfere with running our tests while IDB operations are in
-# flight by overriding the default remote settings bucket pref name to
+# flight by overriding the remote settings server URL to
 # ensure that the IDB database isn't created in the first place.
 prefs =
   services.settings.server=http://localhost:7777/remote-settings-dummy/v1
-  services.settings.default_bucket=nonexistent-bucket-foo
 
 # This file contains tests which are not affected by multi-process
 # configuration, or do not support out-of-process content or extensions
 # for one reason or another.
 #
 # Tests which are affected by remote content or remote extensions should
 # go in one of:
 #
--- a/toolkit/components/normandy/lib/RecipeRunner.jsm
+++ b/toolkit/components/normandy/lib/RecipeRunner.jsm
@@ -615,17 +615,15 @@ var RecipeRunner = {
      * "normandy-recipes-capabilities" collection now.
      */
     async migration01RemoveOldRecipesCollection() {
       // Don't bother to open IDB and clear on clean profiles.
       const lastCheckPref =
         "services.settings.main.normandy-recipes.last_check";
       if (Services.prefs.prefHasUserValue(lastCheckPref)) {
         // We instantiate a client, but it won't take part of sync.
-        const client = new RemoteSettingsClient("normandy-recipes", {
-          bucketNamePref: "services.settings.default_bucket",
-        });
+        const client = new RemoteSettingsClient("normandy-recipes");
         await client.db.clear();
         Services.prefs.clearUserPref(lastCheckPref);
       }
     },
   },
 };
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js
@@ -156,19 +156,16 @@ add_task(async function setup() {
   // the addons database to be created (as does setting new theme).
   // For test_addonsStartup below, we want to test a "warm" startup where
   // there is already a database on disk.  Simulate that here by just
   // restarting the AddonManager.
   await AddonTestUtils.promiseShutdownManager();
   await AddonTestUtils.overrideBuiltIns({ system: [] });
   AddonTestUtils.addonStartup.remove(true);
   await AddonTestUtils.promiseStartupManager();
-  // Override ExtensionXPCShellUtils.jsm's overriding of the pref as the
-  // search service needs it.
-  Services.prefs.clearUserPref("services.settings.default_bucket");
 
   // Setup a webserver to serve Addons, etc.
   gHttpServer = new HttpServer();
   gHttpServer.start(-1);
   let port = gHttpServer.identity.primaryPort;
   gHttpRoot = "http://localhost:" + port + "/";
   gDataRoot = gHttpRoot + "data/";
   gHttpServer.registerDirectory("/data/", do_get_cwd());
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment_search.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment_search.js
@@ -49,19 +49,16 @@ add_task(async function setup() {
   // the addons database to be created (as does setting new theme).
   // For test_addonsStartup below, we want to test a "warm" startup where
   // there is already a database on disk.  Simulate that here by just
   // restarting the AddonManager.
   await AddonTestUtils.promiseShutdownManager();
   await AddonTestUtils.overrideBuiltIns({ system: [] });
   AddonTestUtils.addonStartup.remove(true);
   await AddonTestUtils.promiseStartupManager();
-  // Override ExtensionXPCShellUtils.jsm's overriding of the pref as the
-  // search service needs it.
-  Services.prefs.clearUserPref("services.settings.default_bucket");
 
   // Setup a webserver to serve Addons, etc.
   gHttpServer = new HttpServer();
   gHttpServer.start(-1);
   let port = gHttpServer.identity.primaryPort;
   gHttpRoot = "http://localhost:" + port + "/";
   gDataRoot = gHttpRoot + "data/";
   gHttpServer.registerDirectory("/data/", do_get_cwd());
--- a/toolkit/content/widgets/datetimebox.js
+++ b/toolkit/content/widgets/datetimebox.js
@@ -341,17 +341,17 @@ this.DateTimeBoxWidget = class {
     aPageUpDownInterval
   ) {
     let root = this.shadowRoot.getElementById("edit-wrapper");
     let field = this.shadowRoot.createElementAndAppendChildAt(root, "span");
     field.classList.add("datetime-edit-field");
     field.textContent = aPlaceHolder;
     field.placeholder = aPlaceHolder;
     field.setAttribute("aria-valuetext", "");
-    field.tabIndex = this.mInputElement.tabIndex;
+    field.tabIndex = this.editFieldTabIndex();
 
     field.setAttribute("readonly", this.mInputElement.readOnly);
     field.setAttribute("disabled", this.mInputElement.disabled);
     // Set property as well for convenience.
     field.disabled = this.mInputElement.disabled;
     field.readOnly = this.mInputElement.readOnly;
     field.setAttribute("aria-label", aLabel);
 
@@ -442,16 +442,23 @@ this.DateTimeBoxWidget = class {
     }
   }
 
   setPickerState(aIsOpen) {
     this.log("picker is now " + (aIsOpen ? "opened" : "closed"));
     this.mIsPickerOpen = aIsOpen;
   }
 
+  editFieldTabIndex() {
+    if (this.mInputElement.disabled) {
+      return -1;
+    }
+    return this.mInputElement.tabIndex;
+  }
+
   updateEditAttributes() {
     this.log("updateEditAttributes");
 
     let editRoot = this.shadowRoot.getElementById("edit-wrapper");
 
     for (let child of editRoot.querySelectorAll(
       ":scope > span.datetime-edit-field"
     )) {
@@ -460,18 +467,18 @@ this.DateTimeBoxWidget = class {
       // checks the literal string attribute values.
       child.setAttribute("disabled", this.mInputElement.disabled);
       child.setAttribute("readonly", this.mInputElement.readOnly);
 
       // Set property as well for convenience.
       child.disabled = this.mInputElement.disabled;
       child.readOnly = this.mInputElement.readOnly;
 
-      // tabIndex works on all elements
-      child.tabIndex = this.mInputElement.tabIndex;
+      // tabIndex as a property works on all relevant elements.
+      child.tabIndex = this.editFieldTabIndex();
     }
 
     this.mResetButton.disabled =
       this.mInputElement.disabled || this.mInputElement.readOnly;
     this.updateResetButtonVisibility();
   }
 
   isEmpty(aValue) {
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -413,30 +413,44 @@ this.AppConstants = Object.freeze({
 
   ENABLE_WEBDRIVER:
 #ifdef ENABLE_WEBDRIVER
     true,
 #else
     false,
 #endif
 
+  REMOTE_SETTINGS_SERVER_URL:
+#ifdef MOZ_THUNDERBIRD
+    "https://thunderbird-settings.thunderbird.net/v1",
+#else
+    "https://firefox.settings.services.mozilla.com/v1",
+#endif
+
   REMOTE_SETTINGS_VERIFY_SIGNATURE:
 #ifdef MOZ_THUNDERBIRD
     false,
 #else
     true,
 #endif
 
+  REMOTE_SETTINGS_DEFAULT_BUCKET:
+#ifdef MOZ_THUNDERBIRD
+    "thunderbird",
+#else
+    "main",
+#endif
+
   MOZ_GLEAN_ANDROID:
 #ifdef MOZ_GLEAN_ANDROID
     true,
 #else
     false,
 #endif
-  
+
   MOZ_JXL:
 #ifdef MOZ_JXL
     true,
 #else
     false,
 #endif
 
   // Returns true for CN region build when distibution id set as 'MozillaOnline'
--- a/toolkit/mozapps/extensions/Blocklist.jsm
+++ b/toolkit/mozapps/extensions/Blocklist.jsm
@@ -148,29 +148,17 @@ const PREF_BLOCKLIST_ADDONITEM_URL = "ex
 const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
 const PREF_BLOCKLIST_LEVEL = "extensions.blocklist.level";
 const PREF_BLOCKLIST_USE_MLBF = "extensions.blocklist.useMLBF";
 const PREF_EM_LOGGING_ENABLED = "extensions.logging.enabled";
 const DEFAULT_SEVERITY = 3;
 const DEFAULT_LEVEL = 2;
 const MAX_BLOCK_LEVEL = 3;
 
-// Remote Settings blocklist constants
-const PREF_BLOCKLIST_BUCKET = "services.blocklist.bucket";
-const PREF_BLOCKLIST_GFX_COLLECTION = "services.blocklist.gfx.collection";
-const PREF_BLOCKLIST_GFX_CHECKED_SECONDS = "services.blocklist.gfx.checked";
-const PREF_BLOCKLIST_GFX_SIGNER = "services.blocklist.gfx.signer";
-
-const PREF_BLOCKLIST_ADDONS_COLLECTION = "services.blocklist.addons.collection";
-const PREF_BLOCKLIST_ADDONS_CHECKED_SECONDS =
-  "services.blocklist.addons.checked";
-const PREF_BLOCKLIST_ADDONS_SIGNER = "services.blocklist.addons.signer";
-// Blocklist v3 - MLBF format.
-const PREF_BLOCKLIST_ADDONS3_CHECKED_SECONDS =
-  "services.blocklist.addons-mlbf.checked";
+const BLOCKLIST_BUCKET = "blocklists";
 
 const BlocklistTelemetry = {
   init() {
     // Used by BlocklistTelemetry.recordAddonBlockChangeTelemetry.
     Services.telemetry.setEventRecordingEnabled("blocklist", true);
   },
 
   /**
@@ -508,25 +496,20 @@ async function targetAppFilter(entry, en
  * serialized text form, using ',', '\t' and '\n' as separators.
  */
 const GfxBlocklistRS = {
   _ensureInitialized() {
     if (this._initialized || !gBlocklistEnabled) {
       return;
     }
     this._initialized = true;
-    this._client = RemoteSettings(
-      Services.prefs.getCharPref(PREF_BLOCKLIST_GFX_COLLECTION),
-      {
-        bucketNamePref: PREF_BLOCKLIST_BUCKET,
-        lastCheckTimePref: PREF_BLOCKLIST_GFX_CHECKED_SECONDS,
-        signerName: Services.prefs.getCharPref(PREF_BLOCKLIST_GFX_SIGNER),
-        filterFunc: targetAppFilter,
-      }
-    );
+    this._client = RemoteSettings("gfx", {
+      bucketName: BLOCKLIST_BUCKET,
+      filterFunc: targetAppFilter,
+    });
     this.checkForEntries = this.checkForEntries.bind(this);
     this._client.on("sync", this.checkForEntries);
   },
 
   shutdown() {
     if (this._client) {
       this._client.off("sync", this.checkForEntries);
     }
@@ -740,25 +723,20 @@ const ExtensionBlocklistRS = {
     return this._client.sync();
   },
 
   ensureInitialized() {
     if (!gBlocklistEnabled || this._initialized) {
       return;
     }
     this._initialized = true;
-    this._client = RemoteSettings(
-      Services.prefs.getCharPref(PREF_BLOCKLIST_ADDONS_COLLECTION),
-      {
-        bucketNamePref: PREF_BLOCKLIST_BUCKET,
-        lastCheckTimePref: PREF_BLOCKLIST_ADDONS_CHECKED_SECONDS,
-        signerName: Services.prefs.getCharPref(PREF_BLOCKLIST_ADDONS_SIGNER),
-        filterFunc: this._filterItem,
-      }
-    );
+    this._client = RemoteSettings("addons", {
+      bucketName: BLOCKLIST_BUCKET,
+      filterFunc: this._filterItem,
+    });
     this._onUpdate = this._onUpdate.bind(this);
     this._client.on("sync", this._onUpdate);
   },
 
   shutdown() {
     if (this._client) {
       this._client.off("sync", this._onUpdate);
       this._didShutdown = true;
@@ -1106,18 +1084,17 @@ const ExtensionBlocklistMLBF = {
   },
 
   ensureInitialized() {
     if (!gBlocklistEnabled || this._initialized) {
       return;
     }
     this._initialized = true;
     this._client = RemoteSettings("addons-bloomfilters", {
-      bucketName: "blocklists",
-      lastCheckTimePref: PREF_BLOCKLIST_ADDONS3_CHECKED_SECONDS,
+      bucketName: BLOCKLIST_BUCKET,
     });
     this._onUpdate = this._onUpdate.bind(this);
     this._client.on("sync", this._onUpdate);
   },
 
   shutdown() {
     if (this._client) {
       this._client.off("sync", this._onUpdate);
--- a/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonTestUtils.jsm
@@ -681,31 +681,16 @@ var AddonTestUtils = {
     const { BlocklistPrivate } = ChromeUtils.import(
       "resource://gre/modules/Blocklist.jsm"
     );
     const blocklistMapping = {
       extensions: BlocklistPrivate.ExtensionBlocklistRS,
       extensionsMLBF: BlocklistPrivate.ExtensionBlocklistMLBF,
     };
 
-    // Since we load the specified test data, we shouldn't let the
-    // packaged JSON dumps to interfere.
-    const pref = "services.settings.load_dump";
-    const backup = Services.prefs.getBoolPref(pref, null);
-    Services.prefs.setBoolPref(pref, false);
-    if (this.testScope) {
-      this.testScope.registerCleanupFunction(() => {
-        if (backup === null) {
-          Services.prefs.clearUserPref(pref);
-        } else {
-          Services.prefs.setBoolPref(pref, backup);
-        }
-      });
-    }
-
     for (const [dataProp, blocklistObj] of Object.entries(blocklistMapping)) {
       let newData = data[dataProp];
       if (!newData) {
         continue;
       }
       if (!Array.isArray(newData)) {
         throw new Error(
           "Expected an array of new items to put in the " +
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -1194,20 +1194,19 @@ async function mockGfxBlocklistItemsFrom
   return mockGfxBlocklistItems(json);
 }
 
 async function mockGfxBlocklistItems(items) {
   const { generateUUID } = Services.uuid;
   const { BlocklistPrivate } = ChromeUtils.import(
     "resource://gre/modules/Blocklist.jsm"
   );
-  const client = RemoteSettings(
-    Services.prefs.getCharPref("services.blocklist.gfx.collection"),
-    { bucketNamePref: "services.blocklist.bucket" }
-  );
+  const client = RemoteSettings("gfx", {
+    bucketName: "blocklists",
+  });
   const records = items.map(item => {
     if (item.id && item.last_modified) {
       return item;
     }
     return {
       id: generateUUID()
         .toString()
         .replace(/[{}]/g, ""),
--- a/toolkit/mozapps/extensions/test/xpcshell/rs-blocklist/test_blocklist_clients.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/rs-blocklist/test_blocklist_clients.js
@@ -1,20 +1,25 @@
 const { BlocklistPrivate } = ChromeUtils.import(
   "resource://gre/modules/Blocklist.jsm"
 );
 const { Utils: RemoteSettingsUtils } = ChromeUtils.import(
   "resource://services-settings/Utils.jsm"
 );
+const { RemoteSettings } = ChromeUtils.import(
+  "resource://services-settings/remote-settings.js"
+);
 
 const IS_ANDROID = AppConstants.platform == "android";
 
 let gBlocklistClients;
 
 async function clear_state() {
+  RemoteSettings.enablePreviewMode(undefined);
+
   for (let { client } of gBlocklistClients) {
     // Remove last server times.
     Services.prefs.clearUserPref(client.lastCheckTimePref);
 
     // Clear local DB.
     await client.db.clear();
   }
 }
@@ -204,21 +209,20 @@ add_task(
       });
       equal(list.length, 4);
       ok(list.every(e => e.willMatch));
     }
   }
 );
 add_task(clear_state);
 
-add_task(async function test_bucketname_changes_when_bucket_pref_changes() {
+add_task(async function test_bucketname_changes_when_preview_mode_is_enabled() {
   for (const { client } of gBlocklistClients) {
     equal(client.bucketName, "blocklists");
   }
 
-  Services.prefs.setCharPref("services.blocklist.bucket", "blocklists-preview");
+  RemoteSettings.enablePreviewMode(true);
 
   for (const { client } of gBlocklistClients) {
     equal(client.bucketName, "blocklists-preview", client.identifier);
   }
-  Services.prefs.clearUserPref("services.blocklist.bucket");
 });
 add_task(clear_state);