Merge autoland to mozilla-central. a=merge
authorBogdan Tara <btara@mozilla.com>
Thu, 10 Oct 2019 00:38:40 +0300
changeset 496994 cad27f93a9869351457fc5d5290777420068e173
parent 496961 a43ad34ac8e3033d22c2ea30eebfa8c271130e48 (current diff)
parent 496993 10b30e7c8ff47cca18af706a33e898b733fe6b50 (diff)
child 497072 f20fa8068ec25d685914a5945f2c54e36c89bc65
push id36673
push userbtara@mozilla.com
push dateWed, 09 Oct 2019 21:39:14 +0000
treeherdermozilla-central@cad27f93a986 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone71.0a1
first release with
nightly linux32
cad27f93a986 / 71.0a1 / 20191009213914 / files
nightly linux64
cad27f93a986 / 71.0a1 / 20191009213914 / files
nightly mac
cad27f93a986 / 71.0a1 / 20191009213914 / files
nightly win32
cad27f93a986 / 71.0a1 / 20191009213914 / files
nightly win64
cad27f93a986 / 71.0a1 / 20191009213914 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
browser/actors/ContextMenuSpecialProcessChild.jsm
--- a/accessible/tests/mochitest/name/test_general.xul
+++ b/accessible/tests/mochitest/name/test_general.xul
@@ -104,44 +104,16 @@
       // The label and button are siblings.
       testName("btn_label_3", "label3");
 
       // Multiple labels for single button: XUL button takes the last one.
       testName("btn_label_4", "label5");
 
 
       //////////////////////////////////////////////////////////////////////////
-      // Name from the label element in anonymous content (see bug 362365).
-
-      // Get the name from anonymous label element for anonymous textbox
-      // (@anonid is used).
-      var ID = "box_label_anon1";
-      var box1Acc = testName(ID, null);
-      if (box1Acc) {
-        var textboxAcc = box1Acc.firstChild;
-        is(textboxAcc.name, "Label",
-           "Wrong label for anonymous textbox of " + ID);
-      }
-
-      // Get the name from anonymous label element for anonymous textbox
-      // (@anonid is used). Nested bindings.
-      ID = "box_label_anon2";
-      var box2Acc = testName(ID, null);
-      if (box2Acc) {
-        var textboxAcc = box2Acc.firstChild;
-        is(textboxAcc.name, "Label",
-           "Wrong label for anonymous textbox of " + ID);
-
-        var topTextboxAcc = box2Acc.lastChild;
-        is(topTextboxAcc.name, "Top textbox",
-           "Wrong label for anonymous textbox of " + ID);
-      }
-
-
-      //////////////////////////////////////////////////////////////////////////
       // tooltiptext (if nothing above isn't presented then tooltiptext is used)
       testName("box_tooltiptext", "tooltiptext label");
 
 
       //////////////////////////////////////////////////////////////////////////
       // Name from the @title attribute of <toolbaritem/> (original bug 237249).
 
       // Direct child of toolbaritem.
@@ -302,25 +274,16 @@
     <label control="btn_label_3">label3</label>
     <button id="btn_label_3"/>
 
     <label control="btn_label_4">label4</label>
     <label control="btn_label_4">label5</label>
     <button id="btn_label_4"/>
   </hbox>
 
-  <!-- label element, anonymous content -->
-  <box id="box_label_anon1"
-       class="first"
-       role="group"/>
-
-  <box id="box_label_anon2"
-       class="second"
-       role="group"/>
-
   <!-- tooltiptext -->
   <box id="box_tooltiptext"
        role="group"
        tooltiptext="tooltiptext label"/>
 
   <!-- the name from @title of toolbaritem -->
   <!-- and the name from label of a toolbarbutton -->
   <toolbar>
deleted file mode 100644
--- a/browser/actors/ContextMenuSpecialProcessChild.jsm
+++ /dev/null
@@ -1,34 +0,0 @@
-/* vim: set ts=2 sw=2 sts=2 et tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-var EXPORTED_SYMBOLS = ["ContextMenuSpecialProcessChild"];
-
-const { ActorChild } = ChromeUtils.import(
-  "resource://gre/modules/ActorChild.jsm"
-);
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const { E10SUtils } = ChromeUtils.import(
-  "resource://gre/modules/E10SUtils.jsm"
-);
-
-/**
- * This module is a workaround for bug 1555154, where the contextmenu event doesn't
- * cause the JS Window Actor Child to be constructed automatically in the parent
- * process or extension process documents.
- */
-class ContextMenuSpecialProcessChild extends ActorChild {
-  handleEvent(event) {
-    if (
-      Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_DEFAULT ||
-      Services.appinfo.remoteType == E10SUtils.EXTENSION_REMOTE_TYPE
-    ) {
-      this.content
-        .getWindowGlobalChild()
-        .getActor("ContextMenu")
-        .handleEvent(event);
-    }
-  }
-}
--- a/browser/actors/moz.build
+++ b/browser/actors/moz.build
@@ -26,17 +26,16 @@ FINAL_TARGET_FILES.actors += [
     'AboutReaderChild.jsm',
     'BlockedSiteChild.jsm',
     'BrowserTabChild.jsm',
     'BrowserTabParent.jsm',
     'ClickHandlerChild.jsm',
     'ContentSearchChild.jsm',
     'ContextMenuChild.jsm',
     'ContextMenuParent.jsm',
-    'ContextMenuSpecialProcessChild.jsm',
     'DOMFullscreenChild.jsm',
     'FormValidationChild.jsm',
     'FormValidationParent.jsm',
     'LightweightThemeChild.jsm',
     'LinkHandlerChild.jsm',
     'NetErrorChild.jsm',
     'OfflineAppsChild.jsm',
     'PageInfoChild.jsm',
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1627,19 +1627,19 @@ pref("browser.contentblocking.report.loc
 // Enable Protections report's Monitor card by default.
 pref("browser.contentblocking.report.monitor.enabled", true);
 
 // Disable Protections report's Proxy card by default.
 pref("browser.contentblocking.report.proxy.enabled", false);
 
 pref("browser.contentblocking.report.monitor.url", "https://monitor.firefox.com/?entrypoint=protection_report_monitor&utm_source=about-protections");
 pref("browser.contentblocking.report.monitor.sign_in_url", "https://monitor.firefox.com/oauth/init?entrypoint=protection_report_monitor&utm_source=about-protections&email=");
-pref("browser.contentblocking.report.lockwise.url", "https://lockwise.firefox.com/");
+pref("browser.contentblocking.report.lockwise.url", "https://lockwise.firefox.com/?entrypoint=protection_report_lockwise&utm_source=about-protections");
 pref("browser.contentblocking.report.manage_devices.url", "https://accounts.firefox.com/settings/clients");
-pref("browser.contentblocking.report.proxy_extension.url", "https://fpn.firefox.com/browser");
+pref("browser.contentblocking.report.proxy_extension.url", "https://fpn.firefox.com/browser?entrypoint=protection_report_proxy&utm_source=about-protections");
 
 // Protection Report's SUMO urls
 pref("browser.contentblocking.report.monitor.how_it_works.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/monitor-faq");
 pref("browser.contentblocking.report.lockwise.how_it_works.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/password-manager-report");
 pref("browser.contentblocking.report.social.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/social-media-tracking-report");
 pref("browser.contentblocking.report.cookie.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/cross-site-tracking-report");
 pref("browser.contentblocking.report.tracker.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/tracking-content-report");
 pref("browser.contentblocking.report.fingerprinter.url", "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/fingerprinters-report");
--- a/browser/base/content/test/performance/head.js
+++ b/browser/base/content/test/performance/head.js
@@ -776,47 +776,40 @@ async function runUrlbarTest(
               r.x1 >= urlbarRect.left - SHADOW_SIZE &&
               r.x2 <= urlbarRect.right + SHADOW_SIZE &&
               r.y1 >= urlbarRect.top - SHADOW_SIZE
             )
         );
       },
     };
   } else {
+    // Hide the results as we expect many changes there that we don't want to
+    // detect here.
+    URLBar.view.panel.style.visibility = "hidden";
+
     let dropmarkerRect = URLBar.dropmarker.getBoundingClientRect();
     let textBoxRect = URLBar.querySelector(
       "moz-input-box"
     ).getBoundingClientRect();
-    let resultsRect = {
-      top: URLBar.textbox.closest("toolbar").getBoundingClientRect().bottom,
-      right: win.innerWidth,
-      bottom: win.innerHeight,
-      left: 0,
-    };
     expectedRects = {
       filter: rects =>
         rects.filter(
           r =>
             !// We put text into the urlbar so expect its textbox to change.
             (
               (r.x1 >= textBoxRect.left &&
                 r.x2 <= textBoxRect.right &&
                 r.y1 >= textBoxRect.top &&
                 r.y2 <= textBoxRect.bottom) ||
               // The dropmarker is displayed as active during some of the test.
               // dropmarkerRect.left isn't always an integer, hence the - 1 and + 1
               (r.x1 >= dropmarkerRect.left - 1 &&
                 r.x2 <= dropmarkerRect.right + 1 &&
                 r.y1 >= dropmarkerRect.top &&
-                r.y2 <= dropmarkerRect.bottom) ||
-              // We expect many changes in the results view.
-              (r.x1 >= resultsRect.left &&
-                r.x2 <= resultsRect.right &&
-                r.y1 >= resultsRect.top &&
-                r.y2 <= resultsRect.bottom)
+                r.y2 <= dropmarkerRect.bottom)
             )
         ),
     };
   }
 
   info("First opening");
   await withPerfObserver(
     testFn,
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -225,26 +225,16 @@ let LEGACY_ACTORS = {
       ],
       events: {
         ContentSearchClient: { capture: true, wantUntrusted: true },
       },
       messages: ["ContentSearch"],
     },
   },
 
-  ContextMenuSpecialProcess: {
-    child: {
-      module: "resource:///actors/ContextMenuSpecialProcessChild.jsm",
-      events: {
-        contextmenu: { mozSystemGroup: true },
-      },
-    },
-    allFrames: true,
-  },
-
   DOMFullscreen: {
     child: {
       module: "resource:///actors/DOMFullscreenChild.jsm",
       group: "browsers",
       events: {
         "MozDOMFullscreen:Request": {},
         "MozDOMFullscreen:Entered": {},
         "MozDOMFullscreen:NewOrigin": {},
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -1042,18 +1042,19 @@ var UITour = {
 
   /**
    * Called before opening or after closing a highlight or an info tooltip to see if
    * we need to open or close the menu to see the annotation's anchor.
    *
    * @param {ChromeWindow} aWindow the chrome window
    * @param {bool} aShouldOpen true means we should open the menu, otherwise false
    * @param {String} aMenuName "appMenu" or "pageActionPanel"
+   * @param {Object} aOptions Extra config arguments, example `autohide: true`.
    */
-  _setMenuStateForAnnotation(aWindow, aShouldOpen, aMenuName) {
+  _setMenuStateForAnnotation(aWindow, aShouldOpen, aMenuName, aOptions = {}) {
     log.debug("_setMenuStateForAnnotation: Menu is ", aMenuName);
     log.debug(
       "_setMenuStateForAnnotation: Menu is expected to be:",
       aShouldOpen ? "open" : "closed"
     );
     let menu =
       aMenuName == "appMenu"
         ? aWindow.PanelUI.panel
@@ -1066,17 +1067,17 @@ var UITour = {
       return Promise.resolve();
     }
 
     // Actually show or hide the menu
     let promise = null;
     if (aShouldOpen) {
       log.debug("_setMenuStateForAnnotation: Opening the menu");
       promise = new Promise(resolve => {
-        this.showMenu(aWindow, aMenuName, resolve);
+        this.showMenu(aWindow, aMenuName, resolve, aOptions);
       });
     } else if (!this.noautohideMenus.has(aMenuName)) {
       // If the menu was opened explictly by api user through `Mozilla.UITour.showMenu`,
       // it should be closed explictly by api user through `Mozilla.UITour.hideMenu`.
       // So we shouldn't get to here to close it for the highlight/info annotation.
       log.debug("_setMenuStateForAnnotation: Closing the menu");
       promise = new Promise(resolve => {
         menu.addEventListener("popuphidden", resolve, { once: true });
@@ -1086,18 +1087,19 @@ var UITour = {
     return promise;
   },
 
   /**
    * Ensure the target's visibility and the open/close states of menus for the target.
    *
    * @param {ChromeWindow} aChromeWindow The chrome window
    * @param {Object} aTarget The target on which we show highlight or show info.
+   * @param {Object} options Extra config arguments, example `autohide: true`.
    */
-  async _ensureTarget(aChromeWindow, aTarget) {
+  async _ensureTarget(aChromeWindow, aTarget, aOptions = {}) {
     let shouldOpenAppMenu = false;
     let shouldOpenPageActionPanel = false;
     if (this.targetIsInAppMenu(aTarget)) {
       shouldOpenAppMenu = true;
     } else if (this.targetIsInPageActionPanel(aTarget)) {
       shouldOpenPageActionPanel = true;
       // Ensure the panel visibility so as to ensure the visibility of the target
       // element inside the panel otherwise we would be rejected in the below
@@ -1139,17 +1141,18 @@ var UITour = {
     }
 
     let promise = Promise.all(menuClosePromises);
     await promise;
     if (menuToOpen) {
       promise = this._setMenuStateForAnnotation(
         aChromeWindow,
         true,
-        menuToOpen
+        menuToOpen,
+        aOptions
       );
     }
     return promise;
   },
 
   /**
    * The node to which a highlight or notification(-popup) is anchored is sometimes
    * obscured because it may be inside an overflow menu. This function should figure
@@ -1177,19 +1180,20 @@ var UITour = {
   },
 
   /**
    * @param aChromeWindow The chrome window that the highlight is in. Necessary since some targets
    *                      are in a sub-frame so the defaultView is not the same as the chrome
    *                      window.
    * @param aTarget    The element to highlight.
    * @param aEffect    (optional) The effect to use from UITour.highlightEffects or "none".
+   * @param aOptions   (optional) Extra config arguments, example `autohide: true`.
    * @see UITour.highlightEffects
    */
-  async showHighlight(aChromeWindow, aTarget, aEffect = "none") {
+  async showHighlight(aChromeWindow, aTarget, aEffect = "none", aOptions = {}) {
     let showHighlightElement = aAnchorEl => {
       let highlighter = aChromeWindow.document.getElementById(
         "UITourHighlight"
       );
 
       let effect = aEffect;
       if (effect == "random") {
         // Exclude "random" from the randomly selected effects.
@@ -1261,17 +1265,17 @@ var UITour = {
         highlightAnchor,
         "overlap",
         offsetX,
         offsetY
       );
     };
 
     try {
-      await this._ensureTarget(aChromeWindow, aTarget);
+      await this._ensureTarget(aChromeWindow, aTarget, aOptions);
       let anchorEl = await this._correctAnchor(aChromeWindow, aTarget);
       showHighlightElement(anchorEl);
     } catch (e) {
       log.warn(e);
     }
   },
 
   _hideHighlightElement(aWindow) {
@@ -1447,17 +1451,17 @@ var UITour = {
   },
 
   hideInfo(aWindow) {
     this._hideInfoElement(aWindow);
     this._setMenuStateForAnnotation(aWindow, false, "appMenu");
     this._setMenuStateForAnnotation(aWindow, false, "pageActionPanel");
   },
 
-  showMenu(aWindow, aMenuName, aOpenCallback = null) {
+  showMenu(aWindow, aMenuName, aOpenCallback = null, aOptions = {}) {
     log.debug("showMenu:", aMenuName);
     function openMenuButton(aMenuBtn) {
       if (!aMenuBtn || !aMenuBtn.hasMenu() || aMenuBtn.open) {
         if (aOpenCallback) {
           aOpenCallback();
         }
         return;
       }
@@ -1478,17 +1482,19 @@ var UITour = {
         menu.show = () => aWindow.PanelUI.show();
       } else {
         menu.node = aWindow.BrowserPageActions.panelNode;
         menu.onPopupHiding = this.onPageActionPanelHiding;
         menu.onViewShowing = this.onPageActionPanelSubviewShowing;
         menu.show = () => aWindow.BrowserPageActions.showPanel();
       }
 
-      menu.node.setAttribute("noautohide", "true");
+      if (!aOptions.autohide) {
+        menu.node.setAttribute("noautohide", "true");
+      }
       // If the popup is already opened, don't recreate the widget as it may cause a flicker.
       if (menu.node.state != "open") {
         this.recreatePopup(menu.node);
       }
       if (aOpenCallback) {
         menu.node.addEventListener("popupshown", aOpenCallback, { once: true });
       }
       menu.node.addEventListener("popuphidden", menu.onPanelHidden);
--- a/browser/components/urlbar/UrlbarEventBufferer.jsm
+++ b/browser/components/urlbar/UrlbarEventBufferer.jsm
@@ -118,17 +118,17 @@ class UrlbarEventBufferer {
     // Ensure this runs after other results handling code.
     Services.tm.dispatchToMainThread(() => {
       this.replayDeferredEvents(true);
     });
   }
 
   /**
    * Handles DOM events.
-   * @param {Event} event DOM event from the <textbox>.
+   * @param {Event} event DOM event from the input.
    */
   handleEvent(event) {
     if (event.type == "blur") {
       logger.debug("Clearing queue on blur");
       // The input field was blurred, pending events don't matter anymore.
       // Clear the timeout and the queue.
       this._eventsQueue.length = 0;
       if (this._deferringTimeout) {
@@ -136,17 +136,17 @@ class UrlbarEventBufferer {
         this._deferringTimeout = null;
       }
     }
   }
 
   /**
    * Receives DOM events, eventually queues them up, and calls back when it's
    * the right time to handle the event.
-   * @param {Event} event DOM event from the <textbox>.
+   * @param {Event} event DOM event from the input.
    * @param {Function} callback to be invoked when it's the right time to handle
    *        the event.
    */
   maybeDeferEvent(event, callback) {
     if (!callback) {
       throw new Error("Must provide a callback");
     }
     if (this.shouldDeferEvent(event)) {
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -35,25 +35,24 @@ XPCOMUtils.defineLazyServiceGetter(
   "nsIClipboardHelper"
 );
 
 let getBoundsWithoutFlushing = element =>
   element.ownerGlobal.windowUtils.getBoundsWithoutFlushing(element);
 let px = number => number.toFixed(2) + "px";
 
 /**
- * Represents the urlbar <textbox>.
- * Also forwards important textbox properties and methods.
+ * Implements the text input part of the address bar UI.
  */
 class UrlbarInput {
   /**
    * @param {object} options
    *   The initial options for UrlbarInput.
    * @param {object} options.textbox
-   *   The <textbox> element.
+   *   The container element.
    */
   constructor(options = {}) {
     this.textbox = options.textbox;
 
     this.window = this.textbox.ownerGlobal;
     this.document = this.window.document;
     this.window.addEventListener("unload", this);
 
@@ -351,19 +350,18 @@ class UrlbarInput {
     switch (data) {
       case "browser.urlbar.openViewOnFocus":
         this._setOpenViewOnFocus();
         break;
     }
   }
 
   /**
-   * Passes DOM events for the textbox to the _on_<event type> methods.
+   * Passes DOM events to the _on_<event type> methods.
    * @param {Event} event
-   *   DOM event from the <textbox>.
    */
   handleEvent(event) {
     let methodName = "_on_" + event.type;
     if (methodName in this) {
       this[methodName](event);
     } else {
       throw new Error("Unrecognized UrlbarInput event: " + event.type);
     }
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -253,37 +253,25 @@
   transition-timing-function: var(--animation-easing-function);
   transition-duration: var(--panelui-subview-transition-duration);
 }
 
 #protections-popup[mainviewshowing][side=top]::part(arrow) {
   fill: #0A51BF;
 }
 
-:root[lwt-popup-brighttext] #protections-popup[mainviewshowing][side=top]::part(arrow) {
-  fill: #0CB0F5;
-}
-
 #protections-popup-mainView-panel-header-section {
   color: white;
   background: radial-gradient(circle farthest-side at top right, #9059FF, #0250BB);
 }
 
 #protections-popup-mainView-panel-header-section:-moz-locale-dir(rtl) {
   background: radial-gradient(circle farthest-side at top left, #9059FF, #0250BB);
 }
 
-:root[lwt-popup-brighttext] #protections-popup-mainView-panel-header-section {
-  background: radial-gradient(circle farthest-side at top right, #C689FF, #00B3F4);
-}
-
-:root[lwt-popup-brighttext] #protections-popup-mainView-panel-header-section:-moz-locale-dir(rtl) {
-  background: radial-gradient(circle farthest-side at top left, #C689FF, #00B3F4);
-}
-
 #identity-popup-mainView-panel-header-span,
 #protections-popup-mainView-panel-header-span {
   font-weight: 600;
 }
 
 #protections-popup-mainView-panel-header-span {
   margin: var(--vertical-section-padding) 0;
   /* 9px + 26px to compensate for the info button */
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -1820,35 +1820,33 @@ toolbarpaletteitem[place="menu-panel"] >
 #protections-popup-main-header-label {
   margin-inline-start: 30px;
   text-align: center;
 }
 
 #protections-popup #messaging-system-message-container {
   height: 260px;
   overflow: hidden;
-  transition: margin-bottom .25s;
+  transition: height .25s;
   border-top: 1px solid var(--panel-separator-color);
 }
 
 #protections-popup #messaging-system-message-container[disabled] {
-  /* Offset the height when hidden. This makes the panel content
-   * cover the info message and reveal it as it slides down, rather
-   * than the info message growing in height. */
-  margin-bottom: -260px;
+  height: 0;
 }
 
 #protections-popup #messaging-system-message-container[disabled] #protections-popup-message {
-  opacity: 0;
+  /* height + margins */
+  transform: translateY(calc(-100% - 10px));
 }
 
 #protections-popup-message {
   display: flex;
   align-items: flex-end;
-  transition: opacity .25s;
+  transition: transform .25s;
 }
 
 #protections-popup-message.whatsNew-hero-message {
   background-image: url(chrome://browser/skin/controlcenter/hero-message-background.svg);
   background-repeat: no-repeat;
   background-position: top right;
   height: calc(100% - 20px);
   margin: 10px;
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -1,18 +1,21 @@
 %if 0
 /* 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/. */
 %endif
 
+%filter substitution
+%define identityBoxPaddingInline 6px
+%define identityBoxMarginInlineEnd 2px
+
 #identity-box {
-  padding-inline-start: 6px;
-  padding-inline-end: 6px;
-  margin-inline-end: 2px;
+  padding-inline: @identityBoxPaddingInline@;
+  margin-inline-end: @identityBoxMarginInlineEnd@;
 }
 
 #identity-box,
 #tracking-protection-icon-container {
   /* Set default fill for icons in the identity block.
      Individual icons can override this. */
   fill: currentColor;
   fill-opacity: .6;
@@ -217,18 +220,17 @@
 
 @keyframes in-use-blink {
   50% { opacity: 0; }
 }
 
 /* TRACKING PROTECTION ICON */
 
 #tracking-protection-icon-container {
-  padding-inline-start: 6px;
-  padding-inline-end: 6px;
+  padding-inline: @identityBoxPaddingInline@;
   /* Separator */
   border-inline-end: 1px solid var(--urlbar-separator-color);
   border-image: linear-gradient(transparent 15%, var(--urlbar-separator-color) 15%, var(--urlbar-separator-color) 85%, transparent 85%);
   border-image-slice: 1;
   /* This is needed in order to position the blue dot indicator. */
   position: relative;
 }
 
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -1,20 +1,20 @@
 %if 0
 /* 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/. */
 %endif
 
 %filter substitution
 %define urlbarViewPadding 4px
-%define urlbarViewIconMarginEnd 8px
+%define urlbarViewIconMarginEnd (@identityBoxPaddingInline@ + @identityBoxMarginInlineEnd@)
 %define urlbarViewFaviconOffset (@urlbarViewPadding@ + 16px /* type icon width */ + @urlbarViewIconMarginEnd@)
-%define urlbarViewInlineStartPadding (@urlbarBreakoutHorizontalExtend@ - 16px /* type icon width */ - @urlbarViewIconMarginEnd@)
-%define urlbarViewHorizontalMargin 5px
+%define urlbarViewMarginInline 5px
+%define urlbarViewItemPaddingStart (@urlbarBreakoutHorizontalExtend@ - @urlbarViewMarginInline@ - (/* type icon: */ 16px + @urlbarViewIconMarginEnd@) + /* favicon margin: */ @identityBoxPaddingInline@)
 
 :root {
   --autocomplete-popup-background: var(--arrowpanel-background);
   --autocomplete-popup-color: var(--arrowpanel-color);
   --autocomplete-popup-highlight-background: Highlight;
   --autocomplete-popup-highlight-color: HighlightText;
 
   /* From in-content/common.inc.css. */
@@ -43,18 +43,18 @@
 .urlbarView {
   /* Don't handle window drag events in case we are overlapping a toolbar */
   -moz-window-dragging: no-drag;
   text-shadow: none;
   overflow: -moz-hidden-unscrollable;
 }
 
 .urlbarView.megabar {
-  margin-inline: @urlbarViewHorizontalMargin@;
-  width: calc(100% - 2 * @urlbarViewHorizontalMargin@);
+  margin-inline: @urlbarViewMarginInline@;
+  width: calc(100% - 2 * @urlbarViewMarginInline@);
   /* Match urlbar-background's border. */
   border-inline: 1px solid transparent;
 }
 
 .urlbarView:not([hidden]) {
   display: block;
 }
 
@@ -74,18 +74,18 @@
   box-shadow: 0 5px 18px rgba(0, 0, 0, .2);
 }
 
 #urlbar-contextual-tip {
   align-items: center;
   display: flex;
   padding-top: 24px;
   padding-bottom: 24px;
-  padding-inline-start: calc(@urlbarViewInlineStartPadding@);
-  padding-inline-end: calc(@urlbarBreakoutHorizontalExtend@ - @urlbarViewHorizontalMargin@);
+  padding-inline-start: calc(@urlbarViewItemPaddingStart@);
+  padding-inline-end: calc(@urlbarBreakoutHorizontalExtend@ - @urlbarViewMarginInline@);
 }
 
 .urlbarView:not(.megabar) #urlbar-contextual-tip {
   padding-inline-start: var(--item-padding-start, calc(5px + @urlbarViewFaviconOffset@));
   padding-inline-end: var(--item-padding-end, 5px);
 }
 
 #urlbar-contextual-tip-title {
@@ -128,18 +128,18 @@
 }
 
 .urlbarView-row {
   border-radius: 2px;
   fill: currentColor;
   fill-opacity: .6;
   padding-top: 6px;
   padding-bottom: 6px;
-  padding-inline-start: calc(@urlbarViewInlineStartPadding@);
-  padding-inline-end: calc(@urlbarBreakoutHorizontalExtend@ - @urlbarViewHorizontalMargin@);
+  padding-inline-start: calc(@urlbarViewItemPaddingStart@);
+  padding-inline-end: calc(@urlbarBreakoutHorizontalExtend@ - @urlbarViewMarginInline@);
 }
 
 .urlbarView:not(.megabar) .urlbarView-row {
   padding-inline-start: calc(var(--item-padding-start, calc(5px + @urlbarViewFaviconOffset@)) - @urlbarViewFaviconOffset@);
   padding-inline-end: var(--item-padding-end, 5px);
 }
 
 :root[uidensity=touch] .urlbarView-row {
@@ -177,17 +177,17 @@
   color: var(--autocomplete-popup-highlight-color);
   fill-opacity: 1;
 }
 
 .urlbarView-type-icon,
 .urlbarView-favicon {
   min-width: 16px;
   height: 16px;
-  margin-inline-end: @urlbarViewIconMarginEnd@;
+  margin-inline-end: calc(@urlbarViewIconMarginEnd@);
   background-repeat: no-repeat;
   background-size: contain;
   -moz-context-properties: fill, fill-opacity;
 }
 
 .urlbarView-row[type=tip] > .urlbarView-row-inner > .urlbarView-favicon {
   min-width: 24px;
   height: 24px;
@@ -217,17 +217,17 @@
 
 .urlbarView-tip-help:hover:active {
   background-color: var(--in-content-button-background-active);
 }
 
 #urlbar-contextual-tip-icon {
   min-width: 24px;
   height: 24px;
-  margin-inline-end: @urlbarViewIconMarginEnd@;
+  margin-inline-end: calc(@urlbarViewIconMarginEnd@);
   background-repeat: no-repeat;
   background-size: contain;
   -moz-context-properties: fill, fill-opacity;
 }
 
 @media (min-resolution: 2dppx) {
   .urlbarView-type-icon,
   .urlbarView-favicon,
@@ -384,17 +384,17 @@
 #urlbar .search-one-offs:not([hidden]) {
   display: flex;
   align-items: start;
   padding-block: 16px;
 }
 
 .urlbarView.megabar .search-one-offs {
   padding-inline-start: @urlbarBreakoutHorizontalExtend@;
-  padding-inline-end: calc(@urlbarBreakoutHorizontalExtend@ - @urlbarViewHorizontalMargin@);
+  padding-inline-end: calc(@urlbarBreakoutHorizontalExtend@ - @urlbarViewMarginInline@);
 }
 
 #urlbar:not(.megabar) .search-one-offs {
   padding-inline-start: var(--item-padding-start, 5px);
   padding-inline-end: var(--item-padding-end, 5px);
 }
 
 #urlbar .search-panel-one-offs {
--- a/devtools/client/framework/toolbox-hosts.js
+++ b/devtools/client/framework/toolbox-hosts.js
@@ -429,22 +429,26 @@ function focusTab(tab) {
   browserWindow.focus();
   browserWindow.gBrowser.selectedTab = tab;
 }
 
 /**
  * Create an iframe that can be used to load DevTools via about:devtools-toolbox.
  */
 function createDevToolsFrame(doc, className) {
-  const frame = doc.createXULElement("iframe");
+  let frame;
+  if (Services.prefs.getBoolPref("devtools.toolbox.content-frame", false)) {
+    frame = doc.createXULElement("browser");
+    frame.setAttribute("type", "content");
+  } else {
+    frame = doc.createXULElement("iframe");
+  }
+
   frame.flex = 1; // Required to be able to shrink when the window shrinks
   frame.className = className;
-  if (Services.prefs.getBoolPref("devtools.toolbox.content-frame", false)) {
-    frame.setAttribute("type", "content");
-  }
   frame.tooltip = "aHTMLTooltip";
   return frame;
 }
 
 exports.Hosts = {
   bottom: BottomHost,
   left: LeftHost,
   right: RightHost,
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -265,16 +265,17 @@ function Toolbox(
   this._onPickerStopped = this._onPickerStopped.bind(this);
   this._onPickerCanceled = this._onPickerCanceled.bind(this);
   this._onPickerPicked = this._onPickerPicked.bind(this);
   this._onPickerPreviewed = this._onPickerPreviewed.bind(this);
   this._onInspectObject = this._onInspectObject.bind(this);
   this._onNewSelectedNodeFront = this._onNewSelectedNodeFront.bind(this);
   this._onToolSelected = this._onToolSelected.bind(this);
   this._onContextMenu = this._onContextMenu.bind(this);
+  this._onMouseDown = this._onMouseDown.bind(this);
   this.updateToolboxButtonsVisibility = this.updateToolboxButtonsVisibility.bind(
     this
   );
   this.updateToolboxButtons = this.updateToolboxButtons.bind(this);
   this.selectTool = this.selectTool.bind(this);
   this._pingTelemetrySelectTool = this._pingTelemetrySelectTool.bind(this);
   this.toggleSplitConsole = this.toggleSplitConsole.bind(this);
   this.toggleOptions = this.toggleOptions.bind(this);
@@ -934,16 +935,17 @@ Toolbox.prototype = {
       "keypress",
       this._splitConsoleOnKeypress
     );
     this._chromeEventHandler.addEventListener("focus", this._onFocus, true);
     this._chromeEventHandler.addEventListener(
       "contextmenu",
       this._onContextMenu
     );
+    this._chromeEventHandler.addEventListener("mousedown", this._onMouseDown);
   },
 
   _removeChromeEventHandlerEvents: function() {
     if (!this._chromeEventHandler) {
       return;
     }
 
     // Remove shortcuts and window-host-shortcuts that use the ChromeEventHandler as
@@ -955,16 +957,20 @@ Toolbox.prototype = {
       "keypress",
       this._splitConsoleOnKeypress
     );
     this._chromeEventHandler.removeEventListener("focus", this._onFocus, true);
     this._chromeEventHandler.removeEventListener(
       "contextmenu",
       this._onContextMenu
     );
+    this._chromeEventHandler.removeEventListener(
+      "mousedown",
+      this._onMouseDown
+    );
 
     this._chromeEventHandler = null;
   },
 
   _addShortcuts: function() {
     // Create shortcuts instance for the toolbox
     if (!this.shortcuts) {
       this.shortcuts = new KeyShortcuts({
@@ -1109,29 +1115,53 @@ Toolbox.prototype = {
       this._windowHostShortcuts = null;
     }
   },
 
   _onContextMenu: function(e) {
     // Handle context menu events in standard input elements: <input> and <textarea>.
     // Also support for custom input elements using .devtools-input class
     // (e.g. CodeMirror instances).
-    if (
+    const isInInput =
       e.originalTarget.closest("input[type=text]") ||
       e.originalTarget.closest("input[type=search]") ||
       e.originalTarget.closest("input:not([type])") ||
       e.originalTarget.closest(".devtools-input") ||
-      e.originalTarget.closest("textarea")
+      e.originalTarget.closest("textarea");
+
+    const doc = e.originalTarget.ownerDocument;
+    const isHTMLPanel = doc.documentElement.namespaceURI === HTML_NS;
+
+    if (
+      // Context-menu events on input elements will use a custom context menu.
+      isInInput ||
+      // Context-menu events from HTML panels should not trigger the default
+      // browser context menu for HTML documents.
+      isHTMLPanel
     ) {
       e.stopPropagation();
       e.preventDefault();
+    }
+
+    if (isInInput) {
       this.openTextBoxContextMenu(e.screenX, e.screenY);
     }
   },
 
+  _onMouseDown: function(e) {
+    const isMiddleClick = e.button === 1;
+    if (isMiddleClick) {
+      // Middle clicks will trigger the scroll lock feature to turn on.
+      // When the DevTools toolbox was running in an <iframe>, this behavior was
+      // disabled by default. When running in a <browser> element, we now need
+      // to catch and preventDefault() on those events.
+      e.preventDefault();
+    }
+  },
+
   _getDebugTargetData: function() {
     const url = new URL(this.win.location);
     const searchParams = new this.win.URLSearchParams(url.search);
 
     const targetType = searchParams.get("type") || DEBUG_TARGET_TYPES.TAB;
 
     const remoteId = searchParams.get("remoteId");
     const runtimeInfo = remoteClientManager.getRuntimeInfoByRemoteId(remoteId);
--- a/devtools/client/inspector/markup/views/markup-container.js
+++ b/devtools/client/inspector/markup/views/markup-container.js
@@ -557,16 +557,25 @@ MarkupContainer.prototype = {
     // mouseup (through bubbling) when clicking on a non focusable node in the
     // line. So, if the click happened outside of a focusable element, do
     // prevent the default behavior, so that the tagname or textcontent gains
     // focus.
     if (!target.closest(".editor [tabindex]")) {
       event.preventDefault();
     }
 
+    // Middle clicks will trigger the scroll lock feature to turn on.
+    // The toolbox is normally responsible for calling preventDefault when
+    // needed, but we prevent markup-view mousedown events from bubbling up (via
+    // stopPropagation). So we have to preventDefault here as well in order to
+    // avoid this issue.
+    if (isMiddleClick) {
+      event.preventDefault();
+    }
+
     // Follow attribute links if middle or meta click.
     if (isMiddleClick || isMetaClick) {
       const link = target.dataset.link;
       const type = target.dataset.type;
       // Make container tabbable descendants not tabbable (by default).
       this.canFocus = false;
       this.markup.followAttributeLink(type, link);
       return;
--- a/devtools/docs/backend/protocol.js.md
+++ b/devtools/docs/backend/protocol.js.md
@@ -471,27 +471,34 @@ Here's how the implementation would look
 
 Now you can listen to events on a front:
 
     front.on("good-news", news => {
       console.log(`Got some good news: ${news}\n`);
     });
     front.giveGoodNews().then(() => { console.log("request returned.") });
 
-You might want to update your front's state when an event is fired, before emitting it against the front.  You can use `preEvent` in the front definition for that:
+If you want to modify the argument that will be passed to event listeners callbacks, you
+can use `before(eventName, fn)` in the front definition. This can only be used once for a
+given `eventName`. The `fn` function will be called before emitting the event via
+the EventEmitter API on the Front, and its return value will be passed to the event 
+listener callbacks. If `fn` is async, the event will only be emitted after `fn` call resolves.
 
-    countGoodNews: protocol.preEvent("good-news", function (news) {
-        this.amountOfGoodNews++;
+    // In front file, most probably in the constructor:
+    this.before("good-news", function(news) {
+      return news.join(" - ");
     });
 
-You can have events wait until an asynchronous action completes before firing by returning a promise. If you have multiple preEvents defined for a specific event, and at least one fires asynchronously, then all preEvents most resolve before all events are fired.
+    // In any consumer
+    front.on("good-news", function(news) {
+      console.log(news);
+    });
 
-    countGoodNews: protocol.preEvent("good-news", function (news) {
-        return this.updateGoodNews().then(() => this.amountOfGoodNews++);
-    });
+So if the server sent the following array: `[1, 2, 3]`, the console.log in the consumer
+would print `1 - 2 - 3`.
 
 On a somewhat related note, not every method needs to be request/response.  Just like an actor can emit a one-way event, a method can be marked as a one-way request.  Maybe we don't care about giveGoodNews returning anything:
 
     // spec:
     methods: {
       giveGoodNews: {
         request: { news: Arg(0, "string") },
         oneway: true
--- a/docshell/base/BrowsingContext.cpp
+++ b/docshell/base/BrowsingContext.cpp
@@ -909,16 +909,20 @@ Nullable<WindowProxyHolder> BrowsingCont
 }
 
 void BrowsingContext::PostMessageMoz(JSContext* aCx,
                                      JS::Handle<JS::Value> aMessage,
                                      const nsAString& aTargetOrigin,
                                      const Sequence<JSObject*>& aTransfer,
                                      nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aError) {
+  if (mIsDiscarded) {
+    return;
+  }
+
   RefPtr<BrowsingContext> sourceBc;
   PostMessageData data;
   data.targetOrigin() = aTargetOrigin;
   data.subjectPrincipal() = &aSubjectPrincipal;
   RefPtr<nsGlobalWindowInner> callerInnerWindow;
   if (!nsGlobalWindowOuter::GatherPostMessageData(
           aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
           getter_AddRefs(data.targetOriginURI()),
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -12096,17 +12096,17 @@ already_AddRefed<Document> Document::Cre
       // Font faces created with the JS API will not be reflected in the
       // stylesheets and need to be copied over to the cloned document.
       if (const FontFaceSet* set = GetFonts()) {
         set->CopyNonRuleFacesTo(clonedDoc->Fonts());
       }
 
       clonedDoc->mReferrerInfo =
           static_cast<dom::ReferrerInfo*>(mReferrerInfo.get())->Clone();
-      clonedDoc->mPreloadReferrerInfo = clonedDoc->mPreloadReferrerInfo;
+      clonedDoc->mPreloadReferrerInfo = clonedDoc->mReferrerInfo;
     }
   }
   mCreatingStaticClone = false;
   return clonedDoc.forget();
 }
 
 void Document::UnlinkOriginalDocumentIfStatic() {
   if (IsStaticDocument() && mOriginalDocument) {
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -520,76 +520,16 @@ void Element::ClearStyleStateLocks() {
   StyleStateLocks locks = LockedStyleStates();
 
   DeleteProperty(nsGkAtoms::lockedStyleStates);
   ClearHasLockedStyleStates();
 
   NotifyStyleStateChange(locks.mLocks);
 }
 
-#ifdef MOZ_XBL
-static bool MayNeedToLoadXBLBinding(const Element& aElement) {
-  // Clean this up in https://bugzilla.mozilla.org/show_bug.cgi?id=1585823
-  return false;
-}
-#endif
-
-JSObject* Element::WrapObject(JSContext* aCx,
-                              JS::Handle<JSObject*> aGivenProto) {
-  JS::Rooted<JSObject*> obj(aCx, nsINode::WrapObject(aCx, aGivenProto));
-  if (!obj) {
-    return nullptr;
-  }
-
-#ifdef MOZ_XBL
-  if (!MayNeedToLoadXBLBinding(*this)) {
-    return obj;
-  }
-
-  {
-    RefPtr<ComputedStyle> style =
-        nsComputedDOMStyle::GetComputedStyleNoFlush(this, nullptr);
-    if (!style) {
-      return obj;
-    }
-
-    // We have a binding that must be installed.
-    const StyleUrlOrNone& computedBinding = style->StyleDisplay()->mBinding;
-    if (!computedBinding.IsUrl()) {
-      return obj;
-    }
-
-    auto& url = computedBinding.AsUrl();
-    nsCOMPtr<nsIURI> uri = url.GetURI();
-    nsCOMPtr<nsIPrincipal> principal = url.ExtraData().Principal();
-
-    nsXBLService* xblService = nsXBLService::GetInstance();
-    if (!xblService) {
-      dom::Throw(aCx, NS_ERROR_NOT_AVAILABLE);
-      return nullptr;
-    }
-
-    RefPtr<nsXBLBinding> binding;
-    xblService->LoadBindings(this, uri, principal, getter_AddRefs(binding));
-
-    if (binding) {
-      if (nsContentUtils::IsSafeToRunScript()) {
-        binding->ExecuteAttachedHandler();
-      } else {
-        nsContentUtils::AddScriptRunner(
-            NewRunnableMethod("nsXBLBinding::ExecuteAttachedHandler", binding,
-                              &nsXBLBinding::ExecuteAttachedHandler));
-      }
-    }
-  }
-#endif
-
-  return obj;
-}
-
 /* virtual */
 nsINode* Element::GetScopeChainParent() const { return OwnerDoc(); }
 
 nsDOMTokenList* Element::ClassList() {
   Element::nsDOMSlots* slots = DOMSlots();
 
   if (!slots->mClassList) {
     slots->mClassList = new nsDOMTokenList(this, nsGkAtoms::_class);
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1483,18 +1483,16 @@ class Element : public FragmentOrElement
   static CORSMode StringToCORSMode(const nsAString& aValue);
 
   /**
    * Return the CORS mode for a given nsAttrValue (which may be null,
    * but if not should have been parsed via ParseCORSValue).
    */
   static CORSMode AttrValueToCORSMode(const nsAttrValue* aValue);
 
-  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) final;
-
   nsINode* GetScopeChainParent() const override;
 
   /**
    * Locate a TextEditor rooted at this content node, if there is one.
    */
   mozilla::TextEditor* GetTextEditorInternal();
 
   /**
--- a/dom/base/InProcessBrowserChildMessageManager.cpp
+++ b/dom/base/InProcessBrowserChildMessageManager.cpp
@@ -18,21 +18,40 @@
 #include "xpcpublic.h"
 #include "nsIMozBrowserFrame.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/dom/ChromeMessageSender.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/WindowProxyHolder.h"
+#include "mozilla/dom/JSWindowActorService.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
+/* static */
+already_AddRefed<InProcessBrowserChildMessageManager>
+InProcessBrowserChildMessageManager::Create(nsDocShell* aShell,
+                                            nsIContent* aOwner,
+                                            nsFrameMessageManager* aChrome) {
+  RefPtr<InProcessBrowserChildMessageManager> mm =
+      new InProcessBrowserChildMessageManager(aShell, aOwner, aChrome);
+
+  NS_ENSURE_TRUE(mm->Init(), nullptr);
+
+  if (XRE_IsParentProcess()) {
+    RefPtr<JSWindowActorService> wasvc = JSWindowActorService::GetSingleton();
+    wasvc->RegisterChromeEventTarget(mm);
+  }
+
+  return mm.forget();
+}
+
 bool InProcessBrowserChildMessageManager::DoSendBlockingMessage(
     JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData,
     JS::Handle<JSObject*> aCpows, nsIPrincipal* aPrincipal,
     nsTArray<StructuredCloneData>* aRetVal, bool aIsSync) {
   SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
   queue->Flush();
 
   if (mChromeMessageManager) {
@@ -96,16 +115,20 @@ InProcessBrowserChildMessageManager::InP
   if (browserFrame) {
     mIsBrowserFrame = browserFrame->GetReallyIsBrowser();
   } else {
     mIsBrowserFrame = false;
   }
 }
 
 InProcessBrowserChildMessageManager::~InProcessBrowserChildMessageManager() {
+  if (XRE_IsParentProcess()) {
+    JSWindowActorService::UnregisterChromeEventTarget(this);
+  }
+
   mAnonymousGlobalScopes.Clear();
   mozilla::DropJSObjects(this);
 }
 
 // This method isn't automatically forwarded safely because it's notxpcom, so
 // the IDL binding doesn't know what value to return.
 void InProcessBrowserChildMessageManager::MarkForCC() {
   MarkScopesForCC();
--- a/dom/base/InProcessBrowserChildMessageManager.h
+++ b/dom/base/InProcessBrowserChildMessageManager.h
@@ -42,24 +42,17 @@ class InProcessBrowserChildMessageManage
   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
 
  private:
   InProcessBrowserChildMessageManager(nsDocShell* aShell, nsIContent* aOwner,
                                       nsFrameMessageManager* aChrome);
 
  public:
   static already_AddRefed<InProcessBrowserChildMessageManager> Create(
-      nsDocShell* aShell, nsIContent* aOwner, nsFrameMessageManager* aChrome) {
-    RefPtr<InProcessBrowserChildMessageManager> mm =
-        new InProcessBrowserChildMessageManager(aShell, aOwner, aChrome);
-
-    NS_ENSURE_TRUE(mm->Init(), nullptr);
-
-    return mm.forget();
-  }
+      nsDocShell* aShell, nsIContent* aOwner, nsFrameMessageManager* aChrome);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
       InProcessBrowserChildMessageManager, DOMEventTargetHelper)
 
   void MarkForCC();
 
   virtual JSObject* WrapObject(JSContext* aCx,
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -89,18 +89,18 @@ void ShadowRoot::AddSizeOfExcludingThis(
                                         size_t* aNodeSize) const {
   DocumentFragment::AddSizeOfExcludingThis(aSizes, aNodeSize);
   DocumentOrShadowRoot::AddSizeOfExcludingThis(aSizes);
   aSizes.mLayoutShadowDomAuthorStyles += Servo_AuthorStyles_SizeOfIncludingThis(
       ShadowRootAuthorStylesMallocSizeOf,
       ShadowRootAuthorStylesMallocEnclosingSizeOf, mServoStyles.get());
 }
 
-JSObject* ShadowRoot::WrapObject(JSContext* aCx,
-                                 JS::Handle<JSObject*> aGivenProto) {
+JSObject* ShadowRoot::WrapNode(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) {
   return mozilla::dom::ShadowRoot_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
   size_t sheetCount = aOther->SheetCount();
   for (size_t i = 0; i < sheetCount; ++i) {
     StyleSheet* sheet = aOther->SheetAt(i);
     if (sheet->IsApplicable()) {
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -163,18 +163,17 @@ class ShadowRoot final : public Document
   const RawServoAuthorStyles* GetServoStyles() const {
     return mServoStyles.get();
   }
 
   RawServoAuthorStyles* GetServoStyles() { return mServoStyles.get(); }
 
   mozilla::ServoStyleRuleMap& ServoStyleRuleMap();
 
-  JSObject* WrapObject(JSContext* aCx,
-                       JS::Handle<JSObject*> aGivenProto) override;
+  JSObject* WrapNode(JSContext*, JS::Handle<JSObject*> aGivenProto) final;
 
   void AddToIdTable(Element* aElement, nsAtom* aId);
   void RemoveFromIdTable(Element* aElement, nsAtom* aId);
 
   // WebIDL methods.
   using mozilla::dom::DocumentOrShadowRoot::GetElementById;
 
   Element* GetActiveElement();
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -434,18 +434,17 @@ class nsINode : public mozilla::dom::Eve
   /**
    * Return this node as a document fragment. Asserts IsDocumentFragment().
    *
    * This is defined inline in DocumentFragment.h.
    */
   inline mozilla::dom::DocumentFragment* AsDocumentFragment();
   inline const mozilla::dom::DocumentFragment* AsDocumentFragment() const;
 
-  virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aGivenProto) override;
+  JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) final;
 
   /**
    * Hook for constructing JS::ubi::Concrete specializations for memory
    * reporting. Specializations are defined in NodeUbiReporting.h.
    */
   virtual void ConstructUbiNode(void* storage) = 0;
 
   /**
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -39,17 +39,19 @@ nsWindowRoot::nsWindowRoot(nsPIDOMWindow
   mShowFocusRings = StaticPrefs::browser_display_show_focus_rings();
 }
 
 nsWindowRoot::~nsWindowRoot() {
   if (mListenerManager) {
     mListenerManager->Disconnect();
   }
 
-  JSWindowActorService::UnregisterWindowRoot(this);
+  if (XRE_IsContentProcess()) {
+    JSWindowActorService::UnregisterChromeEventTarget(this);
+  }
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsWindowRoot, mWindow, mListenerManager,
                                       mParent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsWindowRoot)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
@@ -315,15 +317,15 @@ void nsWindowRoot::EnumerateBrowsers(Bro
   }
 }
 
 ///////////////////////////////////////////////////////////////////////////////////
 
 already_AddRefed<EventTarget> NS_NewWindowRoot(nsPIDOMWindowOuter* aWindow) {
   nsCOMPtr<EventTarget> result = new nsWindowRoot(aWindow);
 
-  RefPtr<JSWindowActorService> wasvc = JSWindowActorService::GetSingleton();
-  if (wasvc) {
-    wasvc->RegisterWindowRoot(result);
+  if (XRE_IsContentProcess()) {
+    RefPtr<JSWindowActorService> wasvc = JSWindowActorService::GetSingleton();
+    wasvc->RegisterChromeEventTarget(result);
   }
 
   return result.forget();
 }
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -227,17 +227,17 @@ class nsWrapperCache {
       SetWrapperFlags(WRAPPER_BIT_PRESERVED);
     } else {
       UnsetWrapperFlags(WRAPPER_BIT_PRESERVED);
     }
   }
 
   void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure) {
     if (PreservingWrapper() && mWrapper) {
-      aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
+      aCallbacks.Trace(this, "Preserved wrapper", aClosure);
     }
   }
 
   /*
    * The following methods for getting and manipulating flags allow the unused
    * bits of mFlags to be used by derived classes.
    */
 
@@ -299,23 +299,23 @@ class nsWrapperCache {
 #ifdef DEBUG
     // Make sure the cycle collector will be able to traverse to the wrapper.
     CheckCCWrapperTraversal(aScriptObjectHolder, aTracer);
 #endif
   }
 
   void ReleaseWrapper(void* aScriptObjectHolder);
 
- protected:
   void TraceWrapper(JSTracer* aTrc, const char* name) {
     if (mWrapper) {
       js::UnsafeTraceManuallyBarrieredEdge(aTrc, &mWrapper, name);
     }
   }
 
+ protected:
   void PoisonWrapper() {
     if (mWrapper) {
       // Set the pointer to a value that will cause a crash if it is
       // dereferenced.
       mWrapper = reinterpret_cast<JSObject*>(1);
     }
   }
 
--- a/dom/chrome-webidl/JSWindowActor.webidl
+++ b/dom/chrome-webidl/JSWindowActor.webidl
@@ -151,16 +151,18 @@ dictionary WindowActorSidedOptions {
   ByteString moduleURI;
 };
 
 dictionary WindowActorChildOptions : WindowActorSidedOptions {
   /**
    * Events which this actor wants to be listening to. When these events fire,
    * it will trigger actor creation, and then forward the event to the actor.
    *
+   * NOTE: Listeners are not attached for windows loaded in chrome docshells.
+   *
    * NOTE: `once` option is not support due to we register listeners in a shared
    * location.
    */
   record<DOMString, AddEventListenerOptions> events;
 
  /**
   * An array of observer topics to listen to. An observer will be added for each
   * topic in the list.
--- a/dom/ipc/JSWindowActorService.cpp
+++ b/dom/ipc/JSWindowActorService.cpp
@@ -238,27 +238,27 @@ NS_IMETHODIMP JSWindowActorProtocol::Obs
                                JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
   RefPtr<MozObserverCallback> observerCallback =
       new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr);
   observerCallback->Observe(aSubject, nsDependentCString(aTopic),
                             aData ? nsDependentString(aData) : VoidString());
   return NS_OK;
 }
 
-void JSWindowActorProtocol::RegisterListenersFor(EventTarget* aRoot) {
-  EventListenerManager* elm = aRoot->GetOrCreateListenerManager();
+void JSWindowActorProtocol::RegisterListenersFor(EventTarget* aTarget) {
+  EventListenerManager* elm = aTarget->GetOrCreateListenerManager();
 
   for (auto& event : mChild.mEvents) {
     elm->AddEventListenerByType(EventListenerHolder(this), event.mName,
                                 event.mFlags, event.mPassive);
   }
 }
 
-void JSWindowActorProtocol::UnregisterListenersFor(EventTarget* aRoot) {
-  EventListenerManager* elm = aRoot->GetOrCreateListenerManager();
+void JSWindowActorProtocol::UnregisterListenersFor(EventTarget* aTarget) {
+  EventListenerManager* elm = aTarget->GetOrCreateListenerManager();
 
   for (auto& event : mChild.mEvents) {
     elm->RemoveEventListenerByType(EventListenerHolder(this), event.mName,
                                    event.mFlags);
   }
 }
 
 void JSWindowActorProtocol::AddObservers() {
@@ -383,19 +383,19 @@ void JSWindowActorService::RegisterWindo
 
   // Send information about the newly added entry to every existing content
   // process.
   AutoTArray<JSWindowActorInfo, 1> ipcInfos{proto->ToIPC()};
   for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
     Unused << cp->SendInitJSWindowActorInfos(ipcInfos);
   }
 
-  // Register event listeners for any existing window roots.
-  for (EventTarget* root : mRoots) {
-    proto->RegisterListenersFor(root);
+  // Register event listeners for any existing chrome targets.
+  for (EventTarget* target : mChromeEventTargets) {
+    proto->RegisterListenersFor(target);
   }
 
   // Add observers to the protocol.
   proto->AddObservers();
 }
 
 void JSWindowActorService::UnregisterWindowActor(const nsAString& aName) {
   nsAutoString name(aName);
@@ -405,19 +405,19 @@ void JSWindowActorService::UnregisterWin
     // If we're in the parent process, also unregister the window actor in all
     // live content processes.
     if (XRE_IsParentProcess()) {
       for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
         Unused << cp->SendUnregisterJSWindowActor(name);
       }
     }
 
-    // Remove listeners for this actor from each of our window roots.
-    for (EventTarget* root : mRoots) {
-      proto->UnregisterListenersFor(root);
+    // Remove listeners for this actor from each of our chrome targets.
+    for (EventTarget* target : mChromeEventTargets) {
+      proto->UnregisterListenersFor(target);
     }
 
     // Remove observers for this actor from observer serivce.
     proto->RemoveObservers();
   }
 }
 
 void JSWindowActorService::LoadJSWindowActorInfos(
@@ -426,19 +426,19 @@ void JSWindowActorService::LoadJSWindowA
   MOZ_ASSERT(XRE_IsContentProcess());
 
   for (uint32_t i = 0, len = aInfos.Length(); i < len; i++) {
     // Create our JSWindowActorProtocol, register it in mDescriptors.
     RefPtr<JSWindowActorProtocol> proto =
         JSWindowActorProtocol::FromIPC(aInfos[i]);
     mDescriptors.Put(aInfos[i].name(), proto);
 
-    // Register listeners for each window root.
-    for (EventTarget* root : mRoots) {
-      proto->RegisterListenersFor(root);
+    // Register listeners for each chrome target.
+    for (EventTarget* target : mChromeEventTargets) {
+      proto->RegisterListenersFor(target);
     }
 
     // Add observers for each actor.
     proto->AddObservers();
   }
 }
 
 void JSWindowActorService::GetJSWindowActorInfos(
@@ -446,31 +446,31 @@ void JSWindowActorService::GetJSWindowAc
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(XRE_IsParentProcess());
 
   for (auto iter = mDescriptors.ConstIter(); !iter.Done(); iter.Next()) {
     aInfos.AppendElement(iter.Data()->ToIPC());
   }
 }
 
-void JSWindowActorService::RegisterWindowRoot(EventTarget* aRoot) {
-  MOZ_ASSERT(!mRoots.Contains(aRoot));
-  mRoots.AppendElement(aRoot);
+void JSWindowActorService::RegisterChromeEventTarget(EventTarget* aTarget) {
+  MOZ_ASSERT(!mChromeEventTargets.Contains(aTarget));
+  mChromeEventTargets.AppendElement(aTarget);
 
   // Register event listeners on the newly added Window Root.
   for (auto iter = mDescriptors.Iter(); !iter.Done(); iter.Next()) {
-    iter.Data()->RegisterListenersFor(aRoot);
+    iter.Data()->RegisterListenersFor(aTarget);
   }
 }
 
 /* static */
-void JSWindowActorService::UnregisterWindowRoot(EventTarget* aRoot) {
+void JSWindowActorService::UnregisterChromeEventTarget(EventTarget* aTarget) {
   if (gJSWindowActorService) {
-    // NOTE: No need to unregister listeners here, as the root is going away.
-    gJSWindowActorService->mRoots.RemoveElement(aRoot);
+    // NOTE: No need to unregister listeners here, as the target is going away.
+    gJSWindowActorService->mChromeEventTargets.RemoveElement(aTarget);
   }
 }
 
 already_AddRefed<JSWindowActorProtocol> JSWindowActorService::GetProtocol(
     const nsAString& aName) {
   return mDescriptors.Get(aName);
 }
 
--- a/dom/ipc/JSWindowActorService.h
+++ b/dom/ipc/JSWindowActorService.h
@@ -65,18 +65,18 @@ class JSWindowActorProtocol final : publ
   struct ChildSide : public Sided {
     nsTArray<EventDecl> mEvents;
     nsTArray<nsCString> mObservers;
   };
 
   const ParentSide& Parent() const { return mParent; }
   const ChildSide& Child() const { return mChild; }
 
-  void RegisterListenersFor(EventTarget* aRoot);
-  void UnregisterListenersFor(EventTarget* aRoot);
+  void RegisterListenersFor(EventTarget* aTarget);
+  void UnregisterListenersFor(EventTarget* aTarget);
   void AddObservers();
   void RemoveObservers();
   bool Matches(BrowsingContext* aBrowsingContext, nsIURI* aURI,
                const nsAString& aRemoteType);
 
  private:
   explicit JSWindowActorProtocol(const nsAString& aName) : mName(aName) {}
   extensions::MatchPatternSet* GetURIMatcher();
@@ -109,28 +109,28 @@ class JSWindowActorService final {
 
   // Register child's Window Actor from JSWindowActorInfos for content process.
   void LoadJSWindowActorInfos(nsTArray<JSWindowActorInfo>& aInfos);
 
   // Get the named of Window Actor and the child's WindowActorOptions
   // from mDescriptors to JSWindowActorInfos.
   void GetJSWindowActorInfos(nsTArray<JSWindowActorInfo>& aInfos);
 
-  // Register or unregister a WindowRoot object from this JSWindowActorService.
-  void RegisterWindowRoot(EventTarget* aRoot);
+  // Register or unregister a chrome event target.
+  void RegisterChromeEventTarget(EventTarget* aTarget);
 
   // NOTE: This method is static, as it may be called during shutdown.
-  static void UnregisterWindowRoot(EventTarget* aRoot);
+  static void UnregisterChromeEventTarget(EventTarget* aTarget);
 
   already_AddRefed<JSWindowActorProtocol> GetProtocol(const nsAString& aName);
 
  private:
   JSWindowActorService();
   ~JSWindowActorService();
 
-  nsTArray<EventTarget*> mRoots;
+  nsTArray<EventTarget*> mChromeEventTargets;
   nsRefPtrHashtable<nsStringHashKey, JSWindowActorProtocol> mDescriptors;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_JSWindowActorService_h
\ No newline at end of file
--- a/dom/security/nsContentSecurityUtils.cpp
+++ b/dom/security/nsContentSecurityUtils.cpp
@@ -550,16 +550,18 @@ void nsContentSecurityUtils::AssertAbout
     // about:blank is a special about page -> no CSP
     NS_LITERAL_CSTRING("about:blank"),
     // about:srcdoc is a special about page -> no CSP
     NS_LITERAL_CSTRING("about:srcdoc"),
     // about:sync-log displays plain text only -> no CSP
     NS_LITERAL_CSTRING("about:sync-log"),
     // about:printpreview displays plain text only -> no CSP
     NS_LITERAL_CSTRING("about:printpreview"),
+    // about:logo just displays the firefox logo -> no CSP
+    NS_LITERAL_CSTRING("about:logo"),
 #  if defined(ANDROID)
     NS_LITERAL_CSTRING("about:config"),
 #  endif
   };
 
   for (const nsLiteralCString& allowlistEntry : sAllowedAboutPagesWithNoCSP) {
     // please note that we perform a substring match here on purpose,
     // so we don't have to deal and parse out all the query arguments
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -149,18 +149,18 @@ void record_telemetry_time(mozilla::wr::
     case mozilla::wr::TelemetryProbe::SceneBuildTime:
       mozilla::Telemetry::Accumulate(mozilla::Telemetry::WR_SCENEBUILD_TIME,
                                      time_ms);
       break;
     case mozilla::wr::TelemetryProbe::SceneSwapTime:
       mozilla::Telemetry::Accumulate(mozilla::Telemetry::WR_SCENESWAP_TIME,
                                      time_ms);
       break;
-    case mozilla::wr::TelemetryProbe::RenderTime:
-      mozilla::Telemetry::Accumulate(mozilla::Telemetry::WR_RENDER_TIME,
+    case mozilla::wr::TelemetryProbe::FrameBuildTime:
+      mozilla::Telemetry::Accumulate(mozilla::Telemetry::WR_FRAMEBUILD_TIME,
                                      time_ms);
       break;
     default:
       MOZ_ASSERT(false);
       break;
   }
 }
 
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -509,17 +509,17 @@ fn get_proc_address(glcontext_ptr: *mut 
 
     symbol as *const _
 }
 
 #[repr(C)]
 pub enum TelemetryProbe {
     SceneBuildTime = 0,
     SceneSwapTime = 1,
-    RenderTime = 2,
+    FrameBuildTime = 2,
 }
 
 extern "C" {
     fn is_in_compositor_thread() -> bool;
     fn is_in_render_thread() -> bool;
     fn is_in_main_thread() -> bool;
     fn is_glcontext_gles(glcontext_ptr: *mut c_void) -> bool;
     fn is_glcontext_angle(glcontext_ptr: *mut c_void) -> bool;
@@ -577,17 +577,17 @@ impl RenderNotifier for CppNotifier {
 
     fn new_frame_ready(&self,
                        _: DocumentId,
                        _scrolled: bool,
                        composite_needed: bool,
                        render_time_ns: Option<u64>) {
         unsafe {
             if let Some(time) = render_time_ns {
-                record_telemetry_time(TelemetryProbe::RenderTime, time);
+                record_telemetry_time(TelemetryProbe::FrameBuildTime, time);
             }
             if composite_needed {
                 wr_notifier_new_frame_ready(self.window_id);
             } else {
                 wr_notifier_nop_frame_done(self.window_id);
             }
         }
     }
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -717,28 +717,27 @@ Chunk* GCRuntime::pickChunk(AutoLockGCBg
 
   chunkAllocationSinceLastGC = true;
 
   availableChunks(lock).push(chunk);
 
   return chunk;
 }
 
-BackgroundAllocTask::BackgroundAllocTask(JSRuntime* rt, ChunkPool& pool)
-    : GCParallelTaskHelper(rt),
+BackgroundAllocTask::BackgroundAllocTask(GCRuntime* gc, ChunkPool& pool)
+    : GCParallelTaskHelper(gc),
       chunkPool_(pool),
       enabled_(CanUseExtraThreads() && GetCPUCount() >= 2) {}
 
 void BackgroundAllocTask::run() {
   TraceLoggerThread* logger = TraceLoggerForCurrentThread();
   AutoTraceLog logAllocation(logger, TraceLogger_GCAllocation);
 
-  GCRuntime* gc = &runtime()->gc;
   AutoLockGC lock(gc);
-  while (!cancel_ && runtime()->gc.wantBackgroundAllocation(lock)) {
+  while (!cancel_ && gc->wantBackgroundAllocation(lock)) {
     Chunk* chunk;
     {
       AutoUnlockGC unlock(lock);
       chunk = Chunk::allocate(gc);
       if (!chunk) {
         break;
       }
       chunk->init(gc);
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -849,17 +849,17 @@ void GCRuntime::releaseArena(Arena* aren
   arena->release(lock);
   arena->chunk()->releaseArena(this, arena, lock);
 }
 
 GCRuntime::GCRuntime(JSRuntime* rt)
     : rt(rt),
       systemZone(nullptr),
       atomsZone(nullptr),
-      stats_(rt),
+      stats_(this),
       marker(rt),
       heapSize(nullptr),
       rootsHash(256),
       nextCellUniqueId_(LargestTaggedNullCellPointer +
                         1),  // Ensure disjoint from null tagged pointers.
       numArenasFreeCommitted(0),
       verifyPreData(nullptr),
       chunkAllocationSinceLastGC(false),
@@ -910,20 +910,20 @@ GCRuntime::GCRuntime(JSRuntime* rt)
       deterministicOnly(false),
       incrementalLimit(0),
 #endif
       fullCompartmentChecks(false),
       gcCallbackDepth(0),
       alwaysPreserveCode(false),
       lowMemoryState(false),
       lock(mutexid::GCLock),
-      allocTask(rt, emptyChunks_.ref()),
-      sweepTask(rt),
-      freeTask(rt),
-      decommitTask(rt),
+      allocTask(this, emptyChunks_.ref()),
+      sweepTask(this),
+      freeTask(this),
+      decommitTask(this),
       nursery_(this),
       storeBuffer_(rt, nursery()) {
   setGCMode(JSGC_MODE_GLOBAL);
 }
 
 #ifdef JS_GC_ZEAL
 
 void GCRuntime::getZealBits(uint32_t* zealBits, uint32_t* frequency,
@@ -1269,17 +1269,17 @@ void GCRuntime::finish() {
 
 #ifdef JS_GC_ZEAL
   // Free memory associated with GC verification.
   finishVerifier();
 #endif
 
   // Delete all remaining zones.
   if (rt->gcInitialized) {
-    for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+    for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
       AutoSetThreadIsSweeping threadIsSweeping(zone);
       for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
         for (RealmsInCompartmentIter realm(comp); !realm.done(); realm.next()) {
           js_delete(realm.get());
         }
         comp->realms().clear();
         js_delete(comp.get());
       }
@@ -1328,17 +1328,17 @@ bool GCRuntime::setParameter(JSGCParamKe
       break;
     case JSGC_COMPACTING_ENABLED:
       compactingEnabled = value != 0;
       break;
     default:
       if (!tunables.setParameter(key, value, lock)) {
         return false;
       }
-      for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+      for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
         zone->updateGCThresholds(*this, GC_NORMAL, lock);
       }
   }
 
   return true;
 }
 
 void GCRuntime::resetParameter(JSGCParamKey key) {
@@ -1359,17 +1359,17 @@ void GCRuntime::resetParameter(JSGCParam
     case JSGC_MODE:
       mode = TuningDefaults::Mode;
       break;
     case JSGC_COMPACTING_ENABLED:
       compactingEnabled = TuningDefaults::CompactingEnabled;
       break;
     default:
       tunables.resetParameter(key, lock);
-      for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+      for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
         zone->updateGCThresholds(*this, GC_NORMAL, lock);
       }
   }
 }
 
 uint32_t GCRuntime::getParameter(JSGCParamKey key) {
   MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
   AutoLockGC lock(this);
@@ -2217,19 +2217,19 @@ ArenaListSegment ArenasToUpdate::getAren
 struct UpdatePointersTask : public GCParallelTaskHelper<UpdatePointersTask> {
   // Maximum number of arenas to update in one block.
 #ifdef DEBUG
   static const unsigned MaxArenasToProcess = 16;
 #else
   static const unsigned MaxArenasToProcess = 256;
 #endif
 
-  UpdatePointersTask(JSRuntime* rt, ArenasToUpdate* source,
+  UpdatePointersTask(GCRuntime* gc, ArenasToUpdate* source,
                      AutoLockHelperThreadState& lock)
-      : GCParallelTaskHelper(rt), source_(source) {
+      : GCParallelTaskHelper(gc), source_(source) {
     arenas_.begin = nullptr;
     arenas_.end = nullptr;
   }
 
   void run();
 
  private:
   ArenasToUpdate* source_;
@@ -2241,17 +2241,17 @@ struct UpdatePointersTask : public GCPar
 
 bool UpdatePointersTask::getArenasToUpdate() {
   AutoLockHelperThreadState lock;
   arenas_ = source_->getArenasToUpdate(lock, MaxArenasToProcess);
   return arenas_.begin != nullptr;
 }
 
 void UpdatePointersTask::updateArenas() {
-  MovingTracer trc(runtime());
+  MovingTracer trc(gc->rt);
   for (Arena* arena = arenas_.begin; arena != arenas_.end;
        arena = arena->next) {
     UpdateArenaPointers(&trc, arena);
   }
 }
 
 void UpdatePointersTask::run() {
   // These checks assert when run in parallel.
@@ -2330,26 +2330,26 @@ void GCRuntime::updateCellPointers(Zone*
   Maybe<UpdatePointersTask> fgTask;
   Maybe<UpdatePointersTask> bgTasks[MaxCellUpdateBackgroundTasks];
 
   size_t tasksStarted = 0;
 
   {
     AutoLockHelperThreadState lock;
 
-    fgTask.emplace(rt, &fgArenas, lock);
+    fgTask.emplace(this, &fgArenas, lock);
 
     for (size_t i = 0; i < bgTaskCount && !bgArenas.done(); i++) {
-      bgTasks[i].emplace(rt, &bgArenas, lock);
+      bgTasks[i].emplace(this, &bgArenas, lock);
       startTask(*bgTasks[i], gcstats::PhaseKind::COMPACT_UPDATE_CELLS, lock);
       tasksStarted++;
     }
   }
 
-  fgTask->runFromMainThread(rt);
+  fgTask->runFromMainThread();
 
   {
     AutoLockHelperThreadState lock;
 
     for (size_t i = 0; i < tasksStarted; i++) {
       joinTask(*bgTasks[i], gcstats::PhaseKind::COMPACT_UPDATE_CELLS, lock);
     }
     for (size_t i = tasksStarted; i < MaxCellUpdateBackgroundTasks; i++) {
@@ -3086,17 +3086,17 @@ void GCRuntime::maybeGC() {
     return;
   }
 
   if (isIncrementalGCInProgress()) {
     return;
   }
 
   bool scheduledZones = false;
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     if (checkEagerAllocTrigger(zone->gcHeapSize, zone->gcHeapThreshold) ||
         checkEagerAllocTrigger(zone->mallocHeapSize,
                                zone->mallocHeapThreshold)) {
       zone->scheduleGC();
       scheduledZones = true;
     }
   }
 
@@ -3176,46 +3176,46 @@ void GCRuntime::startDecommit() {
     }
   }
   decommitTask.setChunksToScan(toDecommit);
 
   if (sweepOnBackgroundThread && decommitTask.start()) {
     return;
   }
 
-  decommitTask.runFromMainThread(rt);
+  decommitTask.runFromMainThread();
 }
 
 void js::gc::BackgroundDecommitTask::setChunksToScan(ChunkVector& chunks) {
-  MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime()));
+  MOZ_ASSERT(CurrentThreadCanAccessRuntime(gc->rt));
   MOZ_ASSERT(!isRunning());
   MOZ_ASSERT(toDecommit.ref().empty());
   Swap(toDecommit.ref(), chunks);
 }
 
 void js::gc::BackgroundDecommitTask::run() {
-  AutoLockGC lock(runtime());
+  AutoLockGC lock(gc);
 
   for (Chunk* chunk : toDecommit.ref()) {
     // The arena list is not doubly-linked, so we have to work in the free
     // list order and not in the natural order.
     while (chunk->info.numArenasFreeCommitted) {
-      bool ok = chunk->decommitOneFreeArena(&runtime()->gc, lock);
+      bool ok = chunk->decommitOneFreeArena(gc, lock);
 
       // If we are low enough on memory that we can't update the page
       // tables, or if we need to return for any other reason, break out
       // of the loop.
       if (cancel_ || !ok) {
         break;
       }
     }
   }
   toDecommit.ref().clearAndFree();
 
-  ChunkPool toFree = runtime()->gc.expireEmptyChunkPool(lock);
+  ChunkPool toFree = gc->expireEmptyChunkPool(lock);
   if (toFree.count()) {
     AutoUnlockGC unlock(lock);
     FreeChunkPool(toFree);
   }
 }
 
 void GCRuntime::sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks) {
   freeBlocks.freeAll();
@@ -3269,17 +3269,17 @@ void GCRuntime::sweepBackgroundThings(Zo
 
 void GCRuntime::assertBackgroundSweepingFinished() {
 #ifdef DEBUG
   {
     AutoLockHelperThreadState lock;
     MOZ_ASSERT(backgroundSweepZones.ref().isEmpty());
   }
 
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     for (auto i : AllAllocKinds()) {
       MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i));
       MOZ_ASSERT(zone->arenas.doneBackgroundFinalize(i));
     }
   }
 #endif
 }
 
@@ -3287,27 +3287,27 @@ void GCRuntime::queueZonesAndStartBackgr
   {
     AutoLockHelperThreadState lock;
     backgroundSweepZones.ref().transferFrom(zones);
     if (sweepOnBackgroundThread) {
       sweepTask.startOrRunIfIdle(lock);
     }
   }
   if (!sweepOnBackgroundThread) {
-    sweepTask.joinAndRunFromMainThread(rt);
+    sweepTask.joinAndRunFromMainThread();
   }
 }
 
 void BackgroundSweepTask::run() {
   AutoTraceLog logSweeping(TraceLoggerForCurrentThread(),
                            TraceLogger_GCSweeping);
 
   AutoLockHelperThreadState lock;
 
-  runtime()->gc.sweepFromBackgroundThread(lock);
+  gc->sweepFromBackgroundThread(lock);
 
   // Signal to the main thread that we're about to finish, because we release
   // the lock again before GCParallelTask's state is changed to finished.
   setFinishing(lock);
 }
 
 void GCRuntime::sweepFromBackgroundThread(AutoLockHelperThreadState& lock) {
   do {
@@ -3369,17 +3369,17 @@ void GCRuntime::startBackgroundFree() {
   freeTask.startOrRunIfIdle(lock);
 }
 
 void BackgroundFreeTask::run() {
   AutoTraceLog logFreeing(TraceLoggerForCurrentThread(), TraceLogger_GCFree);
 
   AutoLockHelperThreadState lock;
 
-  runtime()->gc.freeFromBackgroundThread(lock);
+  gc->freeFromBackgroundThread(lock);
 
   // Signal to the main thread that we're about to finish, because we release
   // the lock again before GCParallelTask's state is changed to finished.
   setFinishing(lock);
 }
 
 void GCRuntime::freeFromBackgroundThread(AutoLockHelperThreadState& lock) {
   do {
@@ -3574,45 +3574,45 @@ void ArenaLists::checkEmptyArenaList(All
   MOZ_ASSERT(arenaLists(kind).isEmpty());
 }
 
 class MOZ_RAII js::gc::AutoRunParallelTask : public GCParallelTask {
   gcstats::PhaseKind phase_;
   AutoLockHelperThreadState& lock_;
 
  public:
-  AutoRunParallelTask(JSRuntime* rt, TaskFunc func, gcstats::PhaseKind phase,
+  AutoRunParallelTask(GCRuntime* gc, TaskFunc func, gcstats::PhaseKind phase,
                       AutoLockHelperThreadState& lock)
-      : GCParallelTask(rt, func), phase_(phase), lock_(lock) {
-    runtime()->gc.startTask(*this, phase_, lock_);
-  }
-
-  ~AutoRunParallelTask() { runtime()->gc.joinTask(*this, phase_, lock_); }
+      : GCParallelTask(gc, func), phase_(phase), lock_(lock) {
+    gc->startTask(*this, phase_, lock_);
+  }
+
+  ~AutoRunParallelTask() { gc->joinTask(*this, phase_, lock_); }
 };
 
 void GCRuntime::purgeRuntimeForMinorGC() {
   // If external strings become nursery allocable, remember to call
   // zone->externalStringCache().purge() (and delete this assert.)
   MOZ_ASSERT(!IsNurseryAllocable(AllocKind::EXTERNAL_STRING));
 
-  for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, SkipAtoms); !zone.done(); zone.next()) {
     zone->functionToStringCache().purge();
   }
 
   rt->caches().purgeForMinorGC(rt);
 }
 
 void GCRuntime::purgeRuntime() {
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE);
 
   for (GCRealmsIter realm(rt); !realm.done(); realm.next()) {
     realm->purge();
   }
 
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     zone->purgeAtomCacheOrDefer();
     zone->externalStringCache().purge();
     zone->functionToStringCache().purge();
   }
 
   JSContext* cx = rt->mainContextFromOwnThread();
   queueUnusedLifoBlocksForFree(&cx->tempLifoAlloc());
   cx->interpreterStack().purge(rt);
@@ -3721,17 +3721,17 @@ bool CompartmentCheckTracer::onChild(con
 void GCRuntime::checkForCompartmentMismatches() {
   JSContext* cx = rt->mainContextFromOwnThread();
   if (cx->disableStrictProxyCheckingCount) {
     return;
   }
 
   CompartmentCheckTracer trc(rt);
   AutoAssertEmptyNursery empty(cx);
-  for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, SkipAtoms); !zone.done(); zone.next()) {
     trc.zone = zone;
     for (auto thingKind : AllAllocKinds()) {
       for (auto i = zone->cellIterUnsafe<TenuredCell>(thingKind, empty);
            !i.done(); i.next()) {
         trc.src = i.getCell();
         trc.srcKind = MapAllocToTraceKind(thingKind);
         trc.compartment = MapGCThingTyped(
             trc.src, trc.srcKind, [](auto t) { return t->maybeCompartment(); });
@@ -3797,31 +3797,31 @@ static bool ShouldCollectZone(Zone* zone
 
   return zone->canCollect();
 }
 
 bool GCRuntime::prepareZonesForCollection(JS::GCReason reason,
                                           bool* isFullOut) {
 #ifdef DEBUG
   /* Assert that zone state is as we expect */
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     MOZ_ASSERT(!zone->isCollecting());
     MOZ_ASSERT_IF(!zone->isAtomsZone(), !zone->compartments().empty());
     for (auto i : AllAllocKinds()) {
       MOZ_ASSERT(!zone->arenas.arenaListsToSweep(i));
     }
   }
 #endif
 
   *isFullOut = true;
   bool any = false;
 
   auto currentTime = ReallyNow();
 
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     /* Set up which zones will be collected. */
     bool shouldCollect = ShouldCollectZone(zone, reason);
     if (shouldCollect) {
       MOZ_ASSERT(zone->canCollect());
       any = true;
       zone->changeGCState(Zone::NoGC, Zone::MarkBlackOnly);
     } else {
       *isFullOut = false;
@@ -3865,85 +3865,84 @@ bool GCRuntime::prepareZonesForCollectio
    */
   MOZ_ASSERT_IF(reason == JS::GCReason::DELAYED_ATOMS_GC,
                 atomsZone->isGCMarking());
 
   /* Check that at least one zone is scheduled for collection. */
   return any;
 }
 
-static void DiscardJITCodeForGC(JSRuntime* rt) {
+void GCRuntime::discardJITCodeForGC() {
   js::CancelOffThreadIonCompile(rt, JS::Zone::MarkBlackOnly);
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
-    gcstats::AutoPhase ap(rt->gc.stats(),
-                          gcstats::PhaseKind::MARK_DISCARD_CODE);
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
+    gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_DISCARD_CODE);
     zone->discardJitCode(rt->defaultFreeOp(), Zone::DiscardBaselineCode,
                          Zone::DiscardJitScripts);
   }
 }
 
-static void RelazifyFunctionsForShrinkingGC(JSRuntime* rt) {
-  gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::RELAZIFY_FUNCTIONS);
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+void GCRuntime::relazifyFunctionsForShrinkingGC() {
+  gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::RELAZIFY_FUNCTIONS);
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     if (zone->isSelfHostingZone()) {
       continue;
     }
     RelazifyFunctions(zone, AllocKind::FUNCTION);
     RelazifyFunctions(zone, AllocKind::FUNCTION_EXTENDED);
   }
 }
 
-static void PurgeShapeCachesForShrinkingGC(JSRuntime* rt) {
-  gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::PURGE_SHAPE_CACHES);
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+void GCRuntime::purgeShapeCachesForShrinkingGC() {
+  gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE_SHAPE_CACHES);
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     if (!CanRelocateZone(zone) || zone->keepShapeCaches()) {
       continue;
     }
     for (auto baseShape = zone->cellIterUnsafe<BaseShape>(); !baseShape.done();
          baseShape.next()) {
       baseShape->maybePurgeCache(rt->defaultFreeOp());
     }
   }
 }
 
 // The debugger keeps track of the URLs for the sources of each realm's scripts.
 // These URLs are purged on shrinking GCs.
-static void PurgeSourceURLsForShrinkingGC(JSRuntime* rt) {
-  gcstats::AutoPhase ap(rt->gc.stats(), gcstats::PhaseKind::PURGE_SOURCE_URLS);
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+void GCRuntime::purgeSourceURLsForShrinkingGC() {
+  gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::PURGE_SOURCE_URLS);
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     // URLs are not tracked for realms in the system zone.
     if (!CanRelocateZone(zone) || zone->isSystem) {
       continue;
     }
     for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
       for (RealmsInCompartmentIter realm(comp); !realm.done(); realm.next()) {
         GlobalObject* global = realm.get()->unsafeUnbarrieredMaybeGlobal();
         if (global) {
           global->clearSourceURLSHolder();
         }
       }
     }
   }
 }
 
 static void UnmarkCollectedZones(GCParallelTask* task) {
-  JSRuntime* rt = task->runtime();
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  GCRuntime* gc = task->gc;
+  for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
     /* Unmark everything in the zones being collected. */
     zone->arenas.unmarkAll();
   }
 
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
     /* Unmark all weak maps in the zones being collected. */
     WeakMapBase::unmarkZone(zone);
   }
 }
 
 static void BufferGrayRoots(GCParallelTask* task) {
-  task->runtime()->gc.bufferGrayRoots();
+  task->gc->bufferGrayRoots();
 }
 
 bool GCRuntime::beginMarkPhase(JS::GCReason reason, AutoGCSession& session) {
 #ifdef DEBUG
   if (fullCompartmentChecks) {
     checkForCompartmentMismatches();
   }
 #endif
@@ -3957,17 +3956,17 @@ bool GCRuntime::beginMarkPhase(JS::GCRea
     session.maybeCheckAtomsAccess.emplace(rt);
   }
 
   /*
    * In an incremental GC, clear the area free lists to ensure that subsequent
    * allocations refill them and end up marking new cells back. See
    * arenaAllocatedDuringGC().
    */
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     zone->arenas.clearFreeLists();
   }
 
   marker.start();
   GCMarker* gcmarker = &marker;
   gcmarker->clearMarkCount();
 
   {
@@ -3975,48 +3974,48 @@ bool GCRuntime::beginMarkPhase(JS::GCRea
     AutoLockHelperThreadState helperLock;
 
     /*
      * Clear all mark state for the zones we are collecting. This is linear
      * in the size of the heap we are collecting and so can be slow. Do this
      * in parallel with the rest of this block.
      */
     AutoRunParallelTask unmarkCollectedZones(
-        rt, UnmarkCollectedZones, gcstats::PhaseKind::UNMARK, helperLock);
+        this, UnmarkCollectedZones, gcstats::PhaseKind::UNMARK, helperLock);
 
     /*
      * Buffer gray roots for incremental collections. This is linear in the
      * number of roots which can be in the tens of thousands. Do this in
      * parallel with the rest of this block.
      */
     Maybe<AutoRunParallelTask> bufferGrayRoots;
     if (isIncremental) {
-      bufferGrayRoots.emplace(rt, BufferGrayRoots,
+      bufferGrayRoots.emplace(this, BufferGrayRoots,
                               gcstats::PhaseKind::BUFFER_GRAY_ROOTS,
                               helperLock);
     }
     AutoUnlockHelperThreadState unlock(helperLock);
 
     // Discard JIT code. For incremental collections, the sweep phase will
     // also discard JIT code.
-    DiscardJITCodeForGC(rt);
+    discardJITCodeForGC();
     startBackgroundFreeAfterMinorGC();
 
     /*
      * Relazify functions after discarding JIT code (we can't relazify
      * functions with JIT code) and before the actual mark phase, so that
      * the current GC can collect the JSScripts we're unlinking here.  We do
      * this only when we're performing a shrinking GC, as too much
      * relazification can cause performance issues when we have to reparse
      * the same functions over and over.
      */
     if (invocationKind == GC_SHRINK) {
-      RelazifyFunctionsForShrinkingGC(rt);
-      PurgeShapeCachesForShrinkingGC(rt);
-      PurgeSourceURLsForShrinkingGC(rt);
+      relazifyFunctionsForShrinkingGC();
+      purgeShapeCachesForShrinkingGC();
+      purgeSourceURLsForShrinkingGC();
     }
 
     /*
      * We must purge the runtime at the beginning of an incremental GC. The
      * danger if we purge later is that the snapshot invariant of
      * incremental GC will be broken, as follows. If some object is
      * reachable only through some cache (say the dtoaCache) then it will
      * not be part of the snapshot.  If we purge after root marking, then
@@ -4124,17 +4123,17 @@ void GCRuntime::findDeadCompartments() {
     }
   }
 }
 
 void GCRuntime::updateMemoryCountersOnGCStart() {
   heapSize.updateOnGCStart();
 
   // Update memory counters for the zones we are collecting.
-  for (GCZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     zone->updateMemoryCountersOnGCStart();
   }
 }
 
 template <class ZoneIterT>
 void GCRuntime::markWeakReferences(gcstats::PhaseKind phase) {
   MOZ_ASSERT(marker.isDrained());
 
@@ -4143,17 +4142,17 @@ void GCRuntime::markWeakReferences(gcsta
   marker.enterWeakMarkingMode();
 
   // TODO bug 1167452: Make weak marking incremental
   drainMarkStack();
 
   for (;;) {
     bool markedAny = false;
     if (!marker.isWeakMarkingTracer()) {
-      for (ZoneIterT zone(rt); !zone.done(); zone.next()) {
+      for (ZoneIterT zone(this); !zone.done(); zone.next()) {
         markedAny |= WeakMapBase::markZoneIteratively(zone, &marker);
       }
     }
     markedAny |= DebugAPI::markIteratively(&marker);
     markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
 
     if (!markedAny) {
       break;
@@ -4171,17 +4170,17 @@ void GCRuntime::markWeakReferencesInCurr
 }
 
 template <class ZoneIterT>
 void GCRuntime::markGrayRoots(gcstats::PhaseKind phase) {
   MOZ_ASSERT(marker.markColor() == MarkColor::Gray);
 
   gcstats::AutoPhase ap(stats(), phase);
   if (hasValidGrayRootsBuffer()) {
-    for (ZoneIterT zone(rt); !zone.done(); zone.next()) {
+    for (ZoneIterT zone(this); !zone.done(); zone.next()) {
       markBufferedGrayRoots(zone);
     }
   } else {
     MOZ_ASSERT(!isIncremental);
     traceEmbeddingGrayRoots(&marker);
     Compartment::traceIncomingCrossCompartmentEdgesForZoneGC(
         &marker, Compartment::GrayEdges);
   }
@@ -4283,17 +4282,17 @@ void js::gc::MarkingValidator::nonIncrem
    * up into per-zone tables when restoring.
    */
   gc::WeakKeyTable savedWeakKeys(SystemAllocPolicy(),
                                  runtime->randomHashCodeScrambler());
   if (!savedWeakKeys.init()) {
     return;
   }
 
-  for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
     if (!WeakMapBase::saveZoneMarkedWeakMaps(zone, markedWeakMaps)) {
       return;
     }
 
     AutoEnterOOMUnsafeRegion oomUnsafe;
     for (gc::WeakKeyTable::Range r = zone->gcWeakKeys().all(); !r.empty();
          r.popFront()) {
       if (!savedWeakKeys.put(std::move(r.front().key),
@@ -4318,17 +4317,17 @@ void js::gc::MarkingValidator::nonIncrem
   gc->incrementalState = State::MarkRoots;
 
   {
     gcstats::AutoPhase ap(gc->stats(), gcstats::PhaseKind::PREPARE);
 
     {
       gcstats::AutoPhase ap(gc->stats(), gcstats::PhaseKind::UNMARK);
 
-      for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
+      for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
         WeakMapBase::unmarkZone(zone);
       }
 
       MOZ_ASSERT(gcmarker->isDrained());
       gcmarker->reset();
 
       AutoLockGC lock(gc);
       for (auto chunk = gc->allNonEmptyChunks(lock); !chunk.done();
@@ -4350,44 +4349,44 @@ void js::gc::MarkingValidator::nonIncrem
   gc->incrementalState = State::Sweep;
   {
     gcstats::AutoPhase ap1(gc->stats(), gcstats::PhaseKind::SWEEP);
     gcstats::AutoPhase ap2(gc->stats(), gcstats::PhaseKind::SWEEP_MARK);
 
     gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_WEAK);
 
     /* Update zone state for gray marking. */
-    for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
+    for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
       zone->changeGCState(Zone::MarkBlackOnly, Zone::MarkBlackAndGray);
     }
 
     AutoSetMarkColor setColorGray(gc->marker, MarkColor::Gray);
 
     gc->markAllGrayReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY);
     gc->markAllWeakReferences(gcstats::PhaseKind::SWEEP_MARK_GRAY_WEAK);
 
     /* Restore zone state. */
-    for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
+    for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
       zone->changeGCState(Zone::MarkBlackAndGray, Zone::MarkBlackOnly);
     }
     MOZ_ASSERT(gc->marker.isDrained());
   }
 
   /* Take a copy of the non-incremental mark state and restore the original. */
   {
     AutoLockGC lock(gc);
     for (auto chunk = gc->allNonEmptyChunks(lock); !chunk.done();
          chunk.next()) {
       ChunkBitmap* bitmap = &chunk->bitmap;
       ChunkBitmap* entry = map.lookup(chunk)->value().get();
       Swap(*entry, *bitmap);
     }
   }
 
-  for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
     WeakMapBase::unmarkZone(zone);
     AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!zone->gcWeakKeys().clear()) {
       oomUnsafe.crash("clearing weak keys table for validator");
     }
   }
 
   WeakMapBase::restoreMarkedWeakMaps(markedWeakMaps);
@@ -4493,23 +4492,23 @@ void GCRuntime::validateIncrementalMarki
 
 void GCRuntime::finishMarkingValidation() {
 #ifdef JS_GC_ZEAL
   js_delete(markingValidator.ref());
   markingValidator = nullptr;
 #endif
 }
 
-static void DropStringWrappers(JSRuntime* rt) {
+void GCRuntime::dropStringWrappers() {
   /*
    * String "wrappers" are dropped on GC because their presence would require
    * us to sweep the wrappers in all compartments every time we sweep a
    * compartment group.
    */
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     zone->dropStringWrappersOnGC();
   }
 }
 
 /*
  * Group zones that must be swept at the same time.
  *
  * From the point of view of the mutator, groups of zones transition atomically
@@ -4605,32 +4604,32 @@ static bool AddEdgesForMarkQueue(GCMarke
     }
     prevZone = zone;
   }
 #endif
   return true;
 }
 
 bool GCRuntime::findSweepGroupEdges() {
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     if (!zone->findSweepGroupEdges(atomsZone)) {
       return false;
     }
   }
 
   if (!AddEdgesForMarkQueue(marker)) {
     return false;
   }
 
   return DebugAPI::findSweepGroupEdges(rt);
 }
 
 void GCRuntime::groupZonesForSweeping(JS::GCReason reason) {
 #ifdef DEBUG
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     MOZ_ASSERT(zone->gcSweepGroupEdges().empty());
   }
 #endif
 
   JSContext* cx = rt->mainContextFromOwnThread();
   ZoneComponentFinder finder(cx->nativeStackLimit[JS::StackForSystemCode]);
   if (!isIncremental || !findSweepGroupEdges()) {
     finder.useOneComponent();
@@ -4638,40 +4637,40 @@ void GCRuntime::groupZonesForSweeping(JS
 
 #ifdef JS_GC_ZEAL
   // Use one component for two-slice zeal modes.
   if (useZeal && hasIncrementalTwoSliceZealMode()) {
     finder.useOneComponent();
   }
 #endif
 
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     MOZ_ASSERT(zone->isGCMarking());
     finder.addNode(zone);
   }
   sweepGroups = finder.getResultsList();
   currentSweepGroup = sweepGroups;
   sweepGroupIndex = 1;
 
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     zone->clearSweepGroupEdges();
   }
 
 #ifdef DEBUG
   unsigned idx = sweepGroupIndex;
   for (Zone* head = currentSweepGroup; head; head = head->nextGroup()) {
     for (Zone* zone = head; zone; zone = zone->nextNodeInGroup()) {
       MOZ_ASSERT(zone->isGCMarking());
       zone->gcSweepGroupIndex = idx;
     }
     idx++;
   }
 
   MOZ_ASSERT_IF(!isIncremental, !currentSweepGroup->nextGroup());
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     MOZ_ASSERT(zone->gcSweepGroupEdges().empty());
   }
 #endif
 }
 
 static void ResetGrayList(Compartment* comp);
 
 void GCRuntime::getNextSweepGroup() {
@@ -4688,17 +4687,17 @@ void GCRuntime::getNextSweepGroup() {
   }
 
   for (Zone* zone = currentSweepGroup; zone; zone = zone->nextNodeInGroup()) {
     MOZ_ASSERT(zone->isGCMarkingBlackOnly());
     MOZ_ASSERT(!zone->isQueuedForBackgroundSweep());
   }
 
   if (abortSweepAfterCurrentGroup) {
-    for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+    for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
       MOZ_ASSERT(!zone->gcNextGraphComponent);
       zone->setNeedsIncrementalBarrier(false);
       zone->changeGCState(Zone::MarkBlackOnly, Zone::NoGC);
       zone->arenas.unmarkPreMarkedFreeCells();
       zone->gcGrayRoots().Clear();
     }
 
     for (SweepGroupCompartmentsIter comp(rt); !comp.done(); comp.next()) {
@@ -4960,17 +4959,17 @@ static inline void MaybeCheckWeakMapMark
   bool shouldCheck;
 #  if defined(DEBUG)
   shouldCheck = true;
 #  else
   shouldCheck = gc->hasZealMode(ZealMode::CheckWeakMapMarking);
 #  endif
 
   if (shouldCheck) {
-    for (SweepGroupZonesIter zone(gc->rt); !zone.done(); zone.next()) {
+    for (SweepGroupZonesIter zone(gc); !zone.done(); zone.next()) {
       MOZ_RELEASE_ASSERT(WeakMapBase::checkMarkingForZone(zone));
     }
   }
 
 #endif
 }
 
 IncrementalProgress GCRuntime::markGrayReferencesInCurrentGroup(
@@ -4990,17 +4989,17 @@ IncrementalProgress GCRuntime::markGrayR
   // become black by the action of UnmarkGray.
   markIncomingCrossCompartmentPointers(MarkColor::Black);
   drainMarkStack();
 
   // Change state of current group to MarkGray to restrict marking to this
   // group.  Note that there may be pointers to the atoms zone, and
   // these will be marked through, as they are not marked with
   // TraceCrossCompartmentEdge.
-  for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
     zone->changeGCState(Zone::MarkBlackOnly, Zone::MarkBlackAndGray);
   }
 
   AutoSetMarkColor setColorGray(marker, MarkColor::Gray);
 
   // Mark incoming gray pointers from previously swept compartments.
   markIncomingCrossCompartmentPointers(MarkColor::Gray);
 
@@ -5045,138 +5044,133 @@ IncrementalProgress GCRuntime::endMarkin
 class ImmediateSweepWeakCacheTask
     : public GCParallelTaskHelper<ImmediateSweepWeakCacheTask> {
   Zone* zone;
   JS::detail::WeakCacheBase& cache;
 
   ImmediateSweepWeakCacheTask(const ImmediateSweepWeakCacheTask&) = delete;
 
  public:
-  ImmediateSweepWeakCacheTask(JSRuntime* runtime, Zone* zone,
+  ImmediateSweepWeakCacheTask(GCRuntime* gc, Zone* zone,
                               JS::detail::WeakCacheBase& wc)
-      : GCParallelTaskHelper(runtime), zone(zone), cache(wc) {}
+      : GCParallelTaskHelper(gc), zone(zone), cache(wc) {}
 
   ImmediateSweepWeakCacheTask(ImmediateSweepWeakCacheTask&& other)
       : GCParallelTaskHelper(std::move(other)),
         zone(other.zone),
         cache(other.cache) {}
 
   void run() {
     AutoSetThreadIsSweeping threadIsSweeping(zone);
     cache.sweep();
   }
 };
 
-static void UpdateAtomsBitmap(JSRuntime* runtime) {
+void GCRuntime::updateAtomsBitmap() {
   DenseBitmap marked;
-  if (runtime->gc.atomMarking.computeBitmapFromChunkMarkBits(runtime, marked)) {
-    for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
-      runtime->gc.atomMarking.refineZoneBitmapForCollectedZone(zone, marked);
+  if (atomMarking.computeBitmapFromChunkMarkBits(rt, marked)) {
+    for (GCZonesIter zone(this); !zone.done(); zone.next()) {
+      atomMarking.refineZoneBitmapForCollectedZone(zone, marked);
     }
   } else {
     // Ignore OOM in computeBitmapFromChunkMarkBits. The
     // refineZoneBitmapForCollectedZone call can only remove atoms from the
     // zone bitmap, so it is conservative to just not call it.
   }
 
-  runtime->gc.atomMarking.markAtomsUsedByUncollectedZones(runtime);
+  atomMarking.markAtomsUsedByUncollectedZones(rt);
 
   // For convenience sweep these tables non-incrementally as part of bitmap
   // sweeping; they are likely to be much smaller than the main atoms table.
-  runtime->symbolRegistry().sweep();
-  for (RealmsIter realm(runtime); !realm.done(); realm.next()) {
+  rt->symbolRegistry().sweep();
+  for (RealmsIter realm(this); !realm.done(); realm.next()) {
     realm->sweepVarNames();
   }
 }
 
 static void SweepCCWrappers(GCParallelTask* task) {
   AutoSetThreadIsSweeping threadIsSweeping;  // This can touch all zones.
-  JSRuntime* runtime = task->runtime();
-  for (SweepGroupZonesIter zone(runtime); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(task->gc); !zone.done(); zone.next()) {
     zone->sweepAllCrossCompartmentWrappers();
   }
 }
 
 static void SweepObjectGroups(GCParallelTask* task) {
-  JSRuntime* runtime = task->runtime();
-  for (SweepGroupRealmsIter r(runtime); !r.done(); r.next()) {
+  for (SweepGroupRealmsIter r(task->gc); !r.done(); r.next()) {
     AutoSetThreadIsSweeping threadIsSweeping(r->zone());
     r->sweepObjectGroups();
   }
 }
 
 static void SweepMisc(GCParallelTask* task) {
-  JSRuntime* runtime = task->runtime();
-  for (SweepGroupRealmsIter r(runtime); !r.done(); r.next()) {
+  for (SweepGroupRealmsIter r(task->gc); !r.done(); r.next()) {
     AutoSetThreadIsSweeping threadIsSweeping(r->zone());
     r->sweepGlobalObject();
     r->sweepTemplateObjects();
     r->sweepSavedStacks();
     r->sweepSelfHostingScriptSource();
     r->sweepObjectRealm();
     r->sweepRegExps();
   }
 }
 
 static void SweepCompressionTasks(GCParallelTask* task) {
-  JSRuntime* runtime = task->runtime();
+  JSRuntime* runtime = task->gc->rt;
 
   // Attach finished compression tasks.
   AutoLockHelperThreadState lock;
   AttachFinishedCompressions(runtime, lock);
 
   // Sweep pending tasks that are holding onto should-be-dead ScriptSources.
   auto& pending = HelperThreadState().compressionPendingList(lock);
   for (size_t i = 0; i < pending.length(); i++) {
     if (pending[i]->shouldCancel()) {
       HelperThreadState().remove(pending, &i);
     }
   }
 }
 
 void js::gc::SweepLazyScripts(GCParallelTask* task) {
-  JSRuntime* runtime = task->runtime();
-  for (SweepGroupZonesIter zone(runtime); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(task->gc); !zone.done(); zone.next()) {
     AutoSetThreadIsSweeping threadIsSweeping(zone);
     for (auto i = zone->cellIter<LazyScript>(); !i.done(); i.next()) {
       WeakHeapPtrScript* edge = &i.unbarrieredGet()->script_;
       if (*edge && IsAboutToBeFinalized(edge)) {
         *edge = nullptr;
       }
     }
   }
 }
 
 static void SweepWeakMaps(GCParallelTask* task) {
   AutoSetThreadIsSweeping threadIsSweeping;  // This may touch any zone.
-  JSRuntime* runtime = task->runtime();
-  for (SweepGroupZonesIter zone(runtime); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(task->gc); !zone.done(); zone.next()) {
     /* No need to look up any more weakmap keys from this sweep group. */
     AutoEnterOOMUnsafeRegion oomUnsafe;
     if (!zone->gcWeakKeys().clear()) {
       oomUnsafe.crash("clearing weak keys in beginSweepingSweepGroup()");
     }
 
     zone->sweepWeakMaps();
   }
 }
 
 static void SweepUniqueIds(GCParallelTask* task) {
-  for (SweepGroupZonesIter zone(task->runtime()); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(task->gc); !zone.done(); zone.next()) {
     AutoSetThreadIsSweeping threadIsSweeping(zone);
     zone->sweepUniqueIds();
   }
 }
 
 void GCRuntime::startTask(GCParallelTask& task, gcstats::PhaseKind phase,
                           AutoLockHelperThreadState& locked) {
   if (!CanUseExtraThreads() || !task.startWithLockHeld(locked)) {
     AutoUnlockHelperThreadState unlock(locked);
     gcstats::AutoPhase ap(stats(), phase);
-    task.runFromMainThread(rt);
+    task.runFromMainThread();
   }
 }
 
 void GCRuntime::joinTask(GCParallelTask& task, gcstats::PhaseKind phase,
                          AutoLockHelperThreadState& locked) {
   {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::JOIN_PARALLEL_TASKS);
     task.joinWithLockHeld(locked);
@@ -5200,17 +5194,17 @@ void GCRuntime::sweepDebuggerOnMainThrea
       r->sweepDebugEnvironments();
     }
   }
 
   // Sweep breakpoints. This is done here to be with the other debug sweeping,
   // although note that it can cause JIT code to be patched.
   {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_BREAKPOINT);
-    for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+    for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
       zone->sweepBreakpoints(fop);
     }
   }
 }
 
 void GCRuntime::sweepJitDataOnMainThread(JSFreeOp* fop) {
   {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_JIT_DATA);
@@ -5227,41 +5221,41 @@ void GCRuntime::sweepJitDataOnMainThread
 
     // Sweep entries containing about-to-be-finalized JitCode and
     // update relocated TypeSet::Types inside the JitcodeGlobalTable.
     jit::JitRuntime::SweepJitcodeGlobalTable(rt);
   }
 
   if (initialState != State::NotActive) {
     gcstats::AutoPhase apdc(stats(), gcstats::PhaseKind::SWEEP_DISCARD_CODE);
-    for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+    for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
       zone->discardJitCode(fop);
     }
   }
 
   // JitZone/JitRealm must be swept *after* discarding JIT code, because
   // Zone::discardJitCode might access CacheIRStubInfos deleted here.
   {
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_JIT_DATA);
 
     for (SweepGroupRealmsIter r(rt); !r.done(); r.next()) {
       r->sweepJitRealm();
     }
 
-    for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+    for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
       if (jit::JitZone* jitZone = zone->jitZone()) {
         jitZone->sweep();
       }
     }
   }
 
   {
     gcstats::AutoPhase ap1(stats(), gcstats::PhaseKind::SWEEP_TYPES);
     gcstats::AutoPhase ap2(stats(), gcstats::PhaseKind::SWEEP_TYPES_BEGIN);
-    for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+    for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
       zone->beginSweepTypes();
     }
   }
 }
 
 using WeakCacheTaskVector =
     mozilla::Vector<ImmediateSweepWeakCacheTask, 0, SystemAllocPolicy>;
 
@@ -5299,17 +5293,17 @@ static bool PrepareWeakCacheTasks(JSRunt
           return true;
         }
 
         // Caches that support incremental sweeping will be swept later.
         if (zone && cache->setNeedsIncrementalBarrier(true)) {
           return true;
         }
 
-        return immediateTasks->emplaceBack(rt, zone, *cache);
+        return immediateTasks->emplaceBack(&rt->gc, zone, *cache);
       });
 
   if (!ok) {
     immediateTasks->clearAndFree();
   }
 
   return ok;
 }
@@ -5333,17 +5327,17 @@ IncrementalProgress GCRuntime::beginSwee
    * actions that must be done before yielding to caller.
    */
 
   using namespace gcstats;
 
   AutoSCC scc(stats(), sweepGroupIndex);
 
   bool sweepingAtoms = false;
-  for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
     /* Set the GC state to sweeping. */
     zone->changeGCState(Zone::MarkBlackAndGray, Zone::Sweep);
 
     /* Purge the ArenaLists before sweeping. */
     zone->arenas.unmarkPreMarkedFreeCells();
     zone->arenas.clearFreeLists();
 
     if (zone->isAtomsZone()) {
@@ -5364,54 +5358,54 @@ IncrementalProgress GCRuntime::beginSwee
     AutoPhase ap(stats(), PhaseKind::FINALIZE_START);
     callFinalizeCallbacks(fop, JSFINALIZE_GROUP_PREPARE);
     {
       AutoPhase ap2(stats(), PhaseKind::WEAK_ZONES_CALLBACK);
       callWeakPointerZonesCallbacks();
     }
     {
       AutoPhase ap2(stats(), PhaseKind::WEAK_COMPARTMENT_CALLBACK);
-      for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+      for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
         for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
           callWeakPointerCompartmentCallbacks(comp);
         }
       }
     }
     callFinalizeCallbacks(fop, JSFINALIZE_GROUP_START);
   }
 
   // Updating the atom marking bitmaps. This marks atoms referenced by
   // uncollected zones so cannot be done in parallel with the other sweeping
   // work below.
   if (sweepingAtoms) {
     AutoPhase ap(stats(), PhaseKind::UPDATE_ATOMS_BITMAP);
-    UpdateAtomsBitmap(rt);
+    updateAtomsBitmap();
   }
 
   AutoSetThreadIsSweeping threadIsSweeping;
 
   sweepDebuggerOnMainThread(fop);
 
   {
     AutoLockHelperThreadState lock;
 
     AutoPhase ap(stats(), PhaseKind::SWEEP_COMPARTMENTS);
 
-    AutoRunParallelTask sweepCCWrappers(rt, SweepCCWrappers,
+    AutoRunParallelTask sweepCCWrappers(this, SweepCCWrappers,
                                         PhaseKind::SWEEP_CC_WRAPPER, lock);
-    AutoRunParallelTask sweepObjectGroups(rt, SweepObjectGroups,
+    AutoRunParallelTask sweepObjectGroups(this, SweepObjectGroups,
                                           PhaseKind::SWEEP_TYPE_OBJECT, lock);
-    AutoRunParallelTask sweepMisc(rt, SweepMisc, PhaseKind::SWEEP_MISC, lock);
-    AutoRunParallelTask sweepCompTasks(rt, SweepCompressionTasks,
+    AutoRunParallelTask sweepMisc(this, SweepMisc, PhaseKind::SWEEP_MISC, lock);
+    AutoRunParallelTask sweepCompTasks(this, SweepCompressionTasks,
                                        PhaseKind::SWEEP_COMPRESSION, lock);
-    AutoRunParallelTask sweepLazyScripts(rt, SweepLazyScripts,
+    AutoRunParallelTask sweepLazyScripts(this, SweepLazyScripts,
                                          PhaseKind::SWEEP_LAZYSCRIPTS, lock);
-    AutoRunParallelTask sweepWeakMaps(rt, SweepWeakMaps,
+    AutoRunParallelTask sweepWeakMaps(this, SweepWeakMaps,
                                       PhaseKind::SWEEP_WEAKMAPS, lock);
-    AutoRunParallelTask sweepUniqueIds(rt, SweepUniqueIds,
+    AutoRunParallelTask sweepUniqueIds(this, SweepUniqueIds,
                                        PhaseKind::SWEEP_UNIQUEIDS, lock);
 
     WeakCacheTaskVector sweepCacheTasks;
     if (!PrepareWeakCacheTasks(rt, &sweepCacheTasks)) {
       SweepWeakCachesOnMainThread(rt);
     }
 
     for (auto& task : sweepCacheTasks) {
@@ -5430,17 +5424,17 @@ IncrementalProgress GCRuntime::beginSwee
 
   if (sweepingAtoms) {
     startSweepingAtomsTable();
   }
 
   // Queue all GC things in all zones for sweeping, either on the foreground
   // or on the background thread.
 
-  for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
     zone->arenas.queueForForegroundSweep(fop, ForegroundObjectFinalizePhase);
     zone->arenas.queueForForegroundSweep(fop, ForegroundNonObjectFinalizePhase);
     for (unsigned i = 0; i < ArrayLength(BackgroundFinalizePhases); ++i) {
       zone->arenas.queueForBackgroundSweep(fop, BackgroundFinalizePhases[i]);
     }
 
     zone->arenas.queueForegroundThingsForSweep();
   }
@@ -5472,30 +5466,30 @@ IncrementalProgress GCRuntime::endSweepi
     JSFreeOp fop(rt);
     callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_END);
   }
 
   /* Free LIFO blocks on a background thread if possible. */
   startBackgroundFree();
 
   /* Update the GC state for zones we have swept. */
-  for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
     AutoLockGC lock(this);
     zone->changeGCState(Zone::Sweep, Zone::Finished);
     zone->updateGCThresholds(*this, invocationKind, lock);
     zone->arenas.unmarkPreMarkedFreeCells();
   }
 
   /*
    * Start background thread to sweep zones if required, sweeping the atoms
    * zone last if present.
    */
   bool sweepAtomsZone = false;
   ZoneList zones;
-  for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
     if (zone->isAtomsZone()) {
       sweepAtomsZone = true;
     } else {
       zones.append(zone);
     }
   }
   if (sweepAtomsZone) {
     zones.append(atomsZone);
@@ -5521,17 +5515,17 @@ void GCRuntime::beginSweepPhase(JS::GCRe
 
   computeNonIncrementalMarkingForValidation(session);
 
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP);
 
   hasMarkedGrayRoots = false;
 
   AssertNoWrappersInGrayList(rt);
-  DropStringWrappers(rt);
+  dropStringWrappers();
 
   groupZonesForSweeping(reason);
 
   sweepActions->assertFinished();
 }
 
 bool ArenaLists::foregroundFinalize(JSFreeOp* fop, AllocKind thingKind,
                                     SliceBudget& sliceBudget,
@@ -5657,17 +5651,17 @@ IncrementalProgress GCRuntime::sweepType
 }
 
 IncrementalProgress GCRuntime::releaseSweptEmptyArenas(JSFreeOp* fop,
                                                        SliceBudget& budget) {
   // Foreground finalized GC things have already been finalized, and now their
   // arenas can be reclaimed by freeing empty ones and making non-empty ones
   // available for allocation.
 
-  for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (SweepGroupZonesIter zone(this); !zone.done(); zone.next()) {
     zone->arenas.releaseForegroundSweptEmptyArenas();
   }
   return Finished;
 }
 
 void GCRuntime::startSweepingAtomsTable() {
   auto& maybeAtoms = maybeAtomsToSweep.ref();
   MOZ_ASSERT(maybeAtoms.isNothing());
@@ -5779,31 +5773,30 @@ class js::gc::WeakCacheSweepIterator {
 class IncrementalSweepWeakCacheTask
     : public GCParallelTaskHelper<IncrementalSweepWeakCacheTask> {
   WeakCacheSweepIterator& work_;
   SliceBudget& budget_;
   AutoLockHelperThreadState& lock_;
   WeakCacheSweepIterator::Item item_;
 
  public:
-  IncrementalSweepWeakCacheTask(JSRuntime* rt, WeakCacheSweepIterator& work,
+  IncrementalSweepWeakCacheTask(GCRuntime* gc, WeakCacheSweepIterator& work,
                                 SliceBudget& budget,
                                 AutoLockHelperThreadState& lock)
-      : GCParallelTaskHelper(rt),
+      : GCParallelTaskHelper(gc),
         work_(work),
         budget_(budget),
         lock_(lock),
         item_(work.next(lock)) {
     MOZ_ASSERT(item_.cache);
-    runtime()->gc.startTask(*this, gcstats::PhaseKind::SWEEP_WEAK_CACHES,
-                            lock_);
+    gc->startTask(*this, gcstats::PhaseKind::SWEEP_WEAK_CACHES, lock_);
   }
 
   ~IncrementalSweepWeakCacheTask() {
-    runtime()->gc.joinTask(*this, gcstats::PhaseKind::SWEEP_WEAK_CACHES, lock_);
+    gc->joinTask(*this, gcstats::PhaseKind::SWEEP_WEAK_CACHES, lock_);
   }
 
   void run() {
     do {
       JS::detail::WeakCacheBase* cache = item_.cache;
       AutoSetThreadIsSweeping threadIsSweeping(item_.zone);
 
       MOZ_ASSERT(cache->needsIncrementalBarrier());
@@ -5834,17 +5827,17 @@ IncrementalProgress GCRuntime::sweepWeak
 
   {
     AutoLockHelperThreadState lock;
     gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::SWEEP_COMPARTMENTS);
 
     Maybe<IncrementalSweepWeakCacheTask> tasks[MaxWeakCacheSweepTasks];
     for (size_t i = 0; !work.empty(lock) && i < WeakCacheSweepTaskCount();
          i++) {
-      tasks[i].emplace(rt, work, budget, lock);
+      tasks[i].emplace(this, work, budget, lock);
     }
 
     // Tasks run until budget or work is exhausted.
   }
 
   AutoLockHelperThreadState lock;
   return work.empty(lock) ? Finished : NotFinished;
 }
@@ -6213,30 +6206,30 @@ IncrementalProgress GCRuntime::performSw
       return NotFinished;
     }
   }
 
   SweepAction::Args args{this, &fop, budget};
   return sweepActions->run(args);
 }
 
-bool GCRuntime::allCCVisibleZonesWereCollected() const {
+bool GCRuntime::allCCVisibleZonesWereCollected() {
   // Calculate whether the gray marking state is now valid.
   //
   // The gray bits change from invalid to valid if we finished a full GC from
   // the point of view of the cycle collector. We ignore the following:
   //
   //  - Helper thread zones, as these are not reachable from the main heap.
   //  - The atoms zone, since strings and symbols are never marked gray.
   //  - Empty zones.
   //
   // These exceptions ensure that when the CC requests a full GC the gray mark
   // state ends up valid even it we don't collect all of the zones.
 
-  for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, SkipAtoms); !zone.done(); zone.next()) {
     if (!zone->isCollecting() && !zone->usedByHelperThread() &&
         !zone->arenas.arenaListsAreEmpty()) {
       return false;
     }
   }
 
   return true;
 }
@@ -6273,17 +6266,17 @@ void GCRuntime::endSweepPhase(bool destr
     if (allCCVisibleZonesWereCollected()) {
       grayBitsValid = true;
     }
   }
 
   finishMarkingValidation();
 
 #ifdef DEBUG
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     for (auto i : AllAllocKinds()) {
       MOZ_ASSERT_IF(!IsBackgroundFinalized(i) || !sweepOnBackgroundThread,
                     !zone->arenas.arenaListsToSweep(i));
     }
   }
 #endif
 
   AssertNoWrappersInGrayList(rt);
@@ -6291,17 +6284,17 @@ void GCRuntime::endSweepPhase(bool destr
 
 void GCRuntime::beginCompactPhase() {
   MOZ_ASSERT(!isBackgroundSweeping());
   assertBackgroundSweepingFinished();
 
   gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::COMPACT);
 
   MOZ_ASSERT(zonesToMaybeCompact.ref().isEmpty());
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     if (CanRelocateZone(zone)) {
       zonesToMaybeCompact.ref().append(zone);
     }
   }
 
   MOZ_ASSERT(!relocatedArenasToRelease);
   startedCompacting = true;
 }
@@ -6358,17 +6351,17 @@ IncrementalProgress GCRuntime::compactPh
   } else {
     releaseRelocatedArenas(relocatedArenas);
   }
 
   // Clear caches that can contain cell pointers.
   rt->caches().purgeForCompaction();
 
 #ifdef DEBUG
-  CheckHashTablesAfterMovingGC(rt);
+  checkHashTablesAfterMovingGC();
 #endif
 
   return zonesToMaybeCompact.ref().isEmpty() ? Finished : NotFinished;
 }
 
 void GCRuntime::endCompactPhase() { startedCompacting = false; }
 
 void GCRuntime::finishCollection() {
@@ -6376,17 +6369,17 @@ void GCRuntime::finishCollection() {
   MOZ_ASSERT(marker.isDrained());
   marker.stop();
   clearBufferedGrayRoots();
 
   auto currentTime = ReallyNow();
   schedulingState.updateHighFrequencyMode(lastGCEndTime_, currentTime,
                                           tunables);
 
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     if (zone->isCollecting()) {
       zone->changeGCState(Zone::Finished, Zone::NoGC);
       zone->gcDelayBytes = 0;
       zone->notifyObservingDebuggers();
     }
 
     MOZ_ASSERT(!zone->wasGCStarted());
     MOZ_ASSERT(!zone->needsIncrementalBarrier());
@@ -6461,17 +6454,17 @@ GCRuntime::IncrementalResult GCRuntime::
       // Cancel any ongoing marking.
       marker.reset();
       clearBufferedGrayRoots();
 
       for (GCCompartmentsIter c(rt); !c.done(); c.next()) {
         ResetGrayList(c);
       }
 
-      for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+      for (GCZonesIter zone(this); !zone.done(); zone.next()) {
         zone->setNeedsIncrementalBarrier(false);
         zone->changeGCState(Zone::MarkBlackOnly, Zone::NoGC);
         zone->gcDelayBytes = 0;
         zone->arenas.unmarkPreMarkedFreeCells();
       }
 
       {
         AutoLockHelperThreadState lock;
@@ -6523,44 +6516,44 @@ GCRuntime::IncrementalResult GCRuntime::
 
 namespace {
 
 /*
  * Temporarily disable barriers during GC slices.
  */
 class AutoDisableBarriers {
  public:
-  explicit AutoDisableBarriers(JSRuntime* rt);
+  explicit AutoDisableBarriers(GCRuntime* gc);
   ~AutoDisableBarriers();
 
  private:
-  JSRuntime* runtime;
+  GCRuntime* gc;
 };
 
 } /* anonymous namespace */
 
-AutoDisableBarriers::AutoDisableBarriers(JSRuntime* rt) : runtime(rt) {
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+AutoDisableBarriers::AutoDisableBarriers(GCRuntime* gc) : gc(gc) {
+  for (GCZonesIter zone(gc); !zone.done(); zone.next()) {
     /*
      * Clear needsIncrementalBarrier early so we don't do any write
      * barriers during GC. We don't need to update the Ion barriers (which
      * is expensive) because Ion code doesn't run during GC. If need be,
      * we'll update the Ion barriers in ~AutoDisableBarriers.
      */
     if (zone->isGCMarking()) {
       MOZ_ASSERT(zone->needsIncrementalBarrier());
       zone->setNeedsIncrementalBarrier(false);
     }
     MOZ_ASSERT(!zone->needsIncrementalBarrier());
   }
 }
 
 AutoDisableBarriers::~AutoDisableBarriers() {
   /* We can't use GCZonesIter if this is the end of the last slice. */
-  for (ZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(gc, WithAtoms); !zone.done(); zone.next()) {
     MOZ_ASSERT(!zone->needsIncrementalBarrier());
     if (zone->isGCMarking()) {
       zone->setNeedsIncrementalBarrier(true);
     }
   }
 }
 
 void GCRuntime::pushZealSelectedObjects() {
@@ -6589,17 +6582,17 @@ static bool ShouldCleanUpEverything(JS::
 static bool ShouldSweepOnBackgroundThread(JS::GCReason reason) {
   return reason != JS::GCReason::DESTROY_RUNTIME && !gcTracer.traceEnabled() &&
          CanUseExtraThreads();
 }
 
 void GCRuntime::incrementalSlice(SliceBudget& budget,
                                  const MaybeInvocationKind& gckind,
                                  JS::GCReason reason, AutoGCSession& session) {
-  AutoDisableBarriers disableBarriers(rt);
+  AutoDisableBarriers disableBarriers(this);
   AutoSetThreadIsPerformingGC performingGC;
 
   bool destroyingRuntime = (reason == JS::GCReason::DESTROY_RUNTIME);
 
   initialState = incrementalState;
 
 #ifdef JS_GC_ZEAL
   /*
@@ -6653,17 +6646,17 @@ void GCRuntime::incrementalSlice(SliceBu
       cleanUpEverything = ShouldCleanUpEverything(reason, invocationKind);
       sweepOnBackgroundThread = ShouldSweepOnBackgroundThread(reason);
       isCompacting = shouldCompact();
       MOZ_ASSERT(!lastMarkSlice);
       rootsRemoved = false;
       lastGCStartTime_ = ReallyNow();
 
 #ifdef DEBUG
-      for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+      for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
         zone->gcSweepGroupIndex = 0;
       }
 #endif
 
       incrementalState = State::MarkRoots;
 
       MOZ_FALLTHROUGH;
 
@@ -6856,29 +6849,28 @@ gc::AbortReason gc::IsIncrementalGCUnsaf
 
   if (!rt->gc.isIncrementalGCAllowed()) {
     return gc::AbortReason::IncrementalDisabled;
   }
 
   return gc::AbortReason::None;
 }
 
-static inline void CheckZoneIsScheduled(Zone* zone, JS::GCReason reason,
-                                        const char* trigger) {
+inline void GCRuntime::checkZoneIsScheduled(Zone* zone, JS::GCReason reason,
+                                            const char* trigger) {
 #ifdef DEBUG
   if (zone->isGCScheduled()) {
     return;
   }
 
   fprintf(stderr,
-          "CheckZoneIsScheduled: Zone %p not scheduled as expected in %s GC "
+          "checkZoneIsScheduled: Zone %p not scheduled as expected in %s GC "
           "for %s trigger\n",
           zone, JS::ExplainGCReason(reason), trigger);
-  JSRuntime* rt = zone->runtimeFromMainThread();
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     fprintf(stderr, "  Zone %p:%s%s\n", zone.get(),
             zone->isAtomsZone() ? " atoms" : "",
             zone->isGCScheduled() ? " scheduled" : "");
   }
   fflush(stderr);
   MOZ_CRASH("Zone not scheduled");
 #endif
 }
@@ -6933,44 +6925,44 @@ GCRuntime::IncrementalResult GCRuntime::
 
   if (unsafeReason != AbortReason::None) {
     budget.makeUnlimited();
     stats().nonincremental(unsafeReason);
     return resetIncrementalGC(unsafeReason);
   }
 
   AbortReason resetReason = AbortReason::None;
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     if (!zone->canCollect()) {
       continue;
     }
 
     if (zone->gcHeapSize.bytes() >=
         zone->gcHeapThreshold.nonIncrementalTriggerBytes(tunables)) {
-      CheckZoneIsScheduled(zone, reason, "GC bytes");
+      checkZoneIsScheduled(zone, reason, "GC bytes");
       budget.makeUnlimited();
       stats().nonincremental(AbortReason::GCBytesTrigger);
       if (zone->wasGCStarted() && zone->gcState() > Zone::Sweep) {
         resetReason = AbortReason::GCBytesTrigger;
       }
     }
 
     if (zone->mallocHeapSize.bytes() >=
         zone->mallocHeapThreshold.nonIncrementalTriggerBytes(tunables)) {
-      CheckZoneIsScheduled(zone, reason, "malloc bytes");
+      checkZoneIsScheduled(zone, reason, "malloc bytes");
       budget.makeUnlimited();
       stats().nonincremental(AbortReason::MallocBytesTrigger);
       if (zone->wasGCStarted() && zone->gcState() > Zone::Sweep) {
         resetReason = AbortReason::MallocBytesTrigger;
       }
     }
 
     if (zone->jitHeapSize.bytes() >=
         zone->jitHeapThreshold.nonIncrementalTriggerBytes(tunables)) {
-      CheckZoneIsScheduled(zone, reason, "JIT code bytes");
+      checkZoneIsScheduled(zone, reason, "JIT code bytes");
       budget.makeUnlimited();
       stats().nonincremental(AbortReason::JitCodeBytesTrigger);
       if (zone->wasGCStarted() && zone->gcState() > Zone::Sweep) {
         resetReason = AbortReason::JitCodeBytesTrigger;
       }
     }
 
     if (isIncrementalGCInProgress() &&
@@ -7010,19 +7002,17 @@ GCRuntime::IncrementalResult GCRuntime::
     }
   }
 #endif  // JS_MORE_DETERMINISTIC
 
   return IncrementalResult::Ok;
 }
 
 static void ScheduleZones(GCRuntime* gc) {
-  JSRuntime* rt = gc->rt;
-
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(gc, WithAtoms); !zone.done(); zone.next()) {
     if (!zone->canCollect()) {
       continue;
     }
 
     if (gc->gcMode() == JSGC_MODE_GLOBAL ||
         gc->gcMode() == JSGC_MODE_INCREMENTAL) {
       zone->scheduleGC();
     }
@@ -7067,31 +7057,31 @@ void GCRuntime::maybeCallGCCallback(JSGC
   }
 
   if (isIncrementalGCInProgress()) {
     return;
   }
 
   if (gcCallbackDepth == 0) {
     // Save scheduled zone information in case the callback changes it.
-    for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+    for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
       zone->gcScheduledSaved_ = zone->gcScheduled_;
     }
   }
 
   gcCallbackDepth++;
 
   callGCCallback(status);
 
   MOZ_ASSERT(gcCallbackDepth != 0);
   gcCallbackDepth--;
 
   if (gcCallbackDepth == 0) {
     // Restore scheduled zone information again.
-    for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+    for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
       zone->gcScheduled_ = zone->gcScheduledSaved_;
     }
   }
 }
 
 /*
  * We disable inlining to ensure that the bottom of the stack with possible GC
  * roots recorded in MarkRuntime excludes any pointers we use during the marking
@@ -7218,17 +7208,17 @@ static bool IsDeterministicGCReason(JS::
     default:
       return false;
   }
 }
 #endif
 
 gcstats::ZoneGCStats GCRuntime::scanZonesBeforeGC() {
   gcstats::ZoneGCStats zoneStats;
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     zoneStats.zoneCount++;
     zoneStats.compartmentCount += zone->compartments().length();
     if (zone->canCollect()) {
       zoneStats.collectableZoneCount++;
       if (zone->isGCScheduled()) {
         zoneStats.collectedZoneCount++;
         zoneStats.collectedCompartmentCount += zone->compartments().length();
       }
@@ -7461,44 +7451,44 @@ void GCRuntime::finishGC(JS::GCReason re
 void GCRuntime::abortGC() {
   MOZ_ASSERT(isIncrementalGCInProgress());
   checkCanCallAPI();
   MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
   collect(false, SliceBudget::unlimited(), Nothing(), JS::GCReason::ABORT_GC);
 }
 
-static bool ZonesSelected(JSRuntime* rt) {
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+static bool ZonesSelected(GCRuntime* gc) {
+  for (ZonesIter zone(gc, WithAtoms); !zone.done(); zone.next()) {
     if (zone->isGCScheduled()) {
       return true;
     }
   }
   return false;
 }
 
 void GCRuntime::startDebugGC(JSGCInvocationKind gckind, SliceBudget& budget) {
   MOZ_ASSERT(!isIncrementalGCInProgress());
-  if (!ZonesSelected(rt)) {
+  if (!ZonesSelected(this)) {
     JS::PrepareForFullGC(rt->mainContextFromOwnThread());
   }
   collect(false, budget, Some(gckind), JS::GCReason::DEBUG_GC);
 }
 
 void GCRuntime::debugGCSlice(SliceBudget& budget) {
   MOZ_ASSERT(isIncrementalGCInProgress());
-  if (!ZonesSelected(rt)) {
+  if (!ZonesSelected(this)) {
     JS::PrepareForIncrementalGC(rt->mainContextFromOwnThread());
   }
   collect(false, budget, Nothing(), JS::GCReason::DEBUG_GC);
 }
 
 /* Schedule a full GC unless a zone will already be collected. */
 void js::PrepareForDebugGC(JSRuntime* rt) {
-  if (!ZonesSelected(rt)) {
+  if (!ZonesSelected(&rt->gc)) {
     JS::PrepareForFullGC(rt->mainContextFromOwnThread());
   }
 }
 
 void GCRuntime::onOutOfMallocMemory() {
   // Stop allocating new chunks.
   allocTask.cancelAndWait();
 
@@ -7538,17 +7528,17 @@ void GCRuntime::minorGC(JS::GCReason rea
 
   AutoMaybeLeaveAtomsZone leaveAtomsZone(rt->mainContextFromOwnThread());
 
   // Note that we aren't collecting the updated alloc counts from any helper
   // threads.  We should be but I'm not sure where to add that
   // synchronisation.
   uint32_t numAllocs =
       rt->mainContextFromOwnThread()->getAndResetAllocsThisZoneSinceMinorGC();
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     numAllocs += zone->getAndResetTenuredAllocsSinceMinorGC();
   }
   stats().setAllocsSinceMinorGCTenured(numAllocs);
 
   gcstats::AutoPhase ap(stats(), phase);
 
   nursery().clearMinorGCRequest();
   TraceLoggerThread* logger = TraceLoggerForCurrentThread();
@@ -7559,17 +7549,17 @@ void GCRuntime::minorGC(JS::GCReason rea
   startBackgroundFreeAfterMinorGC();
 
 #ifdef JS_GC_ZEAL
   if (hasZealMode(ZealMode::CheckHeapAfterGC)) {
     CheckHeapAfterGC(rt);
   }
 #endif
 
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     maybeAllocTriggerZoneGC(zone);
     maybeMallocTriggerZoneGC(zone);
   }
 }
 
 void GCRuntime::startBackgroundFreeAfterMinorGC() {
   MOZ_ASSERT(nursery().isEmpty());
 
@@ -8174,38 +8164,38 @@ JS::GCCellPtr::GCCellPtr(const Value& v)
 
 JS::TraceKind JS::GCCellPtr::outOfLineKind() const {
   MOZ_ASSERT((ptr & OutOfLineTraceKindMask) == OutOfLineTraceKindMask);
   MOZ_ASSERT(asCell()->isTenured());
   return MapAllocToTraceKind(asCell()->asTenured().getAllocKind());
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
-void js::gc::CheckHashTablesAfterMovingGC(JSRuntime* rt) {
+void GCRuntime::checkHashTablesAfterMovingGC() {
   /*
    * Check that internal hash tables no longer have any pointers to things
    * that have been moved.
    */
   rt->geckoProfiler().checkStringsMapAfterMovingGC();
-  for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, SkipAtoms); !zone.done(); zone.next()) {
     zone->checkUniqueIdTableAfterMovingGC();
     zone->checkInitialShapesTableAfterMovingGC();
     zone->checkBaseShapeTableAfterMovingGC();
     zone->checkAllCrossCompartmentWrappersAfterMovingGC();
     zone->checkScriptMapsAfterMovingGC();
 
     JS::AutoCheckCannotGC nogc;
     for (auto baseShape = zone->cellIterUnsafe<BaseShape>(); !baseShape.done();
          baseShape.next()) {
       ShapeCachePtr p = baseShape->getCache(nogc);
       p.checkAfterMovingGC();
     }
   }
 
-  for (CompartmentsIter c(rt); !c.done(); c.next()) {
+  for (CompartmentsIter c(this); !c.done(); c.next()) {
     for (RealmsInCompartmentIter r(c); !r.done(); r.next()) {
       r->checkObjectGroupTablesAfterMovingGC();
       r->dtoaCache.checkCacheAfterMovingGC();
       if (r->debugEnvs()) {
         r->debugEnvs()->checkHashTablesAfterMovingGC();
       }
     }
   }
@@ -8354,17 +8344,17 @@ JS::UniqueChars JS::GCDescription::slice
 
 JS::UniqueChars JS::GCDescription::formatJSONProfiler(JSContext* cx) const {
   return cx->runtime()->gc.stats().renderJsonMessage(
       0, js::gcstats::Statistics::JSONUse::PROFILER);
 }
 
 JS_PUBLIC_API JS::UniqueChars JS::MinorGcToJSON(JSContext* cx) {
   JSRuntime* rt = cx->runtime();
-  return rt->gc.stats().renderNurseryJson(rt);
+  return rt->gc.stats().renderNurseryJson();
 }
 
 JS_PUBLIC_API JS::GCSliceCallback JS::SetGCSliceCallback(
     JSContext* cx, GCSliceCallback callback) {
   return cx->runtime()->gc.setSliceCallback(callback);
 }
 
 JS_PUBLIC_API JS::DoCycleCollectionCallback JS::SetDoCycleCollectionCallback(
--- a/js/src/gc/GCParallelTask.h
+++ b/js/src/gc/GCParallelTask.h
@@ -21,18 +21,19 @@ class AutoLockHelperThreadState;
 //
 // Note that we don't use virtual functions here because destructors can write
 // the vtable pointer on entry, which can causes races if synchronization
 // happens there.
 class GCParallelTask : public RunnableTask {
  public:
   using TaskFunc = void (*)(GCParallelTask*);
 
+  gc::GCRuntime* const gc;
+
  private:
-  JSRuntime* const runtime_;
   TaskFunc func_;
 
   // The state of the parallel computation.
   enum class State { NotStarted, Dispatched, Finishing, Finished };
   UnprotectedData<State> state_;
 
   // Amount of time this task took to execute.
   MainThreadOrGCTaskData<mozilla::TimeDuration> duration_;
@@ -41,50 +42,48 @@ class GCParallelTask : public RunnableTa
 
  protected:
   // A flag to signal a request for early completion of the off-thread task.
   mozilla::Atomic<bool, mozilla::MemoryOrdering::ReleaseAcquire,
                   mozilla::recordreplay::Behavior::DontPreserve>
       cancel_;
 
  public:
-  explicit GCParallelTask(JSRuntime* runtime, TaskFunc func)
-      : runtime_(runtime),
+  explicit GCParallelTask(gc::GCRuntime* gc, TaskFunc func)
+      : gc(gc),
         func_(func),
         state_(State::NotStarted),
         duration_(nullptr),
         cancel_(false) {}
   GCParallelTask(GCParallelTask&& other)
-      : runtime_(other.runtime_),
+      : gc(other.gc),
         func_(other.func_),
         state_(other.state_),
         duration_(nullptr),
         cancel_(false) {}
 
   // Derived classes must override this to ensure that join() gets called
   // before members get destructed.
   virtual ~GCParallelTask();
 
-  JSRuntime* runtime() { return runtime_; }
-
   // Time spent in the most recent invocation of this task.
   mozilla::TimeDuration duration() const { return duration_; }
 
   // The simple interface to a parallel task works exactly like pthreads.
   MOZ_MUST_USE bool start();
   void join();
 
   // If multiple tasks are to be started or joined at once, it is more
   // efficient to take the helper thread lock once and use these methods.
   MOZ_MUST_USE bool startWithLockHeld(AutoLockHelperThreadState& locked);
   void joinWithLockHeld(AutoLockHelperThreadState& locked);
 
   // Instead of dispatching to a helper, run the task on the current thread.
-  void runFromMainThread(JSRuntime* rt);
-  void joinAndRunFromMainThread(JSRuntime* rt);
+  void runFromMainThread();
+  void joinAndRunFromMainThread();
 
   // If the task is not already running, either start it or run it on the main
   // thread if that fails.
   void startOrRunIfIdle(AutoLockHelperThreadState& lock);
 
   // Dispatch a cancelation request.
   void cancelAndWait() {
     cancel_ = true;
@@ -146,18 +145,18 @@ class GCParallelTask : public RunnableTa
  public:
   void runFromHelperThread(AutoLockHelperThreadState& locked);
 };
 
 // CRTP template to handle cast to derived type when calling run().
 template <typename Derived>
 class GCParallelTaskHelper : public GCParallelTask {
  public:
-  explicit GCParallelTaskHelper(JSRuntime* runtime)
-      : GCParallelTask(runtime, &runTaskTyped) {}
+  explicit GCParallelTaskHelper(gc::GCRuntime* gc)
+      : GCParallelTask(gc, &runTaskTyped) {}
   GCParallelTaskHelper(GCParallelTaskHelper&& other)
       : GCParallelTask(std::move(other)) {}
 
  private:
   static void runTaskTyped(GCParallelTask* task) {
     static_cast<Derived*>(task)->run();
   }
 };
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -113,48 +113,48 @@ class ChunkPool {
 
    private:
     Chunk* current_;
   };
 };
 
 class BackgroundSweepTask : public GCParallelTaskHelper<BackgroundSweepTask> {
  public:
-  explicit BackgroundSweepTask(JSRuntime* rt) : GCParallelTaskHelper(rt) {}
+  explicit BackgroundSweepTask(GCRuntime* gc) : GCParallelTaskHelper(gc) {}
   void run();
 };
 
 class BackgroundFreeTask : public GCParallelTaskHelper<BackgroundFreeTask> {
  public:
-  explicit BackgroundFreeTask(JSRuntime* rt) : GCParallelTaskHelper(rt) {}
+  explicit BackgroundFreeTask(GCRuntime* gc) : GCParallelTaskHelper(gc) {}
   void run();
 };
 
 // Performs extra allocation off thread so that when memory is required on the
 // main thread it will already be available and waiting.
 class BackgroundAllocTask : public GCParallelTaskHelper<BackgroundAllocTask> {
   // Guarded by the GC lock.
   GCLockData<ChunkPool&> chunkPool_;
 
   const bool enabled_;
 
  public:
-  BackgroundAllocTask(JSRuntime* rt, ChunkPool& pool);
+  BackgroundAllocTask(GCRuntime* gc, ChunkPool& pool);
   bool enabled() const { return enabled_; }
 
   void run();
 };
 
 // Search the provided Chunks for free arenas and decommit them.
 class BackgroundDecommitTask
     : public GCParallelTaskHelper<BackgroundDecommitTask> {
  public:
   using ChunkVector = mozilla::Vector<Chunk*>;
 
-  explicit BackgroundDecommitTask(JSRuntime* rt) : GCParallelTaskHelper(rt) {}
+  explicit BackgroundDecommitTask(GCRuntime* gc) : GCParallelTaskHelper(gc) {}
   void setChunksToScan(ChunkVector& chunks);
 
   void run();
 
  private:
   MainThreadOrGCTaskData<ChunkVector> toDecommit;
 };
 
@@ -474,16 +474,20 @@ class GCRuntime {
   void endVerifyPreBarriers();
   void finishVerifier();
   bool isVerifyPreBarriersEnabled() const { return verifyPreData; }
   bool shouldYieldForZeal(ZealMode mode);
 #else
   bool isVerifyPreBarriersEnabled() const { return false; }
 #endif
 
+#ifdef JSGC_HASH_TABLE_CHECKS
+  void checkHashTablesAfterMovingGC();
+#endif
+
   // Queue memory memory to be freed on a background thread if possible.
   void queueUnusedLifoBlocksForFree(LifoAlloc* lifo);
   void queueAllLifoBlocksForFree(LifoAlloc* lifo);
   void queueAllLifoBlocksForFreeAfterMinorGC(LifoAlloc* lifo);
   void queueBuffersForFreeAfterMinorGC(Nursery::BufferSet& buffers);
 
   // Public here for ReleaseArenaLists and FinalizeTypedArenas.
   void releaseArena(Arena* arena, const AutoLockGC& lock);
@@ -563,16 +567,18 @@ class GCRuntime {
   bool wantBackgroundAllocation(const AutoLockGC& lock) const;
   bool startBackgroundAllocTaskIfIdle();
 
   void requestMajorGC(JS::GCReason reason);
   SliceBudget defaultBudget(JS::GCReason reason, int64_t millis);
   IncrementalResult budgetIncrementalGC(bool nonincrementalByAPI,
                                         JS::GCReason reason,
                                         SliceBudget& budget);
+  void checkZoneIsScheduled(Zone* zone, JS::GCReason reason,
+                            const char* trigger);
   IncrementalResult resetIncrementalGC(AbortReason reason);
 
   // Assert if the system state is such that we should never
   // receive a request to do GC work.
   void checkCanCallAPI();
 
   // Check if the system state is such that GC has been supressed
   // or otherwise delayed.
@@ -610,17 +616,21 @@ class GCRuntime {
 
   void pushZealSelectedObjects();
   void purgeRuntime();
   MOZ_MUST_USE bool beginMarkPhase(JS::GCReason reason, AutoGCSession& session);
   bool prepareZonesForCollection(JS::GCReason reason, bool* isFullOut);
   bool shouldPreserveJITCode(JS::Realm* realm,
                              const mozilla::TimeStamp& currentTime,
                              JS::GCReason reason, bool canAllocateMoreCode);
+  void discardJITCodeForGC();
   void startBackgroundFreeAfterMinorGC();
+  void relazifyFunctionsForShrinkingGC();
+  void purgeShapeCachesForShrinkingGC();
+  void purgeSourceURLsForShrinkingGC();
   void traceRuntimeForMajorGC(JSTracer* trc, AutoGCSession& session);
   void traceRuntimeAtoms(JSTracer* trc, const AutoAccessAtomsZone& atomsAccess);
   void traceKeptAtoms(JSTracer* trc);
   void traceRuntimeCommon(JSTracer* trc, TraceOrMarkRuntime traceOrMark);
   void traceEmbeddingBlackRoots(JSTracer* trc);
   void traceEmbeddingGrayRoots(JSTracer* trc);
   void checkNoRuntimeRoots(AutoGCSession& session);
   void maybeDoCycleCollection();
@@ -633,39 +643,41 @@ class GCRuntime {
   void markWeakReferencesInCurrentGroup(gcstats::PhaseKind phase);
   template <class ZoneIterT>
   void markGrayRoots(gcstats::PhaseKind phase);
   void markBufferedGrayRoots(JS::Zone* zone);
   void markAllWeakReferences(gcstats::PhaseKind phase);
   void markAllGrayReferences(gcstats::PhaseKind phase);
 
   void beginSweepPhase(JS::GCReason reason, AutoGCSession& session);
+  void dropStringWrappers();
   void groupZonesForSweeping(JS::GCReason reason);
   MOZ_MUST_USE bool findSweepGroupEdges();
   void getNextSweepGroup();
   IncrementalProgress markGrayReferencesInCurrentGroup(JSFreeOp* fop,
                                                        SliceBudget& budget);
   IncrementalProgress endMarkingSweepGroup(JSFreeOp* fop, SliceBudget& budget);
   void markIncomingCrossCompartmentPointers(MarkColor color);
   IncrementalProgress beginSweepingSweepGroup(JSFreeOp* fop,
                                               SliceBudget& budget);
+  void updateAtomsBitmap();
   void sweepDebuggerOnMainThread(JSFreeOp* fop);
   void sweepJitDataOnMainThread(JSFreeOp* fop);
   IncrementalProgress endSweepingSweepGroup(JSFreeOp* fop, SliceBudget& budget);
   IncrementalProgress performSweepActions(SliceBudget& sliceBudget);
   IncrementalProgress sweepTypeInformation(JSFreeOp* fop, SliceBudget& budget);
   IncrementalProgress releaseSweptEmptyArenas(JSFreeOp* fop,
                                               SliceBudget& budget);
   void startSweepingAtomsTable();
   IncrementalProgress sweepAtomsTable(JSFreeOp* fop, SliceBudget& budget);
   IncrementalProgress sweepWeakCaches(JSFreeOp* fop, SliceBudget& budget);
   IncrementalProgress finalizeAllocKind(JSFreeOp* fop, SliceBudget& budget);
   IncrementalProgress sweepShapeTree(JSFreeOp* fop, SliceBudget& budget);
   void endSweepPhase(bool lastGC);
-  bool allCCVisibleZonesWereCollected() const;
+  bool allCCVisibleZonesWereCollected();
   void sweepZones(JSFreeOp* fop, bool destroyingRuntime);
   void decommitFreeArenasWithoutUnlocking(const AutoLockGC& lock);
   void startDecommit();
   void queueZonesAndStartBackgroundSweep(ZoneList& zones);
   void sweepFromBackgroundThread(AutoLockHelperThreadState& lock);
   void startBackgroundFree();
   void freeFromBackgroundThread(AutoLockHelperThreadState& lock);
   void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks);
@@ -809,17 +821,17 @@ class GCRuntime {
   // accumulation can fail, but in that case we switch to non-incremental GC.
   enum class GrayBufferState { Unused, Okay, Failed };
   MainThreadOrGCTaskData<GrayBufferState> grayBufferState;
   bool hasValidGrayRootsBuffer() const {
     return grayBufferState == GrayBufferState::Okay;
   }
 
   // Clear each zone's gray buffers, but do not change the current state.
-  void resetBufferedGrayRoots() const;
+  void resetBufferedGrayRoots();
 
   // Reset the gray buffering state to Unused.
   void clearBufferedGrayRoots() {
     grayBufferState = GrayBufferState::Unused;
     resetBufferedGrayRoots();
   }
 
   /*
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -122,17 +122,17 @@ inline Chunk* js::NurseryChunk::toChunk(
   auto chunk = reinterpret_cast<Chunk*>(this);
   chunk->init(gc);
   return chunk;
 }
 
 void js::NurseryDecommitTask::queueChunk(
     NurseryChunk* nchunk, const AutoLockHelperThreadState& lock) {
   // Using the chunk pointers to build the queue is infallible.
-  Chunk* chunk = nchunk->toChunk(&runtime()->gc);
+  Chunk* chunk = nchunk->toChunk(gc);
   chunk->info.prev = nullptr;
   chunk->info.next = queue;
   queue = chunk;
 }
 
 void js::NurseryDecommitTask::queueRange(
     size_t newCapacity, NurseryChunk& newChunk,
     const AutoLockHelperThreadState& lock) {
@@ -185,17 +185,16 @@ void js::NurseryDecommitTask::run() {
 
     setFinishing(lock);
   }
 }
 
 void js::NurseryDecommitTask::decommitChunk(Chunk* chunk) {
   chunk->decommitAllArenas();
   {
-    GCRuntime* gc = &runtime()->gc;
     AutoLockGC lock(gc);
     gc->recycleChunk(chunk, lock);
   }
 }
 
 void js::NurseryDecommitTask::decommitRange(AutoLockHelperThreadState& lock) {
   // Clear this field here before releasing the lock. While the lock is
   // released the main thread may make new decommit requests or update the range
@@ -220,17 +219,17 @@ js::Nursery::Nursery(GCRuntime* gc)
       currentChunk_(0),
       capacity_(0),
       timeInChunkAlloc_(0),
       profileThreshold_(0),
       enableProfiling_(false),
       canAllocateStrings_(true),
       reportTenurings_(0),
       minorGCTriggerReason_(JS::GCReason::NO_REASON),
-      decommitTask(gc->rt)
+      decommitTask(gc)
 #ifdef JS_GC_ZEAL
       ,
       lastCanary_(nullptr)
 #endif
 {
   const char* env = getenv("MOZ_NURSERY_STRINGS");
   if (env && *env) {
     canAllocateStrings_ = (*env == '1');
@@ -1086,17 +1085,17 @@ void js::Nursery::doCollection(JS::GCRea
   startProfile(ProfileKey::ClearStoreBuffer);
   gc->storeBuffer().clear();
   endProfile(ProfileKey::ClearStoreBuffer);
 
   // Make sure hashtables have been updated after the collection.
   startProfile(ProfileKey::CheckHashTables);
 #ifdef JS_GC_ZEAL
   if (gc->hasZealMode(ZealMode::CheckHashTablesOnMinorGC)) {
-    CheckHashTablesAfterMovingGC(rt);
+    gc->checkHashTablesAfterMovingGC();
   }
 #endif
   endProfile(ProfileKey::CheckHashTables);
 
   previousGC.reason = reason;
   previousGC.nurseryCapacity = initialNurseryCapacity;
   previousGC.nurseryCommitted = spaceToEnd(allocatedChunkCount());
   previousGC.nurseryUsedBytes = initialNurseryUsedBytes;
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -70,17 +70,17 @@ class TenuredCell;
 }  // namespace gc
 
 namespace jit {
 class MacroAssembler;
 }  // namespace jit
 
 class NurseryDecommitTask : public GCParallelTaskHelper<NurseryDecommitTask> {
  public:
-  explicit NurseryDecommitTask(JSRuntime* rt) : GCParallelTaskHelper(rt) {}
+  explicit NurseryDecommitTask(gc::GCRuntime* gc) : GCParallelTaskHelper(gc) {}
 
   void queueChunk(NurseryChunk* chunk, const AutoLockHelperThreadState& lock);
 
   // queueRange can also update the current to-decommit range of the
   // current chunk.
   void queueRange(size_t newCapacity, NurseryChunk& chunk,
                   const AutoLockHelperThreadState& lock);
 
--- a/js/src/gc/PrivateIterators-inl.h
+++ b/js/src/gc/PrivateIterators-inl.h
@@ -45,26 +45,28 @@ class GrayObjectIter : public ZoneAllCel
   operator JSObject*() const { return get(); }
   JSObject* operator->() const { return get(); }
 };
 
 class GCZonesIter {
   ZonesIter zone;
 
  public:
-  explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms)
-      : zone(rt, selector) {
+  explicit GCZonesIter(GCRuntime* gc, ZoneSelector selector = WithAtoms)
+      : zone(gc, selector) {
     MOZ_ASSERT(JS::RuntimeHeapIsBusy());
-    MOZ_ASSERT_IF(rt->gc.atomsZone->isCollectingFromAnyThread(),
-                  !rt->hasHelperThreadZones());
+    MOZ_ASSERT_IF(gc->atomsZone->isCollectingFromAnyThread(),
+                  !gc->rt->hasHelperThreadZones());
 
     if (!done() && !zone->isCollectingFromAnyThread()) {
       next();
     }
   }
+  explicit GCZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms)
+      : GCZonesIter(&rt->gc, selector) {}
 
   bool done() const { return zone.done(); }
 
   void next() {
     MOZ_ASSERT(!done());
     do {
       zone.next();
     } while (!zone.done() && !zone->isCollectingFromAnyThread());
@@ -84,22 +86,24 @@ using GCCompartmentsIter =
 using GCRealmsIter = CompartmentsOrRealmsIterT<GCZonesIter, RealmsInZoneIter>;
 
 /* Iterates over all zones in the current sweep group. */
 class SweepGroupZonesIter {
   JS::Zone* current;
   ZoneSelector selector;
 
  public:
-  explicit SweepGroupZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms)
+  explicit SweepGroupZonesIter(GCRuntime* gc, ZoneSelector selector = WithAtoms)
       : selector(selector) {
     MOZ_ASSERT(CurrentThreadIsPerformingGC());
-    current = rt->gc.getCurrentSweepGroup();
+    current = gc->getCurrentSweepGroup();
     maybeSkipAtomsZone();
   }
+  explicit SweepGroupZonesIter(JSRuntime* rt, ZoneSelector selector = WithAtoms)
+      : SweepGroupZonesIter(&rt->gc, selector) {}
 
   void maybeSkipAtomsZone() {
     if (selector == SkipAtoms && current && current->isAtomsZone()) {
       current = current->nextNodeInGroup();
       MOZ_ASSERT_IF(current, !current->isAtomsZone());
     }
   }
 
--- a/js/src/gc/PublicIterators.h
+++ b/js/src/gc/PublicIterators.h
@@ -33,25 +33,27 @@ enum ZoneSelector { WithAtoms, SkipAtoms
 // parse threads.
 class ZonesIter {
   gc::AutoEnterIteration iterMarker;
   JS::Zone* atomsZone;
   JS::Zone** it;
   JS::Zone** end;
 
  public:
-  ZonesIter(JSRuntime* rt, ZoneSelector selector)
-      : iterMarker(&rt->gc),
-        atomsZone(selector == WithAtoms ? rt->gc.atomsZone.ref() : nullptr),
-        it(rt->gc.zones().begin()),
-        end(rt->gc.zones().end()) {
+  ZonesIter(gc::GCRuntime* gc, ZoneSelector selector)
+      : iterMarker(gc),
+        atomsZone(selector == WithAtoms ? gc->atomsZone.ref() : nullptr),
+        it(gc->zones().begin()),
+        end(gc->zones().end()) {
     if (!atomsZone) {
       skipHelperThreadZones();
     }
   }
+  ZonesIter(JSRuntime* rt, ZoneSelector selector)
+      : ZonesIter(&rt->gc, selector) {}
 
   bool done() const { return !atomsZone && it == end; }
 
   void next() {
     MOZ_ASSERT(!done());
     if (atomsZone) {
       atomsZone = nullptr;
     } else {
@@ -179,22 +181,24 @@ template <class ZonesIterT, class InnerI
 class CompartmentsOrRealmsIterT {
   using T = typename InnerIterT::ItemType;
 
   gc::AutoEnterIteration iterMarker;
   ZonesIterT zone;
   mozilla::Maybe<InnerIterT> inner;
 
  public:
-  explicit CompartmentsOrRealmsIterT(JSRuntime* rt)
-      : iterMarker(&rt->gc), zone(rt, SkipAtoms) {
+  explicit CompartmentsOrRealmsIterT(gc::GCRuntime* gc)
+      : iterMarker(gc), zone(gc, SkipAtoms) {
     if (!zone.done()) {
       inner.emplace(zone);
     }
   }
+  explicit CompartmentsOrRealmsIterT(JSRuntime* rt)
+      : CompartmentsOrRealmsIterT(&rt->gc) {}
 
   bool done() const { return zone.done(); }
 
   void next() {
     MOZ_ASSERT(!done());
     MOZ_ASSERT(!inner.ref().done());
     inner->next();
     if (inner->done()) {
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -333,17 +333,17 @@ void js::gc::GCRuntime::traceRuntimeAtom
   TraceWellKnownSymbols(trc);
   jit::JitRuntime::Trace(trc, access);
 }
 
 void js::gc::GCRuntime::traceKeptAtoms(JSTracer* trc) {
   // We don't have exact rooting information for atoms while parsing. When
   // this is happeninng we set a flag on the zone and trace all atoms in the
   // zone's cache.
-  for (GCZonesIter zone(trc->runtime()); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     if (zone->hasKeptAtoms()) {
       zone->traceAtomCache(trc);
     }
   }
 }
 
 void js::gc::GCRuntime::traceRuntimeCommon(JSTracer* trc,
                                            TraceOrMarkRuntime traceOrMark) {
@@ -387,17 +387,17 @@ void js::gc::GCRuntime::traceRuntimeComm
   for (RealmsIter r(rt); !r.done(); r.next()) {
     r->traceRoots(trc, traceOrMark);
   }
 
   // Trace zone script-table roots. See comment in
   // Zone::traceScriptTableRoots() for justification re: calling this only
   // during major (non-nursery) collections.
   if (!JS::RuntimeHeapIsMinorCollecting()) {
-    for (ZonesIter zone(rt, ZoneSelector::SkipAtoms); !zone.done();
+    for (ZonesIter zone(this, ZoneSelector::SkipAtoms); !zone.done();
          zone.next()) {
       zone->traceScriptTableRoots(trc);
     }
   }
 
   // Trace helper thread roots.
   HelperThreadState().trace(trc);
 
@@ -522,17 +522,17 @@ class BufferGrayRootsTracer final : publ
   }
 #endif
 };
 
 void js::gc::GCRuntime::bufferGrayRoots() {
   // Precondition: the state has been reset to "unused" after the last GC
   //               and the zone's buffers have been cleared.
   MOZ_ASSERT(grayBufferState == GrayBufferState::Unused);
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     MOZ_ASSERT(zone->gcGrayRoots().IsEmpty());
   }
 
   BufferGrayRootsTracer grayBufferer(rt);
   traceEmbeddingGrayRoots(&grayBufferer);
   Compartment::traceIncomingCrossCompartmentEdgesForZoneGC(
       &grayBufferer, Compartment::GrayEdges);
 
@@ -596,21 +596,21 @@ void GCRuntime::markBufferedGrayRoots(JS
     MOZ_ASSERT(IsCellPointerValid(cell));
 #endif
 
     TraceManuallyBarrieredGenericPointerEdge(&marker, &cell,
                                              "buffered gray root");
   }
 }
 
-void GCRuntime::resetBufferedGrayRoots() const {
+void GCRuntime::resetBufferedGrayRoots() {
   MOZ_ASSERT(
       grayBufferState != GrayBufferState::Okay,
       "Do not clear the gray buffers unless we are Failed or becoming Unused");
-  for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(this); !zone.done(); zone.next()) {
     zone->gcGrayRoots().Clear();
   }
 }
 
 JS_PUBLIC_API void JS::AddPersistentRoot(JS::RootingContext* cx, RootKind kind,
                                          PersistentRooted<void*>* root) {
   static_cast<JSContext*>(cx)->runtime()->heapRoots.ref()[kind].insertBack(
       root);
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -148,16 +148,20 @@ using PhaseTable = EnumeratedArray<Phase
 // A table of PhaseKindInfo indexed by PhaseKind.
 using PhaseKindTable =
     EnumeratedArray<PhaseKind, PhaseKind::LIMIT, PhaseKindInfo>;
 
 #include "gc/StatsPhasesGenerated.inc"
 
 static double t(TimeDuration duration) { return duration.ToMilliseconds(); }
 
+inline JSContext* Statistics::context() {
+  return gc->rt->mainContextFromOwnThread();
+}
+
 inline Phase Statistics::currentPhase() const {
   return phaseStack.empty() ? Phase::NONE : phaseStack.back();
 }
 
 PhaseKind Statistics::currentPhaseKind() const {
   // Public API to get the current phase kind, suppressing the synthetic
   // PhaseKind::MUTATOR phase.
 
@@ -565,23 +569,23 @@ UniqueChars Statistics::renderJsonSlice(
     return UniqueChars(nullptr);
   }
   JSONPrinter json(printer);
 
   formatJsonSlice(sliceNum, json);
   return printer.release();
 }
 
-UniqueChars Statistics::renderNurseryJson(JSRuntime* rt) const {
+UniqueChars Statistics::renderNurseryJson() const {
   Sprinter printer(nullptr, false);
   if (!printer.init()) {
     return UniqueChars(nullptr);
   }
   JSONPrinter json(printer);
-  rt->gc.nursery().renderProfileJSON(json);
+  gc->nursery().renderProfileJSON(json);
   return printer.release();
 }
 
 #ifdef DEBUG
 void Statistics::writeLogMessage(const char* fmt, ...) {
   va_list args;
   va_start(args, fmt);
   if (gcDebugFile) {
@@ -745,18 +749,18 @@ void Statistics::formatJsonPhaseTimes(co
   for (auto phase : AllPhases()) {
     TimeDuration ownTime = phaseTimes[phase];
     if (!ownTime.IsZero()) {
       json.property(phases[phase].path, ownTime, JSONPrinter::MILLISECONDS);
     }
   }
 }
 
-Statistics::Statistics(JSRuntime* rt)
-    : runtime(rt),
+Statistics::Statistics(GCRuntime* gc)
+    : gc(gc),
       gcTimerFile(nullptr),
       gcDebugFile(nullptr),
       nonincrementalReason_(gc::AbortReason::None),
       allocsSinceMinorGC({0, 0}),
       preTotalHeapBytes(0),
       postTotalHeapBytes(0),
       preCollectedHeapBytes(0),
       thresholdTriggered(false),
@@ -984,63 +988,63 @@ void Statistics::printStats() {
 
 void Statistics::beginGC(JSGCInvocationKind kind,
                          const TimeStamp& currentTime) {
   slices_.clearAndFree();
   sccTimes.clearAndFree();
   gckind = kind;
   nonincrementalReason_ = gc::AbortReason::None;
 
-  GCRuntime& gc = runtime->gc;
-  preTotalHeapBytes = gc.heapSize.bytes();
+  preTotalHeapBytes = gc->heapSize.bytes();
 
   preCollectedHeapBytes = 0;
 
-  startingMajorGCNumber = gc.majorGCCount();
-  startingSliceNumber = gc.gcNumber();
+  startingMajorGCNumber = gc->majorGCCount();
+  startingSliceNumber = gc->gcNumber();
 
-  if (gc.lastGCEndTime()) {
-    timeSinceLastGC = currentTime - gc.lastGCEndTime();
+  if (gc->lastGCEndTime()) {
+    timeSinceLastGC = currentTime - gc->lastGCEndTime();
   }
 }
 
 void Statistics::measureInitialHeapSize() {
   MOZ_ASSERT(preCollectedHeapBytes == 0);
-  for (GCZonesIter zone(runtime, WithAtoms); !zone.done(); zone.next()) {
+  for (GCZonesIter zone(gc, WithAtoms); !zone.done(); zone.next()) {
     preCollectedHeapBytes += zone->gcHeapSize.bytes();
   }
 }
 
 void Statistics::adoptHeapSizeDuringIncrementalGC(Zone* mergedZone) {
   // A zone is being merged into a zone that's currently being collected so we
   // need to adjust our record of the total size of heap for collected zones.
-  MOZ_ASSERT(runtime->gc.isIncrementalGCInProgress());
+  MOZ_ASSERT(gc->isIncrementalGCInProgress());
   preCollectedHeapBytes += mergedZone->gcHeapSize.bytes();
 }
 
 void Statistics::endGC() {
-  postTotalHeapBytes = runtime->gc.heapSize.bytes();
+  postTotalHeapBytes = gc->heapSize.bytes();
 
   sendGCTelemetry();
 
   thresholdTriggered = false;
 }
 
 void Statistics::sendGCTelemetry() {
+  JSRuntime* runtime = gc->rt;
   runtime->addTelemetry(JS_TELEMETRY_GC_IS_ZONE_GC,
                         !zoneStats.isFullCollection());
   TimeDuration markTotal = SumPhase(PhaseKind::MARK, phaseTimes);
   TimeDuration markRootsTotal = SumPhase(PhaseKind::MARK_ROOTS, phaseTimes);
   double markTime = t(markTotal);
-  size_t markCount = runtime->gc.marker.getMarkCount();
+  size_t markCount = gc->marker.getMarkCount();
   double markRate = markCount / markTime;
   runtime->addTelemetry(JS_TELEMETRY_GC_MARK_MS, markTime);
   runtime->addTelemetry(JS_TELEMETRY_GC_MARK_RATE, markRate);
   runtime->addTelemetry(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[Phase::SWEEP]));
-  if (runtime->gc.isCompactingGc()) {
+  if (gc->isCompactingGc()) {
     runtime->addTelemetry(JS_TELEMETRY_GC_COMPACT_MS,
                           t(phaseTimes[Phase::COMPACT]));
   }
   runtime->addTelemetry(JS_TELEMETRY_GC_MARK_ROOTS_MS, t(markRootsTotal));
   runtime->addTelemetry(JS_TELEMETRY_GC_MARK_GRAY_MS,
                         t(phaseTimes[Phase::SWEEP_MARK_GRAY]));
   runtime->addTelemetry(JS_TELEMETRY_GC_NON_INCREMENTAL, nonincremental());
   if (nonincremental()) {
@@ -1057,17 +1061,17 @@ void Statistics::sendGCTelemetry() {
   const auto& lastSlice = slices_.back();
   runtime->addTelemetry(JS_TELEMETRY_GC_RESET, lastSlice.wasReset());
   if (lastSlice.wasReset()) {
     runtime->addTelemetry(JS_TELEMETRY_GC_RESET_REASON,
                           uint32_t(lastSlice.resetReason));
   }
 
   runtime->addTelemetry(JS_TELEMETRY_GC_INCREMENTAL_DISABLED,
-                        !runtime->gc.isIncrementalGCAllowed());
+                        !gc->isIncrementalGCAllowed());
 
   TimeDuration sccTotal, sccLongest;
   sccDurations(&sccTotal, &sccLongest);
   runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS, t(sccTotal));
   runtime->addTelemetry(JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS, t(sccLongest));
 
   TimeDuration total, longest;
   gcDuration(&total, &longest);
@@ -1110,68 +1114,67 @@ void Statistics::sendGCTelemetry() {
         (double(bytesFreed) / (1024.0 * 1024.0)) / clampedTotal.ToSeconds();
     runtime->addTelemetry(JS_TELEMETRY_GC_EFFECTIVENESS,
                           uint32_t(effectiveness));
   }
 }
 
 void Statistics::beginNurseryCollection(JS::GCReason reason) {
   count(COUNT_MINOR_GC);
-  startingMinorGCNumber = runtime->gc.minorGCCount();
+  startingMinorGCNumber = gc->minorGCCount();
   if (nurseryCollectionCallback) {
     (*nurseryCollectionCallback)(
-        runtime->mainContextFromOwnThread(),
-        JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START, reason);
+        context(), JS::GCNurseryProgress::GC_NURSERY_COLLECTION_START, reason);
   }
 }
 
 void Statistics::endNurseryCollection(JS::GCReason reason) {
   if (nurseryCollectionCallback) {
     (*nurseryCollectionCallback)(
-        runtime->mainContextFromOwnThread(),
-        JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END, reason);
+        context(), JS::GCNurseryProgress::GC_NURSERY_COLLECTION_END, reason);
   }
 
   allocsSinceMinorGC = {0, 0};
 }
 
 void Statistics::beginSlice(const ZoneGCStats& zoneStats,
                             JSGCInvocationKind gckind, SliceBudget budget,
                             JS::GCReason reason) {
   MOZ_ASSERT(phaseStack.empty() ||
              (phaseStack.length() == 1 && phaseStack[0] == Phase::MUTATOR));
 
   this->zoneStats = zoneStats;
 
   TimeStamp currentTime = ReallyNow();
 
-  bool first = !runtime->gc.isIncrementalGCInProgress();
+  bool first = !gc->isIncrementalGCInProgress();
   if (first) {
     beginGC(gckind, currentTime);
   }
 
+  JSRuntime* runtime = gc->rt;
   if (!runtime->parentRuntime && !slices_.empty()) {
     TimeDuration timeSinceLastSlice = currentTime - slices_.back().end;
     runtime->addTelemetry(JS_TELEMETRY_GC_TIME_BETWEEN_SLICES_MS,
                           uint32_t(timeSinceLastSlice.ToMilliseconds()));
   }
 
   if (!slices_.emplaceBack(budget, reason, currentTime, GetPageFaultCount(),
-                           runtime->gc.state())) {
+                           gc->state())) {
     // If we are OOM, set a flag to indicate we have missing slice data.
     aborted = true;
     return;
   }
 
   runtime->addTelemetry(JS_TELEMETRY_GC_REASON, uint32_t(reason));
 
   // Slice callbacks should only fire for the outermost level.
   bool wasFullGC = zoneStats.isFullCollection();
   if (sliceCallback) {
-    JSContext* cx = runtime->mainContextFromOwnThread();
+    JSContext* cx = context();
     JS::GCDescription desc(!wasFullGC, false, gckind, reason);
     if (first) {
       (*sliceCallback)(cx, JS::GC_CYCLE_BEGIN, desc);
     }
     (*sliceCallback)(cx, JS::GC_SLICE_BEGIN, desc);
   }
 
   writeLogMessage("begin slice");
@@ -1180,26 +1183,26 @@ void Statistics::beginSlice(const ZoneGC
 void Statistics::endSlice() {
   MOZ_ASSERT(phaseStack.empty() ||
              (phaseStack.length() == 1 && phaseStack[0] == Phase::MUTATOR));
 
   if (!aborted) {
     auto& slice = slices_.back();
     slice.end = ReallyNow();
     slice.endFaults = GetPageFaultCount();
-    slice.finalState = runtime->gc.state();
+    slice.finalState = gc->state();
 
     writeLogMessage("end slice");
 
     sendSliceTelemetry(slice);
 
     sliceCount_++;
   }
 
-  bool last = !runtime->gc.isIncrementalGCInProgress();
+  bool last = !gc->isIncrementalGCInProgress();
   if (last) {
     if (gcTimerFile) {
       printStats();
     }
 
     if (!aborted) {
       endGC();
     }
@@ -1209,17 +1212,17 @@ void Statistics::endSlice() {
       slices_.back().duration() >= profileThreshold_) {
     printSliceProfile();
   }
 
   // Slice callbacks should only fire for the outermost level.
   if (!aborted) {
     bool wasFullGC = zoneStats.isFullCollection();
     if (sliceCallback) {
-      JSContext* cx = runtime->mainContextFromOwnThread();
+      JSContext* cx = context();
       JS::GCDescription desc(!wasFullGC, last, gckind, slices_.back().reason);
       (*sliceCallback)(cx, JS::GC_SLICE_END, desc);
       if (last) {
         (*sliceCallback)(cx, JS::GC_CYCLE_END, desc);
       }
     }
   }
 
@@ -1251,16 +1254,17 @@ void Statistics::endSlice() {
     phaseStartTimes[Phase::MUTATOR] = mutatorStartTime;
     phaseTimes[Phase::MUTATOR] = mutatorTime;
   }
 
   aborted = false;
 }
 
 void Statistics::sendSliceTelemetry(const SliceData& slice) {
+  JSRuntime* runtime = gc->rt;
   TimeDuration sliceTime = slice.end - slice.start;
   runtime->addTelemetry(JS_TELEMETRY_GC_SLICE_MS, t(sliceTime));
 
   if (slice.budget.isTimeBudget()) {
     int64_t budget_ms = slice.budget.timeBudget.budget;
     runtime->addTelemetry(JS_TELEMETRY_GC_BUDGET_MS, budget_ms);
     if (IsCurrentlyAnimating(runtime->lastAnimationTime, slice.end)) {
       runtime->addTelemetry(JS_TELEMETRY_GC_ANIMATION_MS, t(sliceTime));
@@ -1286,16 +1290,17 @@ void Statistics::sendSliceTelemetry(cons
     if (overrun > 0) {
       runtime->addTelemetry(JS_TELEMETRY_GC_BUDGET_OVERRUN, uint32_t(overrun));
     }
   }
 }
 
 void Statistics::reportLongestPhaseInMajorGC(PhaseKind longest,
                                              int telemetryId) {
+  JSRuntime* runtime = gc->rt;
   if (longest != PhaseKind::NONE) {
     uint8_t bucket = phaseKinds[longest].telemetryBucket;
     runtime->addTelemetry(telemetryId, bucket);
   }
 }
 
 bool Statistics::startTimingMutator() {
   if (phaseStack.length() != 0) {
@@ -1366,17 +1371,17 @@ void Statistics::beginPhase(PhaseKind ph
   if (currentPhase() == Phase::MUTATOR) {
     suspendPhases(PhaseKind::IMPLICIT_SUSPENSION);
   }
 
   recordPhaseBegin(lookupChildPhase(phaseKind));
 }
 
 void Statistics::recordPhaseBegin(Phase phase) {
-  MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
+  MOZ_ASSERT(CurrentThreadCanAccessRuntime(gc->rt));
 
   // Guard against any other re-entry.
   MOZ_ASSERT(!phaseStartTimes[phase]);
 
   MOZ_ASSERT(phaseStack.length() < MAX_PHASE_NESTING);
 
   Phase current = currentPhase();
   MOZ_ASSERT(phases[phase].parent == current);
@@ -1393,17 +1398,17 @@ void Statistics::recordPhaseBegin(Phase 
   }
 
   phaseStack.infallibleAppend(phase);
   phaseStartTimes[phase] = now;
   writeLogMessage("begin: %s", phases[phase].path);
 }
 
 void Statistics::recordPhaseEnd(Phase phase) {
-  MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
+  MOZ_ASSERT(CurrentThreadCanAccessRuntime(gc->rt));
 
   MOZ_ASSERT(phaseStartTimes[phase]);
 
   TimeStamp now = ReallyNow();
 
   // Make sure this phase ends after it starts.
   MOZ_ASSERT(now >= phaseStartTimes[phase],
              "Inconsistent time data; see bug 1400153");
@@ -1465,17 +1470,17 @@ void Statistics::endPhase(PhaseKind phas
   if (phaseStack.empty() && !suspendedPhases.empty() &&
       suspendedPhases.back() == Phase::IMPLICIT_SUSPENSION) {
     resumePhases();
   }
 }
 
 void Statistics::recordParallelPhase(PhaseKind phaseKind,
                                      TimeDuration duration) {
-  MOZ_ASSERT(CurrentThreadCanAccessRuntime(runtime));
+  MOZ_ASSERT(CurrentThreadCanAccessRuntime(gc->rt));
 
   Phase phase = lookupChildPhase(phaseKind);
 
   // Record the duration for all phases in the tree up to the root. This is
   // not strictly necessary but makes the invariant that parent phase times
   // include their children apply to both phaseTimes and parallelTimes.
   while (phase != Phase::NONE) {
     if (!slices_.empty()) {
@@ -1537,17 +1542,17 @@ double Statistics::computeMMU(TimeDurati
 
   return double((window - gcMax) / window);
 }
 
 void Statistics::maybePrintProfileHeaders() {
   static int printedHeader = 0;
   if ((printedHeader++ % 200) == 0) {
     printProfileHeader();
-    if (runtime->gc.nursery().enableProfiling()) {
+    if (gc->nursery().enableProfiling()) {
       Nursery::printProfileHeader();
     }
   }
 }
 
 void Statistics::printProfileHeader() {
   if (!enableProfiling_) {
     return;
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -132,17 +132,17 @@ struct Statistics {
   using TimeDuration = mozilla::TimeDuration;
   using TimeStamp = mozilla::TimeStamp;
 
   // Create a convenient type for referring to tables of phase times.
   using PhaseTimeTable = EnumeratedArray<Phase, Phase::LIMIT, TimeDuration>;
 
   static MOZ_MUST_USE bool initialize();
 
-  explicit Statistics(JSRuntime* rt);
+  explicit Statistics(gc::GCRuntime* gc);
   ~Statistics();
 
   Statistics(const Statistics&) = delete;
   Statistics& operator=(const Statistics&) = delete;
 
   void beginPhase(PhaseKind phaseKind);
   void endPhase(PhaseKind phaseKind);
   void recordParallelPhase(PhaseKind phaseKind, TimeDuration duration);
@@ -294,27 +294,27 @@ struct Statistics {
   // Return JSON for a whole major GC.  If use == PROFILER then
   // detailed per-slice data and some other fields will be included.
   UniqueChars renderJsonMessage(uint64_t timestamp, JSONUse use) const;
 
   // Return JSON for the timings of just the given slice.
   UniqueChars renderJsonSlice(size_t sliceNum) const;
 
   // Return JSON for the previous nursery collection.
-  UniqueChars renderNurseryJson(JSRuntime* rt) const;
+  UniqueChars renderNurseryJson() const;
 
 #ifdef DEBUG
   // Print a logging message.
   void writeLogMessage(const char* fmt, ...);
 #else
   void writeLogMessage(const char* fmt, ...){};
 #endif
 
  private:
-  JSRuntime* runtime;
+  gc::GCRuntime* const gc;
 
   /* File used for MOZ_GCTIMER output. */
   FILE* gcTimerFile;
 
   /* File used for JS_GC_DEBUG output. */
   FILE* gcDebugFile;
 
   ZoneGCStats zoneStats;
@@ -420,16 +420,18 @@ struct Statistics {
   using ProfileDurations =
       EnumeratedArray<ProfileKey, ProfileKey::KeyCount, TimeDuration>;
 
   TimeDuration profileThreshold_;
   bool enableProfiling_;
   ProfileDurations totalTimes_;
   uint64_t sliceCount_;
 
+  JSContext* context();
+
   Phase currentPhase() const;
   Phase lookupChildPhase(PhaseKind phaseKind) const;
 
   void beginGC(JSGCInvocationKind kind, const TimeStamp& currentTime);
   void endGC();
 
   void sendGCTelemetry();
   void sendSliceTelemetry(const SliceData& slice);
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -247,17 +247,17 @@ void gc::GCRuntime::startVerifyPreBarrie
 
     node = NextNode(node);
   }
 
   verifyPreData = trc;
   incrementalState = State::Mark;
   marker.start();
 
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     MOZ_ASSERT(!zone->usedByHelperThread());
     zone->setNeedsIncrementalBarrier(true);
     zone->arenas.clearFreeLists();
   }
 
   return;
 
 oom:
@@ -341,17 +341,17 @@ void gc::GCRuntime::endVerifyPreBarriers
 
   MOZ_ASSERT(!JS::IsGenerationalGCEnabled(rt));
 
   AutoPrepareForTracing prep(rt->mainContextFromOwnThread());
 
   bool compartmentCreated = false;
 
   /* We need to disable barriers before tracing, which may invoke barriers. */
-  for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
+  for (ZonesIter zone(this, WithAtoms); !zone.done(); zone.next()) {
     if (!zone->needsIncrementalBarrier()) {
       compartmentCreated = true;
     }
 
     zone->setNeedsIncrementalBarrier(false);
   }
 
   /*
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -1732,17 +1732,17 @@ void js::GCParallelTask::startOrRunIfIdl
   }
 
   // Join the previous invocation of the task. This will return immediately
   // if the thread has never been started.
   joinWithLockHeld(lock);
 
   if (!(CanUseExtraThreads() && startWithLockHeld(lock))) {
     AutoUnlockHelperThreadState unlock(lock);
-    runFromMainThread(runtime());
+    runFromMainThread();
   }
 }
 
 void js::GCParallelTask::joinWithLockHeld(AutoLockHelperThreadState& lock) {
   if (isNotStarted(lock)) {
     return;
   }
 
@@ -1764,41 +1764,41 @@ static inline TimeDuration TimeSince(Tim
   // Sadly this happens sometimes.
   MOZ_ASSERT(now >= prev);
   if (now < prev) {
     now = prev;
   }
   return now - prev;
 }
 
-void GCParallelTask::joinAndRunFromMainThread(JSRuntime* rt) {
+void GCParallelTask::joinAndRunFromMainThread() {
   {
     AutoLockHelperThreadState lock;
     MOZ_ASSERT(!isRunningWithLockHeld(lock));
     joinWithLockHeld(lock);
   }
 
-  runFromMainThread(rt);
+  runFromMainThread();
 }
 
-void js::GCParallelTask::runFromMainThread(JSRuntime* rt) {
+void js::GCParallelTask::runFromMainThread() {
   assertNotStarted();
-  MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
+  MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(gc->rt));
   TimeStamp timeStart = ReallyNow();
   runTask();
   duration_ = TimeSince(timeStart);
 }
 
 void js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& lock) {
   MOZ_ASSERT(isDispatched(lock));
 
   {
     AutoUnlockHelperThreadState parallelSection(lock);
     AutoSetHelperThreadContext usesContext;
-    AutoSetContextRuntime ascr(runtime());
+    AutoSetContextRuntime ascr(gc->rt);
     gc::AutoSetThreadIsPerformingGC performingGC;
     TimeStamp timeStart = ReallyNow();
     runTask();
     duration_ = TimeSince(timeStart);
   }
 
   setFinished(lock);
   HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER, lock);
--- a/netwerk/test/unit/test_parse_content_type.js
+++ b/netwerk/test/unit/test_parse_content_type.js
@@ -416,16 +416,27 @@ function makeChan(url, type = Ci.nsICont
     loadingPrincipal: principal,
     contentPolicyType: type,
     securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
   }).QueryInterface(Ci.nsIHttpChannel);
 }
 
 add_task(async function check_channels() {
   do_get_profile();
+  // This test uses a histogram which isn't enabled on for all products
+  // (Thunderbird is missing it in this case).
+  Services.prefs.setBoolPref(
+    "toolkit.telemetry.testing.overrideProductsCheck",
+    true
+  );
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref(
+      "toolkit.telemetry.testing.overrideProductsCheck"
+    );
+  });
 
   let contentType = "text/css;hi";
   let content = `.identity-color-blue {
     --identity-tab-color: #37adff;
     --identity-icon-color: #37adff;
 }`;
 
   function handler(metadata, response) {
--- a/netwerk/url-classifier/UrlClassifierCommon.h
+++ b/netwerk/url-classifier/UrlClassifierCommon.h
@@ -11,16 +11,20 @@
 #include "mozilla/AntiTrackingCommon.h"
 
 #include <vector>
 
 class nsIChannel;
 class nsIURI;
 
 #define UC_LOG(args) MOZ_LOG(UrlClassifierCommon::sLog, LogLevel::Info, args)
+#define UC_LOG_DEBUG(args) \
+  MOZ_LOG(UrlClassifierCommon::sLog, LogLevel::Debug, args)
+#define UC_LOG_WARN(args) \
+  MOZ_LOG(UrlClassifierCommon::sLog, LogLevel::Warning, args)
 #define UC_LOG_ENABLED() MOZ_LOG_TEST(UrlClassifierCommon::sLog, LogLevel::Info)
 
 namespace mozilla {
 namespace net {
 
 class UrlClassifierCommon final {
  public:
   static const nsCString::size_type sMaxSpecLength;
--- a/netwerk/url-classifier/nsChannelClassifier.cpp
+++ b/netwerk/url-classifier/nsChannelClassifier.cpp
@@ -31,27 +31,16 @@
 #include "mozilla/net/UrlClassifierCommon.h"
 #include "mozilla/net/UrlClassifierFeatureFactory.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 
 namespace mozilla {
 namespace net {
 
-//
-// MOZ_LOG=nsChannelClassifier:5
-//
-static LazyLogModule gChannelClassifierLog("nsChannelClassifier");
-
-#undef LOG
-#define LOG(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Info, args)
-#define LOG_DEBUG(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Debug, args)
-#define LOG_WARN(args) MOZ_LOG(gChannelClassifierLog, LogLevel::Warning, args)
-#define LOG_ENABLED() MOZ_LOG_TEST(gChannelClassifierLog, LogLevel::Info)
-
 #define URLCLASSIFIER_SKIP_HOSTNAMES "urlclassifier.skipHostnames"
 
 // Put CachedPrefs in anonymous namespace to avoid any collision from outside of
 // this file.
 namespace {
 
 /**
  * It is not recommended to read from Preference everytime a channel is
@@ -119,22 +108,22 @@ CachedPrefs::~CachedPrefs() {
 }
 
 }  // anonymous namespace
 
 NS_IMPL_ISUPPORTS(nsChannelClassifier, nsIURIClassifierCallback, nsIObserver)
 
 nsChannelClassifier::nsChannelClassifier(nsIChannel* aChannel)
     : mIsAllowListed(false), mSuspendedChannel(false), mChannel(aChannel) {
-  LOG_DEBUG(("nsChannelClassifier::nsChannelClassifier %p", this));
+  UC_LOG_DEBUG(("nsChannelClassifier::nsChannelClassifier %p", this));
   MOZ_ASSERT(mChannel);
 }
 
 nsChannelClassifier::~nsChannelClassifier() {
-  LOG_DEBUG(("nsChannelClassifier::~nsChannelClassifier %p", this));
+  UC_LOG_DEBUG(("nsChannelClassifier::~nsChannelClassifier %p", this));
 }
 
 void nsChannelClassifier::Start() {
   nsresult rv = StartInternal();
   if (NS_FAILED(rv)) {
     // If we aren't getting a callback for any reason, assume a good verdict and
     // make sure we resume the channel if necessary.
     OnClassifyComplete(NS_OK, NS_LITERAL_CSTRING(""), NS_LITERAL_CSTRING(""),
@@ -185,18 +174,18 @@ nsresult nsChannelClassifier::StartInter
 
   rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
                            &hasFlags);
   NS_ENSURE_SUCCESS(rv, rv);
   if (hasFlags) return NS_ERROR_UNEXPECTED;
 
   nsCString skipHostnames = CachedPrefs::GetInstance()->GetSkipHostnames();
   if (!skipHostnames.IsEmpty()) {
-    LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s",
-         this, skipHostnames.get()));
+    UC_LOG(("nsChannelClassifier[%p]:StartInternal whitelisted hostnames = %s",
+            this, skipHostnames.get()));
     if (IsHostnameWhitelisted(uri, skipHostnames)) {
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   nsCOMPtr<nsIURIClassifier> uriClassifier =
       do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
   if (rv == NS_ERROR_FACTORY_NOT_REGISTERED || rv == NS_ERROR_NOT_AVAILABLE) {
@@ -210,48 +199,48 @@ nsresult nsChannelClassifier::StartInter
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPrincipal> principal;
   rv = securityManager->GetChannelURIPrincipal(mChannel,
                                                getter_AddRefs(principal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool expectCallback;
-  if (LOG_ENABLED()) {
+  if (UC_LOG_ENABLED()) {
     nsCOMPtr<nsIURI> principalURI;
     principal->GetURI(getter_AddRefs(principalURI));
     nsCString spec = principalURI->GetSpecOrDefault();
     spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
-    LOG(("nsChannelClassifier[%p]: Classifying principal %s on channel[%p]",
-         this, spec.get(), mChannel.get()));
+    UC_LOG(("nsChannelClassifier[%p]: Classifying principal %s on channel[%p]",
+            this, spec.get(), mChannel.get()));
   }
   // The classify is running in parent process, no need to give a valid event
   // target
   rv = uriClassifier->Classify(principal, nullptr, this, &expectCallback);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (expectCallback) {
     // Suspend the channel, it will be resumed when we get the classifier
     // callback.
     rv = mChannel->Suspend();
     if (NS_FAILED(rv)) {
       // Some channels (including nsJSChannel) fail on Suspend.  This
       // shouldn't be fatal, but will prevent malware from being
       // blocked on these channels.
-      LOG_WARN(("nsChannelClassifier[%p]: Couldn't suspend channel", this));
+      UC_LOG_WARN(("nsChannelClassifier[%p]: Couldn't suspend channel", this));
       return rv;
     }
 
     mSuspendedChannel = true;
-    LOG_DEBUG(("nsChannelClassifier[%p]: suspended channel %p", this,
-               mChannel.get()));
+    UC_LOG_DEBUG(("nsChannelClassifier[%p]: suspended channel %p", this,
+                  mChannel.get()));
   } else {
-    LOG(("nsChannelClassifier[%p]: not expecting callback", this));
+    UC_LOG(("nsChannelClassifier[%p]: not expecting callback", this));
     return NS_ERROR_FAILURE;
   }
 
   // Add an observer for shutdown
   AddShutdownObserver();
   return NS_OK;
 }
 
@@ -263,18 +252,18 @@ bool nsChannelClassifier::IsHostnameWhit
     return false;
   }
   ToLowerCase(host);
 
   nsCCharSeparatedTokenizer tokenizer(aWhitelisted, ',');
   while (tokenizer.hasMoreTokens()) {
     const nsACString& token = tokenizer.nextToken();
     if (token.Equals(host)) {
-      LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)",
-           this, host.get()));
+      UC_LOG(("nsChannelClassifier[%p]:StartInternal skipping %s (whitelisted)",
+              this, host.get()));
       return true;
     }
   }
 
   return false;
 }
 
 // Note in the cache entry that this URL was classified, so that future
@@ -284,26 +273,26 @@ void nsChannelClassifier::MarkEntryClass
   MOZ_ASSERT(XRE_IsParentProcess());
 
   // Don't cache tracking classifications because we support allowlisting.
   if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status) ||
       mIsAllowListed) {
     return;
   }
 
-  if (LOG_ENABLED()) {
+  if (UC_LOG_ENABLED()) {
     nsAutoCString errorName;
     GetErrorName(status, errorName);
     nsCOMPtr<nsIURI> uri;
     mChannel->GetURI(getter_AddRefs(uri));
     nsAutoCString spec;
     uri->GetAsciiSpec(spec);
     spec.Truncate(std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
-    LOG(("nsChannelClassifier::MarkEntryClassified[%s] %s", errorName.get(),
-         spec.get()));
+    UC_LOG(("nsChannelClassifier::MarkEntryClassified[%s] %s", errorName.get(),
+            spec.get()));
   }
 
   nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(mChannel);
   if (!cachingChannel) {
     return;
   }
 
   nsCOMPtr<nsISupports> cacheToken;
@@ -359,17 +348,17 @@ nsresult nsChannelClassifier::SendThreat
                                                   const nsACString& aList,
                                                   const nsACString& aFullHash) {
   NS_ENSURE_ARG_POINTER(aChannel);
 
   nsAutoCString provider(aProvider);
   nsPrintfCString reportEnablePref(
       "browser.safebrowsing.provider.%s.dataSharing.enabled", provider.get());
   if (!Preferences::GetBool(reportEnablePref.get(), false)) {
-    LOG((
+    UC_LOG((
         "nsChannelClassifier::SendThreatHitReport data sharing disabled for %s",
         provider.get()));
     return NS_OK;
   }
 
   nsCOMPtr<nsIURIClassifier> uriClassifier =
       components::UrlClassifierDB::Service();
   if (!uriClassifier) {
@@ -390,31 +379,32 @@ nsChannelClassifier::OnClassifyComplete(
                                         const nsACString& aFullHash) {
   // Should only be called in the parent process.
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_ASSERT(
       !UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
 
   if (mSuspendedChannel) {
     nsAutoCString errorName;
-    if (LOG_ENABLED() && NS_FAILED(aErrorCode)) {
+    if (UC_LOG_ENABLED() && NS_FAILED(aErrorCode)) {
       GetErrorName(aErrorCode, errorName);
-      LOG(("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)",
+      UC_LOG(
+          ("nsChannelClassifier[%p]:OnClassifyComplete %s (suspended channel)",
            this, errorName.get()));
     }
     MarkEntryClassified(aErrorCode);
 
     if (NS_FAILED(aErrorCode)) {
-      if (LOG_ENABLED()) {
+      if (UC_LOG_ENABLED()) {
         nsCOMPtr<nsIURI> uri;
         mChannel->GetURI(getter_AddRefs(uri));
         nsCString spec = uri->GetSpecOrDefault();
         spec.Truncate(
             std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
-        LOG(
+        UC_LOG(
             ("nsChannelClassifier[%p]: cancelling channel %p for %s "
              "with error code %s",
              this, mChannel.get(), spec.get(), errorName.get()));
       }
 
       // Channel will be cancelled (page element blocked) due to Safe Browsing.
       // Do update the security state of the document and fire a security
       // change event.
@@ -425,17 +415,17 @@ nsChannelClassifier::OnClassifyComplete(
           aErrorCode == NS_ERROR_PHISHING_URI ||
           aErrorCode == NS_ERROR_UNWANTED_URI ||
           aErrorCode == NS_ERROR_HARMFUL_URI) {
         SendThreatHitReport(mChannel, aProvider, aList, aFullHash);
       }
 
       mChannel->Cancel(aErrorCode);
     }
-    LOG_DEBUG(
+    UC_LOG_DEBUG(
         ("nsChannelClassifier[%p]: resuming channel[%p] from "
          "OnClassifyComplete",
          this, mChannel.get()));
     mChannel->Resume();
   }
 
   mChannel = nullptr;
   RemoveShutdownObserver();
@@ -476,12 +466,10 @@ nsChannelClassifier::Observe(nsISupports
     }
 
     RemoveShutdownObserver();
   }
 
   return NS_OK;
 }
 
-#undef LOG_ENABLED
-
 }  // namespace net
 }  // namespace mozilla
--- a/taskcluster/ci/release-push-langpacks/kind.yml
+++ b/taskcluster/ci/release-push-langpacks/kind.yml
@@ -19,18 +19,18 @@ only-for-build-platforms:
     - macosx64-shippable/opt  # Although, we need the special locale "ja-JP-Mac" from this platform
     - macosx64-devedition-nightly/opt
 
 
 job-template:
     description: Sends {locales} XPIs for platform to addons.mozilla.org
     worker-type:
         by-release-level:
-            production: scriptworker-prov-v1/addon-v1
-            staging: scriptworker-prov-v1/addon-dev
+            production: scriptworker-k8s/gecko-3-addon
+            staging: scriptworker-k8s/gecko-1-addon
     worker:
         implementation: push-addons
         channel:
             by-project:
                 mozilla-release:
                     by-platform:
                         # ja-JP-mac is only langpack on mac, and is unlisted always
                         macosx64.*: unlisted
--- a/testing/specialpowers/api.js
+++ b/testing/specialpowers/api.js
@@ -33,19 +33,20 @@ this.specialpowers = class extends Exten
       .QueryInterface(Ci.nsIComponentRegistrar)
       .autoRegister(FileUtils.getFile("ProfD", ["tests.manifest"]));
 
     ChromeUtils.registerWindowActor("SpecialPowers", {
       allFrames: true,
       includeChrome: true,
       child: {
         moduleURI: "resource://specialpowers/SpecialPowersChild.jsm",
-        events: {
-          DOMWindowCreated: {},
-        },
+        observers: [
+          "chrome-document-global-created",
+          "content-document-global-created",
+        ],
       },
       parent: {
         moduleURI: "resource://specialpowers/SpecialPowersParent.jsm",
       },
     });
   }
 
   onShutdown() {
--- a/testing/specialpowers/content/SpecialPowersChild.jsm
+++ b/testing/specialpowers/content/SpecialPowersChild.jsm
@@ -182,19 +182,19 @@ class SpecialPowersChild extends JSWindo
     this._xpcomabi = null;
     this._os = null;
     this._pu = null;
 
     this._nextExtensionID = 0;
     this._extensionListeners = null;
   }
 
-  handleEvent(aEvent) {
-    // We don't actually care much about the "DOMWindowCreated" event.
-    // We only listen to it to force creation of the actor.
+  observe(aSubject, aTopic, aData) {
+    // Ignore the "{chrome/content}-document-global-created" event. It
+    // is only observed to force creation of the actor.
   }
 
   actorCreated() {
     this.attachToWindow();
   }
 
   attachToWindow() {
     let window = this.contentWindow;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -254,57 +254,57 @@
     "products": ["firefox", "fennec", "geckoview"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 21,
     "description": "Maximum number of concurrent threads reached during a given download session"
   },
   "CHECKERBOARD_DURATION": {
     "record_in_processes": ["main", "content", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "kgupta@mozilla.com"],
-    "bug_numbers": [1238040, 1539309],
-    "releaseChannelCollection": "opt-out",
-    "expires_in_version": "73",
+    "bug_numbers": [1238040, 1539309, 1584109],
+    "releaseChannelCollection": "opt-out",
+    "expires_in_version": "never",
     "kind": "exponential",
     "high": 100000,
     "n_buckets": 50,
     "description": "Duration of a checkerboard event in milliseconds"
   },
   "CHECKERBOARD_PEAK": {
     "record_in_processes": ["main", "content", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "kgupta@mozilla.com"],
-    "bug_numbers": [1238040, 1539309],
-    "releaseChannelCollection": "opt-out",
-    "expires_in_version": "73",
+    "bug_numbers": [1238040, 1539309, 1584109],
+    "releaseChannelCollection": "opt-out",
+    "expires_in_version": "never",
     "kind": "exponential",
     "high": 66355200,
     "n_buckets": 50,
     "description": "Peak number of CSS pixels checkerboarded during a checkerboard event (the high value is the size of a 4k display with max APZ zooming)"
   },
   "CHECKERBOARD_POTENTIAL_DURATION": {
     "record_in_processes": ["main", "content", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "kgupta@mozilla.com"],
-    "bug_numbers": [1238040, 1539309],
-    "releaseChannelCollection": "opt-out",
-    "expires_in_version": "73",
+    "bug_numbers": [1238040, 1539309, 1584109],
+    "releaseChannelCollection": "opt-out",
+    "expires_in_version": "never",
     "kind": "exponential",
     "high": 1000000,
     "n_buckets": 50,
     "description": "Duration of a chunk of time (in ms) that could reasonably have had checkerboarding"
   },
   "CHECKERBOARD_SEVERITY": {
     "record_in_processes": ["main", "content", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "kgupta@mozilla.com"],
-    "bug_numbers": [1238040, 1539309],
-    "releaseChannelCollection": "opt-out",
-    "expires_in_version": "73",
+    "bug_numbers": [1238040, 1539309, 1584109],
+    "releaseChannelCollection": "opt-out",
+    "expires_in_version": "never",
     "kind": "exponential",
     "high": 1073741824,
     "n_buckets": 50,
     "description": "Opaque measure of the severity of a checkerboard event"
   },
   "CHILD_PROCESS_LAUNCH_MS" : {
     "record_in_processes": ["main"],
     "products": ["firefox", "fennec", "geckoview"],
@@ -13953,103 +13953,103 @@
     "releaseChannelCollection": "opt-out",
     "kind": "exponential",
     "high": 50000,
     "n_buckets": 100,
     "description": "Time in milliseconds from the first non-blank paint to the creation time of the next click, key, mouse or scroll event per top-level content browsing context."
   },
   "CONTENT_PAINT_TIME": {
     "record_in_processes": ["main", "content"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com", "dbolter@mozilla.com"],
-    "bug_numbers": [1309442, 1489524],
+    "bug_numbers": [1309442, 1489524, 1584109],
     "expires_in_version": "never",
     "releaseChannelCollection": "opt-out",
     "kind": "exponential",
     "high": 1000,
     "n_buckets": 50,
     "description": "Time spent in the paint pipeline for content in milliseconds."
   },
   "CONTENT_FULL_PAINT_TIME": {
     "record_in_processes": ["main", "content", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "jmuizelaar@mozilla.com", "dbolter@mozilla.com"],
-    "bug_numbers": [1505858],
+    "bug_numbers": [1505858, 1584109],
     "expires_in_version": "never",
     "releaseChannelCollection": "opt-out",
     "kind": "exponential",
     "high": 1000,
     "n_buckets": 50,
     "description": "Time spent in the paint pipeline until it's ready for composition in milliseconds."
   },
   "CONTENT_FRAME_TIME": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
-    "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "rhunt@mozilla.com"],
-    "bug_numbers": [1470528, 1509536],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
+    "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "jnicol@mozilla.com"],
+    "bug_numbers": [1470528, 1509536, 1584109],
     "expires_in_version": "never",
     "releaseChannelCollection": "opt-out",
     "kind": "exponential",
     "high": 5000,
     "n_buckets": 50,
     "description": "The time, in percentage of a vsync interval, spent from beginning a paint in the content process until that frame is presented in the compositor."
   },
   "CONTENT_FRAME_TIME_VSYNC": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"],
-    "bug_numbers": [1517355],
+    "bug_numbers": [1517355, 1584109],
     "expires_in_version": "never",
     "releaseChannelCollection": "opt-out",
     "kind": "linear",
     "low": 8,
     "high": 792,
     "n_buckets": 100,
     "description": "The time, in percentage of a vsync interval, spent from the vsync that started a paint in the content process until that frame is presented in the compositor."
   },
   "CONTENT_FRAME_TIME_WITH_SVG": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"],
-    "bug_numbers": [1483549, 1509536],
-    "expires_in_version": "70",
+    "bug_numbers": [1483549, 1509536, 1584109],
+    "expires_in_version": "never",
     "releaseChannelCollection": "opt-out",
     "kind": "exponential",
     "high": 5000,
     "n_buckets": 50,
     "description": "The time, in percentage of a vsync interval, spent from beginning a paint in the content process until that frame is presented in the compositor, for frames that contained an SVG to be drawn by WebRender"
   },
   "CONTENT_FRAME_TIME_WITHOUT_RESOURCE_UPLOAD": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"],
-    "bug_numbers": [1503405],
-    "expires_in_version": "70",
+    "bug_numbers": [1503405, 1584109],
+    "expires_in_version": "never",
     "kind": "exponential",
     "high": 5000,
     "n_buckets": 50,
     "description": "The time, in percentage of a vsync interval, spent from beginning a paint in the content process until that frame is presented in the compositor by WebRender, excluding time spent uploading resources"
   },
   "CONTENT_FRAME_TIME_WITHOUT_UPLOAD": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"],
-    "bug_numbers": [1503405],
-    "expires_in_version": "70",
+    "bug_numbers": [1503405, 1584109],
+    "expires_in_version": "never",
     "kind": "exponential",
     "high": 5000,
     "n_buckets": 50,
     "description": "The time, in percentage of a vsync interval, spent from beginning a paint in the content process until that frame is presented in the compositor by WebRender, excluding time spent uploading any content"
   },
   "CONTENT_FRAME_TIME_REASON": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"],
-    "bug_numbers": [1510853],
-    "expires_in_version": "73",
+    "bug_numbers": [1510853, 1584109],
+    "expires_in_version": "never",
     "kind": "categorical",
     "description": "The reason that CONTENT_FRAME_TIME recorded a slow (>200) result, if any.",
     "labels": ["OnTime", "NoVsync", "MissedComposite", "SlowComposite", "MissedCompositeMid", "MissedCompositeLong", "MissedCompositeLow", "NoVsyncNoId"]
   },
   "CONTENT_LARGE_PAINT_PHASE_WEIGHT": {
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec", "geckoview"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "mwoodrow@mozilla.com"],
@@ -15125,41 +15125,41 @@
     "expires_in_version": "70",
     "kind": "categorical",
     "releaseChannelCollection": "opt-out",
     "labels": ["NoOverflow", "Desktop", "ButNotMinScaleSize", "MinScaleSize"],
     "description": "How common are different types of out-of-reach viewport overflow?"
   },
   "WR_SCENEBUILD_TIME": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
-    "alert_emails": ["kgupta@mozilla.com"],
-    "bug_numbers": [1470901],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
+    "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "kgupta@mozilla.com"],
+    "bug_numbers": [1470901, 1584109],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 1000,
     "n_buckets": 50,
     "description": "WebRender scene build time in milliseconds"
   },
   "WR_SCENESWAP_TIME": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
-    "alert_emails": ["kgupta@mozilla.com"],
-    "bug_numbers": [1470901],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
+    "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "kgupta@mozilla.com"],
+    "bug_numbers": [1470901, 1584109],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 1000,
     "n_buckets": 50,
     "description": "WebRender scene swap time in milliseconds"
   },
-  "WR_RENDER_TIME": {
+  "WR_FRAMEBUILD_TIME": {
     "record_in_processes": ["main", "gpu"],
-    "products": ["firefox", "fennec", "geckoview"],
-    "alert_emails": ["kgupta@mozilla.com"],
-    "bug_numbers": [1470901],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
+    "alert_emails": ["gfx-telemetry-alerts@mozilla.com", "kgupta@mozilla.com"],
+    "bug_numbers": [1470901, 1584109],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 1000,
     "n_buckets": 50,
     "description": "WebRender render time in milliseconds"
   },
   "TOUCHBAR_BUTTON_PRESSES": {
     "record_in_processes": ["main"],
@@ -15299,26 +15299,26 @@
     "kind": "exponential",
     "high": 100000,
     "n_buckets": 100,
     "bug_numbers": [1499418],
     "description": "GeckoView: The time between page load progress start (0) and completion (100) in ms."
   },
   "GV_PAGE_LOAD_MS": {
     "record_in_processes": ["main", "content"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": [
       "geckoview-team@mozilla.com",
       "esawin@mozilla.com"
     ],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 100000,
     "n_buckets": 100,
-    "bug_numbers": [1499418],
+    "bug_numbers": [1499418, 1584109],
     "description": "GeckoView: Time taken to load a page in ms. This includes all static contents, no dynamic content. Loading of about: pages is not counted."
   },
   "GV_PAGE_RELOAD_MS": {
     "record_in_processes": ["main", "content"],
     "products": ["firefox", "fennec", "geckoview"],
     "alert_emails": [
       "geckoview-team@mozilla.com",
       "sefeng@mozilla.com",
@@ -15328,26 +15328,26 @@
     "kind": "exponential",
     "high": 100000,
     "n_buckets": 100,
     "bug_numbers": [1549519],
     "description": "GeckoView: Time taken to reload a page in ms. This includes all static contents, no dynamic content. Loading of about: pages is not counted."
   },
   "GV_STARTUP_RUNTIME_MS": {
     "record_in_processes": ["main", "content"],
-    "products": ["firefox", "fennec", "geckoview"],
+    "products": ["firefox", "fennec", "geckoview", "geckoview_streaming"],
     "alert_emails": [
       "geckoview-team@mozilla.com",
       "esawin@mozilla.com"
     ],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 10000,
     "n_buckets": 50,
-    "bug_numbers": [1499418],
+    "bug_numbers": [1499418, 1584109],
     "description": "GeckoView: Time taken to initialize GeckoRuntime in ms."
   },
   "GV_STARTUP_MODULES_MS": {
     "record_in_processes": ["main"],
     "products": ["firefox", "fennec", "geckoview"],
     "alert_emails": [
       "geckoview-team@mozilla.com",
       "esawin@mozilla.com"
--- a/toolkit/components/telemetry/geckoview/streaming/metrics.yaml
+++ b/toolkit/components/telemetry/geckoview/streaming/metrics.yaml
@@ -3,27 +3,378 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # This file defines the metrics that are recorded by the Glean SDK. They are
 # automatically converted to platform-specific code at build time using the
 # `glean_parser` PyPI package.
 
 $schema: moz://mozilla.org/schemas/glean/metrics/1-0-0
 
+geckoview:
+  page_load_time:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: GV_PAGE_LOAD_MS
+    description: >
+      The time taken to load a page. This includes all static contents, no dynamic content.
+      Loading of about: pages is not counted.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1499418
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - geckoview-team@mozilla.com
+      - esawin@mozilla.com
+    expires: never
+
+  startup_runtime:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: GV_STARTUP_RUNTIME_MS
+    description: >
+      The time taken to initialize GeckoRuntime.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1499418
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - geckoview-team@mozilla.com
+      - esawin@mozilla.com
+    expires: never
+
 gfx:
   composite_time:
     type: timing_distribution
     time_unit: millisecond
     gecko_datapoint: COMPOSITE_TIME
-    description: |
+    description: >
       The time taken to composite a frame.
       On non-webrender this is the time taken in `CompositorBridgeParent::CompositeToTarget()`.
-      On webrender, this is the time taken from the start of `WebRenderBridgeParent::CompositeToTarget()`, until the render thread has rendered the frame (in `RenderThread::HandleFrameOneDoc()`).
+      On webrender, this is the time taken from the start of `WebRenderBridgeParent::CompositeToTarget()`,
+      until the render thread has rendered the frame (in `RenderThread::HandleFrameOneDoc()`).
     bugs:
-      - https://bugzilla.mozilla.org/1080160
-      - https://bugzilla.mozilla.org/1529352
-      - https://bugzilla.mozilla.org/1580129
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1080160
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1529352
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1580129
     data_reviews:
       - https://bugzilla.mozilla.org/show_bug.cgi?id=1580129#c7
     notification_emails:
       - gfx-telemetry-alerts@mozilla.com
       - jnicol@mozilla.com
     expires: never
+
+gfx.checkerboard:
+  duration:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: CHECKERBOARD_DURATION
+    description: >
+      The duration of a checkerboard event.
+      Checkerboarding is when painting has not kept up with asynchronous panning and
+      zooming so the compositor has to display a "checkerboard pattern" (or in practice,
+      the background color) rather than the actual page content.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1238040
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1539309
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - kgupta@mozilla.com
+    expires: never
+
+  peak_pixel_count:
+    type: custom_distribution
+    range_max: 66355200
+    bucket_count: 50
+    histogram_type: exponential
+    unit: Pixels
+    gecko_datapoint: CHECKERBOARD_PEAK
+    description: >
+      The peak number of CSS pixels that checkerboarded during a checkerboard event.
+      The minimum value of the largest histogram bucket is the size of a 4k display with
+      maximum APZ zooming.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1238040
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1539309
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - kgupta@mozilla.com
+    expires: never
+
+  potential_duration:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: CHECKERBOARD_POTENTIAL_DURATION
+    description: >
+      The total amount of time that we could reasonably be checkerboarding.
+      This is the union of two possibly-intersecting sets of time periods:
+      The first set is that in which checkerboarding was actually happening,
+      since by definition it could potentially be happening.
+      The second set is that in which the APZC is actively transforming content
+      in the compositor, since it could potentially transform it so as to display
+      checkerboarding to the user. Combined with other information, this allows us
+      to meaningfully say how frequently users actually enncounters checkerboarding.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1238040
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1539309
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - kgupta@mozilla.com
+    expires: never
+
+  severity:
+    type: custom_distribution
+    range_max: 1073741824
+    bucket_count: 50
+    histogram_type: exponential
+    unit: Opaque unit
+    gecko_datapoint: CHECKERBOARD_SEVERITY
+    description: >
+      An opaque measurement of the severity of a checkerboard event.
+      This doesn't have units, it's just useful for comparing two checkerboard
+      events to see which one is worse, for some implementation-specific
+      definition of "worse". The larger the value, the worse the checkerboarding.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1238040
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1539309
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - kgupta@mozilla.com
+    expires: never
+
+gfx.content:
+  paint_time:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: CONTENT_PAINT_TIME
+    description: >
+      Time spent in the main-thread paint pipeline for content.
+      For non-webrender, this includes display list building, layer building, and when OMTP
+      is disabled, rasterization.
+      For webrender, this includes display list building, and webrender display list building.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1309442
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1489524
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - mwoodrow@mozilla.com
+      - dbolter@mozilla.com
+    expires: never
+
+  full_paint_time:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: CONTENT_FULL_PAINT_TIME
+    description: >
+      Time spent in the full paint pipeline for content until it's ready for composition.
+      For non-webrender this includes `paint_time`, plus rasterization if OMTP is enabled.
+      For webrender, this includes `paint_time`, plus scene building time.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1505858
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - jmuizelaar@mozilla.com
+      - dbolter@mozilla.com
+    expires: never
+
+gfx.content.frame_time:
+  from_paint:
+    type: custom_distribution
+    range_max: 5000
+    bucket_count: 50
+    histogram_type: exponential
+    unit: Percentage of vsync interval
+    gecko_datapoint: CONTENT_FRAME_TIME
+    description: >
+      The time, in percentage of a vsync interval, spent from beginning a paint in
+      the content process until that frame is presented in the compositor.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1470528
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1509536
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - jnicol@mozilla.com
+    expires: never
+
+  from_vsync:
+    type: custom_distribution
+    range_min: 8
+    range_max: 792
+    bucket_count: 100
+    histogram_type: linear
+    unit: Percentage of vsync interval
+    gecko_datapoint: CONTENT_FRAME_TIME_VSYNC
+    description: >
+      The time, in percentage of a vsync interval, spent from the vsync that started a
+      paint in the content process until that frame is presented in the compositor.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1517355
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - mwoodrow@mozilla.com
+    expires: never
+
+  with_svg:
+    type: custom_distribution
+    range_max: 5000
+    bucket_count: 50
+    histogram_type: exponential
+    unit: Percentage of vsync interval
+    gecko_datapoint: CONTENT_FRAME_TIME_WITH_SVG
+    description: >
+      The time, in percentage of a vsync interval, spent from beginning a paint in the
+      content process until that frame is presented in the compositor, for frames that
+      contained an SVG to be drawn by webrender.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1483549
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1509536
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - mwoodrow@mozilla.com
+    expires: never
+
+  without_resource_upload:
+    type: custom_distribution
+    range_max: 5000
+    bucket_count: 50
+    histogram_type: exponential
+    unit: Percentage of vsync interval
+    gecko_datapoint: CONTENT_FRAME_TIME_WITHOUT_RESOURCE_UPLOAD
+    description: >
+      The time, in percentage of a vsync interval, spent from beginning a paint in the
+      content process until that frame is presented in the compositor by webrender,
+      excluding time spent uploading resources.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1503405
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - mwoodrow@mozilla.com
+    expires: never
+
+  without_upload:
+    type: custom_distribution
+    range_max: 5000
+    bucket_count: 50
+    histogram_type: exponential
+    unit: Percentage of vsync interval
+    gecko_datapoint: CONTENT_FRAME_TIME_WITHOUT_UPLOAD
+    description: >
+      The time, in percentage of a vsync interval, spent from beginning a paint in the
+      content process until that frame is presented in the compositor by webrender,
+      excluding time spent uploading any content.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1503405
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - mwoodrow@mozilla.com
+    expires: never
+
+  reason:
+    type: labeled_counter
+    labels:
+      - on_time
+      - no_vsync
+      - missed_composite
+      - slow_composite
+      - missed_composite_mid
+      - missed_composite_long
+      - missed_composite_low
+      - no_vsync_no_id
+    gecko_datapoint: CONTENT_FRAME_TIME_REASON
+    description: >
+      The reason that `gfx.content.frame_time.from_paint` recorded a slow (>200ms) result, if any.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1510853
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - mwoodrow@mozilla.com
+    expires: never
+
+gfx.webrender:
+  scenebuild_time:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: WR_SCENEBUILD_TIME
+    description: >
+      The time taken to build a webrender scene.
+      This occurs each time webrender receives a new display list.
+      This additionally includes blob rasterization time.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1470901
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - kgupta@mozilla.com
+    expires: never
+
+  sceneswap_time:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: WR_SCENESWAP_TIME
+    description: >
+      The time taken to do a webrender scene swap. This is book-keeping that APZ must
+      perform once webrender has built a new scene.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1470901
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - kgupta@mozilla.com
+    expires: never
+
+  render_time:
+    type: timing_distribution
+    time_unit: millisecond
+    gecko_datapoint: WR_FRAMEBUILD_TIME
+    description: >
+      The time taken to build a webrender frame.
+      This involves calculating the visibility of primitives, requesting resources,
+      and building the render passes which will be used to render the frame.
+    bugs:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1470901
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109
+    data_reviews:
+      - https://bugzilla.mozilla.org/show_bug.cgi?id=1584109#c1
+    notification_emails:
+      - gfx-telemetry-alerts@mozilla.com
+      - kgupta@mozilla.com
+    expires: never
--- a/toolkit/content/widgets/browser-custom-element.js
+++ b/toolkit/content/widgets/browser-custom-element.js
@@ -82,16 +82,21 @@
 
       this.addEventListener(
         "keypress",
         event => {
           if (event.keyCode != KeyEvent.DOM_VK_F7) {
             return;
           }
 
+          // shift + F7 is the default DevTools shortcut for the Style Editor.
+          if (event.shiftKey) {
+            return;
+          }
+
           if (event.defaultPrevented || !event.isTrusted) {
             return;
           }
 
           const kPrefShortcutEnabled =
             "accessibility.browsewithcaret_shortcut.enabled";
           const kPrefWarnOnEnable = "accessibility.warn_on_browsewithcaret";
           const kPrefCaretBrowsingOn = "accessibility.browsewithcaret";
--- a/tools/tryselect/selectors/fuzzy.py
+++ b/tools/tryselect/selectors/fuzzy.py
@@ -318,28 +318,26 @@ def run(update=False, query=None, inters
         parameters=None, save_query=False, push=True, message='{msg}',
         test_paths=None, exact=False, closed_tree=False, show_estimates=False):
     fzf = fzf_bootstrap(update)
 
     if not fzf:
         print(FZF_NOT_FOUND)
         return 1
 
-    if show_estimates:
-        download_task_history_data()
-
     check_working_directory(push)
     tg = generate_tasks(parameters, full)
     all_tasks = sorted(tg.tasks.keys())
 
     cache_dir = os.path.join(get_state_dir(srcdir=True), 'cache', 'taskgraph')
     graph_cache = os.path.join(cache_dir, 'target_task_graph')
     dep_cache = os.path.join(cache_dir, 'target_task_dependencies')
 
     if show_estimates:
+        download_task_history_data()
         make_trimmed_taskgraph_cache(graph_cache, dep_cache)
 
     if not full:
         all_tasks = filter(filter_target_task, all_tasks)
 
     if test_paths:
         all_tasks = filter_tasks_by_paths(all_tasks, test_paths)
         if not all_tasks:
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -980,20 +980,19 @@ struct JsGcTracer : public TraceCallback
   virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
                      void* aClosure) const override {
     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
   }
   virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
                      void* aClosure) const override {
     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
   }
-  virtual void Trace(JSObject** aPtr, const char* aName,
+  virtual void Trace(nsWrapperCache* aPtr, const char* aName,
                      void* aClosure) const override {
-    js::UnsafeTraceManuallyBarrieredEdge(static_cast<JSTracer*>(aClosure), aPtr,
-                                         aName);
+    aPtr->TraceWrapper(static_cast<JSTracer*>(aClosure), aName);
   }
   virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
                      void* aClosure) const override {
     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
   }
   virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
                      void* aClosure) const override {
     JS::TraceEdge(static_cast<JSTracer*>(aClosure), aPtr, aName);
@@ -1050,19 +1049,19 @@ struct ClearJSHolder : public TraceCallb
     *aPtr = JSID_VOID;
   }
 
   virtual void Trace(JS::Heap<JSObject*>* aPtr, const char*,
                      void*) const override {
     *aPtr = nullptr;
   }
 
-  virtual void Trace(JSObject** aPtr, const char* aName,
+  virtual void Trace(nsWrapperCache* aPtr, const char* aName,
                      void* aClosure) const override {
-    *aPtr = nullptr;
+    aPtr->ClearWrapper();
   }
 
   virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char*,
                      void*) const override {
     *aPtr = nullptr;
   }
 
   virtual void Trace(JS::Heap<JSString*>* aPtr, const char*,
--- a/xpcom/base/nsCycleCollectionParticipant.h
+++ b/xpcom/base/nsCycleCollectionParticipant.h
@@ -44,16 +44,18 @@
 class nsCycleCollectionISupports {
  public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_CYCLECOLLECTIONISUPPORTS_IID)
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionISupports,
                               NS_CYCLECOLLECTIONISUPPORTS_IID)
 
+class nsWrapperCache;
+
 namespace JS {
 template <class T>
 class Heap;
 } /* namespace JS */
 
 /*
  * A struct defining pure virtual methods which are called when tracing cycle
  * collection paticipants.  The appropriate method is called depending on the
@@ -61,17 +63,17 @@ class Heap;
  */
 struct TraceCallbacks {
   virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
                      void* aClosure) const = 0;
   virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
                      void* aClosure) const = 0;
   virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
                      void* aClosure) const = 0;
-  virtual void Trace(JSObject** aPtr, const char* aName,
+  virtual void Trace(nsWrapperCache* aPtr, const char* aName,
                      void* aClosure) const = 0;
   virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
                      void* aClosure) const = 0;
   virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
                      void* aClosure) const = 0;
   virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
                      void* aClosure) const = 0;
   virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
@@ -89,17 +91,17 @@ struct TraceCallbackFunc : public TraceC
   explicit TraceCallbackFunc(Func aCb) : mCallback(aCb) {}
 
   virtual void Trace(JS::Heap<JS::Value>* aPtr, const char* aName,
                      void* aClosure) const override;
   virtual void Trace(JS::Heap<jsid>* aPtr, const char* aName,
                      void* aClosure) const override;
   virtual void Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
                      void* aClosure) const override;
-  virtual void Trace(JSObject** aPtr, const char* aName,
+  virtual void Trace(nsWrapperCache* aPtr, const char* aName,
                      void* aClosure) const override;
   virtual void Trace(JS::TenuredHeap<JSObject*>* aPtr, const char* aName,
                      void* aClosure) const override;
   virtual void Trace(JS::Heap<JSString*>* aPtr, const char* aName,
                      void* aClosure) const override;
   virtual void Trace(JS::Heap<JSScript*>* aPtr, const char* aName,
                      void* aClosure) const override;
   virtual void Trace(JS::Heap<JSFunction*>* aPtr, const char* aName,
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -2485,19 +2485,19 @@ class SnowWhiteKiller : public TraceCall
     }
   }
 
   virtual void Trace(JS::Heap<JSObject*>* aObject, const char* aName,
                      void* aClosure) const override {
     AppendJSObjectToPurpleBuffer(aObject->unbarrieredGet());
   }
 
-  virtual void Trace(JSObject** aObject, const char* aName,
+  virtual void Trace(nsWrapperCache* aWrapperCache, const char* aName,
                      void* aClosure) const override {
-    AppendJSObjectToPurpleBuffer(*aObject);
+    AppendJSObjectToPurpleBuffer(aWrapperCache->GetWrapperPreserveColor());
   }
 
   virtual void Trace(JS::TenuredHeap<JSObject*>* aObject, const char* aName,
                      void* aClosure) const override {
     AppendJSObjectToPurpleBuffer(aObject->unbarrieredGetPtr());
   }
 
   virtual void Trace(JS::Heap<JSString*>* aString, const char* aName,
--- a/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
+++ b/xpcom/base/nsCycleCollectorTraceJSHelpers.cpp
@@ -45,20 +45,21 @@ void TraceCallbackFunc::Trace(JS::Heap<j
 
 void TraceCallbackFunc::Trace(JS::Heap<JSObject*>* aPtr, const char* aName,
                               void* aClosure) const {
   if (*aPtr) {
     mCallback(JS::GCCellPtr(aPtr->unbarrieredGet()), aName, aClosure);
   }
 }
 
-void TraceCallbackFunc::Trace(JSObject** aPtr, const char* aName,
+void TraceCallbackFunc::Trace(nsWrapperCache* aPtr, const char* aName,
                               void* aClosure) const {
-  if (*aPtr) {
-    mCallback(JS::GCCellPtr(*aPtr), aName, aClosure);
+  JSObject* obj = aPtr->GetWrapperPreserveColor();
+  if (obj) {
+    mCallback(JS::GCCellPtr(obj), aName, aClosure);
   }
 }
 
 void TraceCallbackFunc::Trace(JS::TenuredHeap<JSObject*>* aPtr,
                               const char* aName, void* aClosure) const {
   if (*aPtr) {
     mCallback(JS::GCCellPtr(aPtr->unbarrieredGetPtr()), aName, aClosure);
   }
--- a/xpcom/build/PoisonIOInterposer.h
+++ b/xpcom/build/PoisonIOInterposer.h
@@ -32,17 +32,17 @@ void MozillaUnRegisterDebugHandle(intptr
 /** Unregister file descriptor from being ignored by poisoning IO interposer */
 void MozillaUnRegisterDebugFD(int aFd);
 
 /** Unregister file from being ignored by poisoning IO interposer */
 void MozillaUnRegisterDebugFILE(FILE* aFile);
 
 MOZ_END_EXTERN_C
 
-#if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(__MINGW32__))
+#if defined(XP_MACOSX) || defined(XP_WIN)
 
 #  ifdef __cplusplus
 namespace mozilla {
 
 /**
  * Check if a file is registered as a debug file.
  */
 bool IsDebugFile(intptr_t aFileID);
@@ -69,17 +69,17 @@ void OnlyReportDirtyWrites();
  * Clear IO poisoning, this is only safe to do on the main-thread when no other
  * threads are running.
  */
 void ClearPoisonIOInterposer();
 
 }  // namespace mozilla
 #  endif /* __cplusplus */
 
-#else /* defined(XP_MACOSX) || (defined(XP_WIN) && !defined(__MINGW32__)) */
+#else /* defined(XP_MACOSX) || defined(XP_WIN) */
 
 #  ifdef __cplusplus
 namespace mozilla {
 inline bool IsDebugFile(intptr_t aFileID) { return true; }
 inline void InitPoisonIOInterposer() {}
 inline void ClearPoisonIOInterposer() {}
 #    ifdef XP_MACOSX
 inline void OnlyReportDirtyWrites() {}
--- a/xpcom/build/moz.build
+++ b/xpcom/build/moz.build
@@ -25,24 +25,21 @@ EXPORTS.mozilla += [
     'XPCOM.h',
     'XREAppData.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS.mozilla += [
         'perfprobe.h',
     ]
-    SOURCES += ['perfprobe.cpp']
-    if CONFIG['CC_TYPE'] not in ('gcc', 'clang'):
-        SOURCES += [
-            'PoisonIOInterposerBase.cpp',
-            'PoisonIOInterposerWin.cpp',
-        ]
-    else:
-        SOURCES += ['PoisonIOInterposerStub.cpp']
+    SOURCES += [
+        'perfprobe.cpp',
+        'PoisonIOInterposerBase.cpp',
+        'PoisonIOInterposerWin.cpp',
+    ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     UNIFIED_SOURCES += [
         'PoisonIOInterposerBase.cpp',
         'PoisonIOInterposerMac.cpp',
     ]
     SOURCES += ['mach_override.c']
     SOURCES['mach_override.c'].flags += ['-Wno-unused-function']
 else: