Merge inbound to mozilla-central. a=merge
authorBrindusan Cristian <cbrindusan@mozilla.com>
Sat, 25 May 2019 00:49:05 +0300
changeset 475421 5d3e1ea7769357bce7297b83be3863034bcf656e
parent 475393 ac95bdf3c0b3f6f0bb31bb49e7714ca93a5a9c9a (current diff)
parent 475420 5eb6bcb8b9e484c5a169e9f7f663d1826d828615 (diff)
child 475472 8ea63d8493ac727f8c9b6f7d1fe96d7dc52020ac
push id36061
push usercbrindusan@mozilla.com
push dateFri, 24 May 2019 21:49:59 +0000
treeherdermozilla-central@5d3e1ea77693 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone69.0a1
first release with
nightly linux32
5d3e1ea77693 / 69.0a1 / 20190524214959 / files
nightly linux64
5d3e1ea77693 / 69.0a1 / 20190524214959 / files
nightly mac
5d3e1ea77693 / 69.0a1 / 20190524214959 / files
nightly win32
5d3e1ea77693 / 69.0a1 / 20190524214959 / files
nightly win64
5d3e1ea77693 / 69.0a1 / 20190524214959 / 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 inbound to mozilla-central. a=merge
dom/base/CharacterData.cpp
dom/base/CharacterData.h
dom/base/DirectionalityUtils.cpp
dom/base/DirectionalityUtils.h
dom/base/Selection.cpp
dom/base/nsContentUtils.cpp
dom/base/nsRange.cpp
dom/events/ContentEventHandler.cpp
dom/svg/SVGTextContentElement.cpp
editor/libeditor/HTMLEditRules.cpp
editor/libeditor/TextEditor.cpp
editor/libeditor/WSRunObject.cpp
gfx/thebes/gfxPlatform.cpp
js/public/AllocPolicy.h
js/public/RootingAPI.h
js/public/experimental/SourceHook.h
js/rust/src/jsglue.cpp
js/src/builtin/AtomicsObject.cpp
js/src/builtin/ModuleObject.cpp
js/src/builtin/ReflectParse.cpp
js/src/builtin/Stream.cpp
js/src/frontend/BinASTParser.cpp
js/src/frontend/BinASTParserPerTokenizer.cpp
js/src/frontend/BytecodeSection.h
js/src/frontend/FunctionEmitter.cpp
js/src/frontend/ParseContext.cpp
js/src/frontend/ParseContext.h
js/src/frontend/SharedContext.cpp
js/src/frontend/SharedContext.h
js/src/gc/Barrier.h
js/src/gc/FreeOp-inl.h
js/src/gc/GC-inl.h
js/src/gc/GC.cpp
js/src/gc/Marking.cpp
js/src/gc/NurseryAwareHashMap.h
js/src/gc/PrivateIterators-inl.h
js/src/gc/Scheduling.h
js/src/gc/Tracer.h
js/src/gc/Verifier.h
js/src/gc/Zone.cpp
js/src/gc/Zone.h
js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
js/src/jsapi-tests/testGCGrayMarking.cpp
js/src/jsapi-tests/testScriptObject.cpp
js/src/vm/ArrayBufferObject.cpp
js/src/vm/AtomsTable.h
js/src/vm/CompilationAndEvaluation.cpp
js/src/vm/Debugger-inl.h
js/src/vm/EnvironmentObject.cpp
js/src/vm/EnvironmentObject.h
js/src/vm/GeneratorObject.h
js/src/vm/JSScript.h
js/src/vm/Runtime.h
js/src/vm/SavedFrame.h
js/src/vm/SavedStacks.cpp
js/src/vm/Shape.h
js/src/vm/StringType-inl.h
js/src/vm/StringType.cpp
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmJS.h
js/src/wasm/WasmTable.h
js/xpconnect/src/XPCJSRuntime.cpp
layout/base/RestyleManager.cpp
layout/base/nsBidiPresUtils.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsLayoutUtils.cpp
layout/forms/nsTextControlFrame.cpp
layout/generic/nsFontInflationData.cpp
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
layout/generic/nsTextFrameUtils.cpp
layout/generic/nsTextFrameUtils.h
layout/svg/SVGObserverUtils.cpp
layout/svg/SVGTextFrame.cpp
testing/web-platform/meta/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.js.ini
testing/web-platform/meta/html/webappapis/microtask-queuing/queue-microtask.any.js.ini
testing/web-platform/meta/html/webappapis/microtask-queuing/queue-microtask.window.js.ini
widget/gtk/nsWindow.cpp
--- a/devtools/client/inspector/grids/components/GridItem.js
+++ b/devtools/client/inspector/grids/components/GridItem.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { createRef, PureComponent } = require("devtools/client/shared/vendor/react");
+const { createElement, createRef, Fragment, PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 loader.lazyGetter(this, "Rep", function() {
   return require("devtools/client/shared/components/reps/reps").REPS.Rep;
 });
 loader.lazyGetter(this, "MODE", function() {
   return require("devtools/client/shared/components/reps/reps").MODE;
@@ -19,16 +19,17 @@ loader.lazyRequireGetter(this, "translat
 
 const Types = require("../types");
 
 class GridItem extends PureComponent {
   static get propTypes() {
     return {
       getSwatchColorPickerTooltip: PropTypes.func.isRequired,
       grid: PropTypes.shape(Types.grid).isRequired,
+      grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
       onHideBoxModelHighlighter: PropTypes.func.isRequired,
       onSetGridOverlayColor: PropTypes.func.isRequired,
       onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
       onToggleGridHighlighter: PropTypes.func.isRequired,
       setSelectedNode: PropTypes.func.isRequired,
     };
   }
 
@@ -88,24 +89,52 @@ class GridItem extends PureComponent {
   }
 
   onGridInspectIconClick(nodeFront) {
     const { setSelectedNode } = this.props;
     setSelectedNode(nodeFront, { reason: "layout-panel" });
     nodeFront.scrollIntoView().catch(e => console.error(e));
   }
 
+  renderSubgrids() {
+    const { grid, grids } = this.props;
+
+    if (!grid.subgrids.length) {
+      return null;
+    }
+
+    const subgrids = grids.filter(g => grid.subgrids.includes(g.id));
+
+    return (
+      dom.ul({},
+        subgrids.map(g => {
+          return createElement(GridItem, {
+            key: g.id,
+            getSwatchColorPickerTooltip: this.props.getSwatchColorPickerTooltip,
+            grid: g,
+            grids,
+            onHideBoxModelHighlighter: this.props.onHideBoxModelHighlighter,
+            onSetGridOverlayColor: this.props.onSetGridOverlayColor,
+            onShowBoxModelHighlighterForNode: this.props.onShowBoxModelHighlighterForNode,
+            onToggleGridHighlighter: this.props.onToggleGridHighlighter,
+            setSelectedNode: this.props.setSelectedNode,
+          });
+        })
+      )
+    );
+  }
+
   render() {
     const {
       grid,
       onHideBoxModelHighlighter,
       onShowBoxModelHighlighterForNode,
     } = this.props;
 
-    return (
+    return createElement(Fragment, null,
       dom.li({},
         dom.label({},
           dom.input(
             {
               checked: grid.highlighted,
               disabled: grid.disabled,
               type: "checkbox",
               value: grid.id,
@@ -137,14 +166,15 @@ class GridItem extends PureComponent {
         // requirement. See https://bugzilla.mozilla.org/show_bug.cgi?id=1341578
         dom.span(
           {
             className: "layout-color-value",
             ref: this.colorValueEl,
           },
           grid.color
         )
-      )
+      ),
+      this.renderSubgrids()
     );
   }
 }
 
 module.exports = GridItem;
--- a/devtools/client/inspector/grids/components/GridList.js
+++ b/devtools/client/inspector/grids/components/GridList.js
@@ -40,20 +40,24 @@ class GridList extends PureComponent {
     return (
       dom.div({ className: "grid-container" },
         dom.span({}, getStr("layout.overlayGrid")),
         dom.ul(
           {
             id: "grid-list",
             className: "devtools-monospace",
           },
-          grids.map(grid => GridItem({
+          grids
+          // Skip subgrids since they are rendered by their parent grids in GridItem.
+          .filter(grid => !grid.isSubgrid)
+          .map(grid => GridItem({
             key: grid.id,
             getSwatchColorPickerTooltip,
             grid,
+            grids,
             onHideBoxModelHighlighter,
             onSetGridOverlayColor,
             onShowBoxModelHighlighterForNode,
             onToggleGridHighlighter,
             setSelectedNode,
           }))
         )
       )
--- a/devtools/client/inspector/grids/grid-inspector.js
+++ b/devtools/client/inspector/grids/grid-inspector.js
@@ -313,33 +313,57 @@ class GridInspector {
 
       const colorForHost = customColors[hostname] ? customColors[hostname][i] : null;
       const fallbackColor = GRID_COLORS[i % GRID_COLORS.length];
       const color = this.getInitialGridColor(nodeFront, colorForHost, fallbackColor);
       const highlighted = this.highlighters.gridHighlighters.has(nodeFront);
       const disabled = !highlighted &&
                        this.maxHighlighters > 1 &&
                        this.highlighters.gridHighlighters.size === this.maxHighlighters;
-
-      grids.push({
+      const isSubgrid = grid.isSubgrid;
+      const gridData = {
         id: i,
         actorID: grid.actorID,
         color,
         disabled,
         direction: grid.direction,
         gridFragments: grid.gridFragments,
         highlighted,
+        isSubgrid,
         nodeFront,
+        parentNodeActorID: null,
+        subgrids: [],
         writingMode: grid.writingMode,
-      });
+      };
+
+      if (isSubgrid &&
+          await this.inspector.target.actorHasMethod("domwalker", "getParentGridNode")) {
+        let parentGridNodeFront;
+
+        try {
+          parentGridNodeFront = await this.walker.getParentGridNode(nodeFront);
+        } catch (e) {
+          // This call might fail if called asynchrously after the toolbox is finished
+          // closing.
+          return;
+        }
+
+        const parentIndex = grids.findIndex(g =>
+          g.nodeFront.actorID === parentGridNodeFront.actorID);
+        gridData.parentNodeActorID = parentGridNodeFront.actorID;
+        grids[parentIndex].subgrids.push(gridData.id);
+      }
+
+      grids.push(gridData);
     }
 
     this.store.dispatch(updateGrids(grids));
     this.inspector.emit("grid-panel-updated");
   }
+
   /**
    * Handler for "grid-highlighter-shown" events emitted from the
    * HighlightersOverlay. Passes nodefront and event name to handleHighlighterChange.
    * Required since on and off events need the same reference object.
    *
    * @param  {NodeFront} nodeFront
    *         The NodeFront of the grid container element for which the grid
    *         highlighter is shown for.
--- a/devtools/client/inspector/grids/test/browser.ini
+++ b/devtools/client/inspector/grids/test/browser.ini
@@ -1,13 +1,14 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   doc_iframe_reloaded.html
+  doc_subgrid.html
   head.js
   !/devtools/client/inspector/test/head.js
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/shared-redux-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
@@ -20,16 +21,17 @@ support-files =
 [browser_grids_grid-list-color-picker-on-ESC.js]
 [browser_grids_grid-list-color-picker-on-RETURN.js]
 [browser_grids_grid-list-element-rep.js]
 [browser_grids_grid-list-no-grids.js]
 [browser_grids_grid-list-on-iframe-reloaded.js]
 skip-if = (verify && (os == 'win' || os == 'linux'))
 [browser_grids_grid-list-on-mutation-element-added.js]
 [browser_grids_grid-list-on-mutation-element-removed.js]
+[browser_grids_grid-list-subgrids.js]
 [browser_grids_grid-list-toggle-grids_01.js]
 [browser_grids_grid-list-toggle-grids_02.js]
 [browser_grids_grid-list-toggle-multiple-grids.js]
 [browser_grids_grid-outline-cannot-show-outline.js]
 [browser_grids_grid-outline-highlight-area.js]
 skip-if = (verify && (os == 'win')) || (os == "win" && os_version == "10.0" && !debug) #Bug 1501760
 [browser_grids_grid-outline-highlight-cell.js]
 skip-if = (verify && (os == 'win')) || (os == "win" && os_version == "10.0" && asan) #Bug 1501317
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_grid-list-subgrids.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the list of grids show the subgrids in the correct nested list and toggling
+// the CSS grid highlighter for a subgrid.
+
+const TEST_URI = URL_ROOT + "doc_subgrid.html";
+
+add_task(async function() {
+  await addTab(TEST_URI);
+  const { gridInspector, inspector } = await openLayoutView();
+  const { document: doc } = gridInspector;
+  const { highlighters, store } = inspector;
+
+  await selectNode("#grid", inspector);
+  const gridListEl = doc.getElementById("grid-list");
+  const containerSubgridListEl = gridListEl.children[1];
+  const mainSubgridListEl = containerSubgridListEl.querySelector("ul");
+
+  info("Checking the initial state of the Grid Inspector.");
+  is(getGridItemElements(gridListEl).length, 1, "One grid container is listed.");
+  is(getGridItemElements(containerSubgridListEl).length, 2,
+    "Got the correct number of subgrids in div.container");
+  is(getGridItemElements(mainSubgridListEl).length, 2,
+    "Got the correct number of subgrids in main.subgrid");
+  ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
+
+  info("Toggling ON the CSS grid highlighter from the layout panel.");
+  const onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state => state.grids[1].highlighted);
+  const checkbox = containerSubgridListEl.children[0].querySelector("input");
+  checkbox.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is created.");
+  is(highlighters.gridHighlighters.size, 1, "CSS grid highlighter is shown.");
+
+  info("Toggling OFF the CSS grid highlighter from the layout panel.");
+  const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state => !state.grids[1].highlighted);
+  checkbox.click();
+  await onHighlighterHidden;
+  await onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is not shown.");
+  ok(!highlighters.gridHighlighters.size, "No CSS grid highlighter is shown.");
+});
+
+/**
+ * Returns the grid item elements <li> from the grid list element <ul>.
+ *
+ * @param  {Element} gridListEl
+ *         The grid list element <ul>.
+ * @return {Array<Element>} containing the grid item elements <li>.
+ */
+function getGridItemElements(gridListEl) {
+  return [...gridListEl.children].filter(node => node.nodeName === "li");
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/doc_subgrid.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8" />
+  <style>
+    .container {
+      display: grid;
+      grid-gap: 5px;
+      grid-template: auto / 1fr 3fr 1fr;
+      background: lightyellow;
+    }
+
+    .subgrid {
+      display: grid;
+      grid: subgrid / subgrid;
+    }
+
+    header, aside, section, footer {
+      background: lightblue;
+      font-family: sans-serif;
+      font-size: 3em;
+    }
+
+    header, footer {
+      grid-column: span 3;
+    }
+
+    main {
+      grid-column: span 3;
+    }
+
+    .aside1 {
+      grid-column: 1;
+    }
+
+    .aside2 {
+      grid-column: 3;
+    }
+
+    section {
+      grid-column: 2;
+    }
+  </style>
+</head>
+<body>
+  <div class="container">
+    <header class="subgrid">Header</header>
+    <main class="subgrid">
+      <aside class="aside1 subgrid">aside</aside>
+      <section>section</section>
+      <aside class="aside2 subgrid">aside2</aside>
+    </main>
+    <footer>footer</footer>
+  </div>
+</body>
+</html>
--- a/devtools/client/inspector/grids/types.js
+++ b/devtools/client/inspector/grids/types.js
@@ -24,19 +24,28 @@ exports.grid = {
   disabled: PropTypes.bool,
 
   // The grid fragment object of the grid container
   gridFragments: PropTypes.array,
 
   // Whether or not the grid highlighter is highlighting the grid
   highlighted: PropTypes.bool,
 
+  // Whether or not the grid is a subgrid
+  isSubgrid: PropTypes.bool,
+
   // The node front of the grid container
   nodeFront: PropTypes.object,
 
+  // If the grid is a subgrid, this references the parent node front actor ID
+  parentNodeActorID: PropTypes.string,
+
+  // Array of ids belonging to the subgrid within the grid container
+  subgrids: PropTypes.arrayOf(PropTypes.number),
+
   // The writing mode of the grid container
   writingMode: PropTypes.string,
 };
 
 /**
  * The grid highlighter settings on what to display in its grid overlay in the document.
  */
 exports.highlighterSettings = {
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -33,16 +33,20 @@
   text-overflow: ellipsis;
   overflow: hidden;
 }
 
 /**
  * Common styles for the layout container
  */
 
+.layout-container ul {
+  list-style: none;
+}
+
 .layout-container li {
   padding: 3px 0;
   -moz-user-select: none;
 }
 
 .layout-container input {
   margin-inline-end: 7px;
   vertical-align: middle;
@@ -638,21 +642,24 @@ html[dir="rtl"] .flex-item-list .devtool
 
 .grid-container > span {
   font-weight: 600;
   margin-bottom: 5px;
   pointer-events: none;
 }
 
 .grid-container > ul {
-  list-style: none;
   margin: 0;
   padding: 0;
 }
 
+#grid-list ul {
+  padding-inline-start: 20px;
+}
+
 /**
  * Grid Content
  */
 
 .grid-content {
   display: flex;
   flex-wrap: wrap;
   flex: 1;
--- a/devtools/server/actors/inspector/utils.js
+++ b/devtools/server/actors/inspector/utils.js
@@ -312,18 +312,48 @@ function getClosestBackgroundImage(node)
     }
 
     current = current.parentNode;
   }
 
   return "none";
 }
 
+/**
+ * If the provided node is a grid item, then return its parent grid.
+ *
+ * @param  {DOMNode} node
+ *         The node that is supposedly a grid item.
+ * @return {DOMNode|null}
+ *         The parent grid if found, null otherwise.
+ */
+function findGridParentContainerForNode(node) {
+  try {
+    while ((node = node.parentNode)) {
+      const display = node.ownerGlobal.getComputedStyle(node).display;
+
+      if (display.includes("grid")) {
+        return node;
+      } else if (display === "contents") {
+        // Continue walking up the tree since the parent node is a content element.
+        continue;
+      }
+
+      break;
+    }
+  } catch (e) {
+    // Getting the parentNode can fail when the supplied node is in shadow DOM.
+  }
+
+  return null;
+}
+
 module.exports = {
   allAnonymousContentTreeWalkerFilter,
+  findGridParentContainerForNode,
   getClosestBackgroundColor,
   getClosestBackgroundImage,
   getNodeDisplayName,
   imageToImageData,
   isNodeDead,
   nodeDocument,
   scrollbarTreeWalkerFilter,
   standardTreeWalkerFilter,
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -23,16 +23,17 @@ loader.lazyRequireGetter(this, "isNative
 loader.lazyRequireGetter(this, "isShadowHost", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isShadowRoot", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "isTemplateElement", "devtools/shared/layout/utils", true);
 loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);
 
 loader.lazyRequireGetter(this, "throttle", "devtools/shared/throttle", true);
 
 loader.lazyRequireGetter(this, "allAnonymousContentTreeWalkerFilter", "devtools/server/actors/inspector/utils", true);
+loader.lazyRequireGetter(this, "findGridParentContainerForNode", "devtools/server/actors/inspector/utils", true);
 loader.lazyRequireGetter(this, "isNodeDead", "devtools/server/actors/inspector/utils", true);
 loader.lazyRequireGetter(this, "nodeDocument", "devtools/server/actors/inspector/utils", true);
 loader.lazyRequireGetter(this, "standardTreeWalkerFilter", "devtools/server/actors/inspector/utils", true);
 
 loader.lazyRequireGetter(this, "CustomElementWatcher", "devtools/server/actors/inspector/custom-element-watcher", true);
 loader.lazyRequireGetter(this, "DocumentWalker", "devtools/server/actors/inspector/document-walker", true);
 loader.lazyRequireGetter(this, "SKIP_TO_SIBLING", "devtools/server/actors/inspector/document-walker", true);
 loader.lazyRequireGetter(this, "NodeActor", "devtools/server/actors/inspector/node", true);
@@ -2206,16 +2207,29 @@ var WalkerActor = protocol.ActorClassWit
     if (!this.layoutActor) {
       this.layoutActor = new LayoutActor(this.conn, this.targetActor, this);
     }
 
     return this.layoutActor;
   },
 
   /**
+   * Returns the parent grid DOMNode of the given node if it exists, otherwise, it
+   * returns null.
+   */
+  getParentGridNode: function(node) {
+    if (isNodeDead(node)) {
+      return null;
+    }
+
+    const parentGridNode = findGridParentContainerForNode(node.rawNode);
+    return parentGridNode ? this._ref(parentGridNode) : null;
+  },
+
+  /**
    * Returns the offset parent DOMNode of the given node if it exists, otherwise, it
    * returns null.
    */
   getOffsetParent: function(node) {
     if (isNodeDead(node)) {
       return null;
     }
 
--- a/devtools/server/actors/layout.js
+++ b/devtools/server/actors/layout.js
@@ -1,31 +1,36 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Cu } = require("chrome");
+const Services = require("Services");
 const { Actor, ActorClassWithSpec } = require("devtools/shared/protocol");
 const {
   flexboxSpec,
   flexItemSpec,
   gridSpec,
   layoutSpec,
 } = require("devtools/shared/specs/layout");
 const { getStringifiableFragments } =
   require("devtools/server/actors/utils/css-grid-utils");
 
 loader.lazyRequireGetter(this, "CssLogic", "devtools/server/actors/inspector/css-logic", true);
+loader.lazyRequireGetter(this, "findGridParentContainerForNode", "devtools/server/actors/inspector/utils", true);
 loader.lazyRequireGetter(this, "getCSSStyleRules", "devtools/shared/inspector/css-logic", true);
 loader.lazyRequireGetter(this, "isCssPropertyKnown", "devtools/server/actors/css-properties", true);
 loader.lazyRequireGetter(this, "parseDeclarations", "devtools/shared/css/parsing-utils", true);
 loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
 
+const SUBGRID_ENABLED =
+  Services.prefs.getBoolPref("layout.css.grid-template-subgrid-value.enabled");
+
 /**
  * Set of actors the expose the CSS layout information to the devtools protocol clients.
  *
  * The |Layout| actor is the main entry point. It is used to get various CSS
  * layout-related information from the document.
  *
  * The |Flexbox| actor provides the container node information to inspect the flexbox
  * container. It is also used to return an array of |FlexItem| actors which provide the
@@ -269,32 +274,42 @@ const GridActor = ActorClassWithSpec(gri
 
   form() {
     // Seralize the grid fragment data into JSON so protocol.js knows how to write
     // and read the data.
     const gridFragments = this.containerEl.getGridFragments();
     this.gridFragments = getStringifiableFragments(gridFragments);
 
     // Record writing mode and text direction for use by the grid outline.
-    const { direction, writingMode } = CssLogic.getComputedStyle(this.containerEl);
+    const {
+      direction,
+      gridTemplateColumns,
+      gridTemplateRows,
+      writingMode,
+    } = CssLogic.getComputedStyle(this.containerEl);
 
     const form = {
       actor: this.actorID,
       direction,
       gridFragments: this.gridFragments,
       writingMode,
     };
 
     // If the WalkerActor already knows the container element, then also return its
     // ActorID so we avoid the client from doing another round trip to get it in many
     // cases.
     if (this.walker.hasNode(this.containerEl)) {
       form.containerNodeActorID = this.walker.getNode(this.containerEl).actorID;
     }
 
+    if (SUBGRID_ENABLED) {
+      form.isSubgrid = gridTemplateRows === "subgrid" ||
+                       gridTemplateColumns === "subgrid";
+    }
+
     return form;
   },
 });
 
 /**
  * The CSS layout actor provides layout information for the given document.
  */
 const LayoutActor = ActorClassWithSpec(layoutSpec, {
@@ -450,42 +465,12 @@ const LayoutActor = ActorClassWithSpec(l
     return gridActors;
   },
 });
 
 function isNodeDead(node) {
   return !node || (node.rawNode && Cu.isDeadWrapper(node.rawNode));
 }
 
-/**
- * If the provided node is a grid item, then return its parent grid.
- *
- * @param  {DOMNode} node
- *         The node that is supposedly a grid item.
- * @return {DOMNode|null}
- *         The parent grid if found, null otherwise.
- */
-function findGridParentContainerForNode(node) {
-  try {
-    while ((node = node.parentNode)) {
-      const display = node.ownerGlobal.getComputedStyle(node).display;
-
-      if (display.includes("grid")) {
-        return node;
-      } else if (display === "contents") {
-        // Continue walking up the tree since the parent node is a content element.
-        continue;
-      }
-
-      break;
-    }
-  } catch (e) {
-    // Getting the parentNode can fail when the supplied node is in shadow DOM.
-  }
-
-  return null;
-}
-
-exports.findGridParentContainerForNode = findGridParentContainerForNode;
 exports.FlexboxActor = FlexboxActor;
 exports.FlexItemActor = FlexItemActor;
 exports.GridActor = GridActor;
 exports.LayoutActor = LayoutActor;
--- a/devtools/server/actors/replay/control.js
+++ b/devtools/server/actors/replay/control.js
@@ -269,16 +269,23 @@ ChildProcess.prototype = {
         if (manifest.onFinished) {
           manifest.onFinished(this, data);
         }
         resolve(this);
         pokeChildSoon(this);
       },
     });
     this.asyncManifests.shift();
+
+    // If this is the active child then we shouldn't leave it in an unpaused
+    // state, so callers can interact with it as expected.
+    if (this == gActiveChild) {
+      this.waitUntilPaused();
+    }
+
     return true;
   },
 };
 
 // Child which is always at the end of the recording. When there is a recording
 // child this is it, and when we are replaying an old execution this is a
 // replaying child that is unable to rewind and is used in the same way as the
 // recording child.
@@ -1209,17 +1216,19 @@ const gControl = {
 
     if (data.restoredCheckpoint) {
       // The child had an unhandled recording diverge and restored an earlier
       // checkpoint. Restore the child to the point it should be paused at and
       // fill its paused state back in by resending earlier debugger requests.
       pauseReplayingChild(gPausePoint);
       gActiveChild.sendManifest({
         contents: { kind: "batchDebuggerRequest", requests: gDebuggerRequests },
-        onFinished(finishData) { assert(!finishData.restoredCheckpoint); },
+        onFinished(finishData) {
+          assert(!finishData || !finishData.restoredCheckpoint);
+        },
       });
       gActiveChild.waitUntilPaused();
       return { unhandledDivergence: true };
     }
 
     if (data.divergedFromRecording) {
       // Remember whether the child diverged from the recording.
       gActiveChild.divergedFromRecording = true;
--- a/devtools/shared/fronts/layout.js
+++ b/devtools/shared/fronts/layout.js
@@ -108,16 +108,23 @@ class GridFront extends FrontClassWithSp
   /**
    * Getter for the grid fragments data.
    */
   get gridFragments() {
     return this._form.gridFragments;
   }
 
   /**
+   * Get whether or not the grid is a subgrid.
+   */
+  get isSubgrid() {
+    return !!this._form.isSubgrid;
+  }
+
+  /**
    * Get the writing mode of the grid container.
    * Added in Firefox 60.
    */
   get writingMode() {
     if (!this._form.writingMode) {
       return "horizontal-tb";
     }
 
--- a/devtools/shared/specs/inspector.js
+++ b/devtools/shared/specs/inspector.js
@@ -321,16 +321,24 @@ const walkerSpec = generateActorSpec({
       },
     },
     getLayoutInspector: {
       request: {},
       response: {
         actor: RetVal("layout"),
       },
     },
+    getParentGridNode: {
+      request: {
+        node: Arg(0, "nullable:domnode"),
+      },
+      response: {
+        node: RetVal("nullable:domnode"),
+      },
+    },
     getOffsetParent: {
       request: {
         node: Arg(0, "nullable:domnode"),
       },
       response: {
         node: RetVal("nullable:domnode"),
       },
     },
--- a/dom/base/CharacterData.cpp
+++ b/dom/base/CharacterData.cpp
@@ -246,17 +246,18 @@ nsresult CharacterData::SetTextInternal(
     CharacterDataChangeInfo info = {aOffset == textLength, aOffset, endOffset,
                                     aLength, aDetails};
     nsNodeUtils::CharacterDataWillChange(this, info);
   }
 
   Directionality oldDir = eDir_NotSet;
   bool dirAffectsAncestor =
       (NodeType() == TEXT_NODE &&
-       TextNodeWillChangeDirection(this, &oldDir, aOffset));
+       TextNodeWillChangeDirection(static_cast<nsTextNode*>(this), &oldDir,
+                                   aOffset));
 
   if (aOffset == 0 && endOffset == textLength) {
     // Replacing whole text or old text was empty.  Don't bother to check for
     // bidi in this string if the document already has bidi enabled.
     // If this is marked as "maybe modified frequently", the text should be
     // stored as char16_t since converting char* to char16_t* is expensive.
     bool ok =
         mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled(),
--- a/dom/base/CharacterData.h
+++ b/dom/base/CharacterData.h
@@ -112,20 +112,20 @@ class CharacterData : public nsIContent 
 
   void UnbindFromTree(bool aDeep = true, bool aNullParent = true) override;
 
   already_AddRefed<nsINodeList> GetChildren(uint32_t aFilter) final {
     return nullptr;
   }
 
   const nsTextFragment* GetText() override { return &mText; }
+  uint32_t TextLength() const final { return TextDataLength(); }
 
   const nsTextFragment& TextFragment() const { return mText; }
-
-  uint32_t TextLength() const final { return TextDataLength(); }
+  uint32_t TextDataLength() const { return mText.GetLength(); }
 
   /**
    * Set the text to the given value. If aNotify is true then
    * the document is notified of the content change.
    */
   nsresult SetText(const char16_t* aBuffer, uint32_t aLength, bool aNotify);
   /**
    * Append the given value to the current text. If aNotify is true then
@@ -192,18 +192,16 @@ class CharacterData : public nsIContent 
   void SubstringData(uint32_t aStart, uint32_t aCount, nsAString& aReturn,
                      ErrorResult& rv);
   void AppendData(const nsAString& aData, ErrorResult& rv);
   void InsertData(uint32_t aOffset, const nsAString& aData, ErrorResult& rv);
   void DeleteData(uint32_t aOffset, uint32_t aCount, ErrorResult& rv);
   void ReplaceData(uint32_t aOffset, uint32_t aCount, const nsAString& aData,
                    ErrorResult& rv);
 
-  uint32_t TextDataLength() const { return mText.GetLength(); }
-
   //----------------------------------------
 
 #ifdef DEBUG
   void ToCString(nsAString& aBuf, int32_t aOffset, int32_t aLen) const;
 #endif
 
   NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(
       CharacterData, nsIContent)
--- a/dom/base/DirectionalityUtils.cpp
+++ b/dom/base/DirectionalityUtils.cpp
@@ -373,24 +373,24 @@ static Directionality GetDirectionFromTe
   }
 
   if (aFirstStrong) {
     *aFirstStrong = UINT32_MAX;
   }
   return eDir_NotSet;
 }
 
-static Directionality GetDirectionFromText(const nsTextFragment* aFrag,
+static Directionality GetDirectionFromText(const Text* aTextNode,
                                            uint32_t* aFirstStrong = nullptr) {
-  if (aFrag->Is2b()) {
-    return GetDirectionFromText(aFrag->Get2b(), aFrag->GetLength(),
-                                aFirstStrong);
+  const nsTextFragment* frag = &aTextNode->TextFragment();
+  if (frag->Is2b()) {
+    return GetDirectionFromText(frag->Get2b(), frag->GetLength(), aFirstStrong);
   }
 
-  return GetDirectionFromText(aFrag->Get1b(), aFrag->GetLength(), aFirstStrong);
+  return GetDirectionFromText(frag->Get1b(), frag->GetLength(), aFirstStrong);
 }
 
 static nsTextNode* WalkDescendantsAndGetDirectionFromText(
     nsINode* aRoot, nsINode* aSkip, Directionality* aDirectionality) {
   nsIContent* child = aRoot->GetFirstChild();
   while (child) {
     if ((child->IsElement() &&
          DoesNotAffectDirectionOfAncestors(child->AsElement())) ||
@@ -400,41 +400,42 @@ static nsTextNode* WalkDescendantsAndGet
     }
 
     HTMLSlotElement* slot = HTMLSlotElement::FromNode(child);
     if (slot) {
       const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
       for (uint32_t i = 0; i < assignedNodes.Length(); ++i) {
         nsIContent* assignedNode = assignedNodes[i]->AsContent();
         if (assignedNode->NodeType() == nsINode::TEXT_NODE) {
+          auto text = static_cast<nsTextNode*>(assignedNode);
           if (assignedNode != aSkip) {
-            Directionality textNodeDir =
-                GetDirectionFromText(assignedNode->GetText());
+            Directionality textNodeDir = GetDirectionFromText(text);
             if (textNodeDir != eDir_NotSet) {
               *aDirectionality = textNodeDir;
-              return static_cast<nsTextNode*>(assignedNode);
+              return text;
             }
           }
         } else if (assignedNode->IsElement() &&
                    !DoesNotAffectDirectionOfAncestors(
                        assignedNode->AsElement())) {
           nsTextNode* text = WalkDescendantsAndGetDirectionFromText(
               assignedNode, aSkip, aDirectionality);
           if (text) {
             return text;
           }
         }
       }
     }
 
     if (child->NodeType() == nsINode::TEXT_NODE && child != aSkip) {
-      Directionality textNodeDir = GetDirectionFromText(child->GetText());
+      auto text = static_cast<nsTextNode*>(child);
+      Directionality textNodeDir = GetDirectionFromText(text);
       if (textNodeDir != eDir_NotSet) {
         *aDirectionality = textNodeDir;
-        return static_cast<nsTextNode*>(child);
+        return text;
       }
     }
     child = child->GetNextNode(aRoot);
   }
 
   return nullptr;
 }
 
@@ -1047,31 +1048,31 @@ void SetAncestorDirectionIfAuto(nsTextNo
       // parent chain: none of its ancestors will have their direction set by
       // any of its descendants.
       return;
     }
     parent = GetParentOrHostOrSlot(parent, &crossedShadowBoundary);
   }
 }
 
-bool TextNodeWillChangeDirection(nsIContent* aTextNode, Directionality* aOldDir,
+bool TextNodeWillChangeDirection(nsTextNode* aTextNode, Directionality* aOldDir,
                                  uint32_t aOffset) {
   if (!NodeAffectsDirAutoAncestor(aTextNode)) {
     nsTextNodeDirectionalityMap::EnsureMapIsClearFor(aTextNode);
     return false;
   }
 
   uint32_t firstStrong;
-  *aOldDir = GetDirectionFromText(aTextNode->GetText(), &firstStrong);
+  *aOldDir = GetDirectionFromText(aTextNode, &firstStrong);
   return (aOffset <= firstStrong);
 }
 
 void TextNodeChangedDirection(nsTextNode* aTextNode, Directionality aOldDir,
                               bool aNotify) {
-  Directionality newDir = GetDirectionFromText(aTextNode->GetText());
+  Directionality newDir = GetDirectionFromText(aTextNode);
   if (newDir == eDir_NotSet) {
     if (aOldDir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
       // This node used to have a strong directional character but no
       // longer does. ResetTextNodeDirection() will re-resolve the
       // directionality of any elements whose directionality was
       // determined by this node.
       nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode, aTextNode);
     }
@@ -1097,29 +1098,29 @@ void SetDirectionFromNewTextNode(nsTextN
     return;
   }
 
   nsIContent* parent = GetParentOrHostOrSlot(aTextNode);
   if (parent && parent->NodeOrAncestorHasDirAuto()) {
     aTextNode->SetAncestorHasDirAuto();
   }
 
-  Directionality dir = GetDirectionFromText(aTextNode->GetText());
+  Directionality dir = GetDirectionFromText(aTextNode);
   if (dir != eDir_NotSet) {
     SetAncestorDirectionIfAuto(aTextNode, dir);
   }
 }
 
 void ResetDirectionSetByTextNode(nsTextNode* aTextNode) {
   if (!NodeAffectsDirAutoAncestor(aTextNode)) {
     nsTextNodeDirectionalityMap::EnsureMapIsClearFor(aTextNode);
     return;
   }
 
-  Directionality dir = GetDirectionFromText(aTextNode->GetText());
+  Directionality dir = GetDirectionFromText(aTextNode);
   if (dir != eDir_NotSet && aTextNode->HasTextNodeDirectionalityMap()) {
     nsTextNodeDirectionalityMap::ResetTextNodeDirection(aTextNode, aTextNode);
   }
 }
 
 void SetDirectionalityFromValue(Element* aElement, const nsAString& value,
                                 bool aNotify) {
   Directionality dir =
--- a/dom/base/DirectionalityUtils.h
+++ b/dom/base/DirectionalityUtils.h
@@ -91,17 +91,17 @@ void WalkDescendantsSetDirAuto(mozilla::
 void WalkDescendantsClearAncestorDirAuto(nsIContent* aContent);
 
 /**
  * When the contents of a text node are about to change, retrieve the current
  * directionality of the text
  *
  * @return whether the text node affects the directionality of any element
  */
-bool TextNodeWillChangeDirection(nsIContent* aTextNode, Directionality* aOldDir,
+bool TextNodeWillChangeDirection(nsTextNode* aTextNode, Directionality* aOldDir,
                                  uint32_t aOffset);
 
 /**
  * After the contents of a text node have changed, change the directionality
  * of any elements whose directionality is determined by that node
  */
 void TextNodeChangedDirection(nsTextNode* aTextNode, Directionality aOldDir,
                               bool aNotify);
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -1469,18 +1469,18 @@ void Selection::SelectFramesForContent(n
   nsIFrame* frame = aContent->GetPrimaryFrame();
   if (!frame) {
     return;
   }
   // The frame could be an SVG text frame, in which case we don't treat it
   // as a text frame.
   if (frame->IsTextFrame()) {
     nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
-    textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(), aSelected,
-                                mSelectionType);
+    textFrame->SetSelectedRange(0, textFrame->TextFragment()->GetLength(),
+                                aSelected, mSelectionType);
   } else {
     frame->InvalidateFrameSubtree();  // frame continuations?
   }
 }
 
 // select all content children of aContent
 nsresult Selection::SelectAllFramesForContent(
     PostContentIterator& aPostOrderIter, nsIContent* aContent, bool aSelected) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9052,18 +9052,17 @@ bool nsContentUtils::SerializeNodeToMark
           current = next;
           continue;
         }
         break;
       }
 
       case nsINode::TEXT_NODE:
       case nsINode::CDATA_SECTION_NODE: {
-        const nsTextFragment* text =
-            static_cast<nsIContent*>(current)->GetText();
+        const nsTextFragment* text = &current->AsText()->TextFragment();
         nsIContent* parent = current->GetParent();
         if (ShouldEscape(parent)) {
           AppendEncodedCharacters(text, builder);
         } else {
           builder.Append(text);
         }
         break;
       }
--- a/dom/base/nsIGlobalObject.cpp
+++ b/dom/base/nsIGlobalObject.cpp
@@ -1,32 +1,39 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIGlobalObject.h"
-
+#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
+#include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "nsContentUtils.h"
 #include "nsThreadUtils.h"
 #include "nsGlobalWindowInner.h"
 
+using mozilla::AutoSlowOperation;
+using mozilla::CycleCollectedJSContext;
 using mozilla::DOMEventTargetHelper;
+using mozilla::ErrorResult;
+using mozilla::IgnoredErrorResult;
 using mozilla::MallocSizeOf;
 using mozilla::Maybe;
+using mozilla::MicroTaskRunnable;
 using mozilla::dom::BlobURLProtocolHandler;
 using mozilla::dom::ClientInfo;
 using mozilla::dom::ServiceWorker;
 using mozilla::dom::ServiceWorkerDescriptor;
 using mozilla::dom::ServiceWorkerRegistration;
 using mozilla::dom::ServiceWorkerRegistrationDescriptor;
+using mozilla::dom::VoidFunction;
 
 nsIGlobalObject::~nsIGlobalObject() {
   UnlinkHostObjectURIs();
   DisconnectEventTargetObjects();
   MOZ_DIAGNOSTIC_ASSERT(mEventTargetObjects.isEmpty());
 }
 
 nsIPrincipal* nsIGlobalObject::PrincipalOrNull() {
@@ -207,8 +214,33 @@ nsPIDOMWindowInner* nsIGlobalObject::AsI
   }
   return nullptr;
 }
 
 size_t nsIGlobalObject::ShallowSizeOfExcludingThis(MallocSizeOf aSizeOf) const {
   size_t rtn = mHostObjectURIs.ShallowSizeOfExcludingThis(aSizeOf);
   return rtn;
 }
+
+class QueuedMicrotask : public MicroTaskRunnable {
+ public:
+  QueuedMicrotask(nsIGlobalObject* aGlobal, VoidFunction& aCallback)
+      : mGlobal(aGlobal), mCallback(&aCallback) {}
+
+  MOZ_CAN_RUN_SCRIPT_BOUNDARY void Run(AutoSlowOperation& aAso) final {
+    IgnoredErrorResult rv;
+    MOZ_KnownLive(mCallback)->Call(static_cast<ErrorResult&>(rv));
+  }
+
+  bool Suppressed() final { return mGlobal->IsInSyncOperation(); }
+
+ private:
+  nsCOMPtr<nsIGlobalObject> mGlobal;
+  RefPtr<VoidFunction> mCallback;
+};
+
+void nsIGlobalObject::QueueMicrotask(VoidFunction& aCallback) {
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+  if (context) {
+    RefPtr<MicroTaskRunnable> mt = new QueuedMicrotask(this, aCallback);
+    context->DispatchToMicroTask(mt.forget());
+  }
+}
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -29,16 +29,17 @@
 
 class nsCycleCollectionTraversalCallback;
 class nsIPrincipal;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
+class VoidFunction;
 class ServiceWorker;
 class ServiceWorkerRegistration;
 class ServiceWorkerRegistrationDescriptor;
 }  // namespace dom
 }  // namespace mozilla
 
 class nsIGlobalObject : public nsISupports,
                         public mozilla::dom::DispatcherTrait {
@@ -152,16 +153,18 @@ class nsIGlobalObject : public nsISuppor
   virtual RefPtr<mozilla::dom::ServiceWorkerRegistration>
   GetOrCreateServiceWorkerRegistration(
       const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor);
 
   // Returns a pointer to this object as an inner window if this is one or
   // nullptr otherwise.
   nsPIDOMWindowInner* AsInnerWindow();
 
+  void QueueMicrotask(mozilla::dom::VoidFunction& aCallback);
+
  protected:
   virtual ~nsIGlobalObject();
 
   void StartDying() { mIsDying = true; }
 
   void DisconnectEventTargetObjects();
 
   size_t ShallowSizeOfExcludingThis(mozilla::MallocSizeOf aSizeOf) const;
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -460,20 +460,22 @@ nsresult nsJSUtils::CompileModule(JSCont
   MOZ_ASSERT(JS_IsGlobalObject(aEvaluationGlobal));
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx) == aEvaluationGlobal);
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(CycleCollectedJSContext::Get() &&
              CycleCollectedJSContext::Get()->MicroTaskLevel());
 
   NS_ENSURE_TRUE(xpc::Scriptability::Get(aEvaluationGlobal).Allowed(), NS_OK);
 
-  if (!JS::CompileModule(aCx, aCompileOptions, aSrcBuf, aModule)) {
+  JSObject* module = JS::CompileModule(aCx, aCompileOptions, aSrcBuf);
+  if (!module) {
     return NS_ERROR_FAILURE;
   }
 
+  aModule.set(module);
   return NS_OK;
 }
 
 nsresult nsJSUtils::InitModuleSourceElement(JSContext* aCx,
                                             JS::Handle<JSObject*> aModule,
                                             nsIScriptElement* aElement) {
   JS::Rooted<JS::Value> value(aCx);
   nsresult rv = nsContentUtils::WrapNative(aCx, aElement, &value,
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -2870,17 +2870,17 @@ void nsRange::CollectClientRectsAndText(
     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
     iter.Next();
     nsCOMPtr<nsIContent> content = do_QueryInterface(node);
     if (!content) continue;
     if (content->IsText()) {
       if (node == startContainer) {
         int32_t offset = startContainer == endContainer
                              ? static_cast<int32_t>(aEndOffset)
-                             : content->GetText()->GetLength();
+                             : content->AsText()->TextDataLength();
         GetPartialTextRect(aCollector, aTextList, content,
                            static_cast<int32_t>(aStartOffset), offset,
                            aClampToEdge, aFlushLayout);
         continue;
       } else if (node == endContainer) {
         GetPartialTextRect(aCollector, aTextList, content, 0,
                            static_cast<int32_t>(aEndOffset), aClampToEdge,
                            aFlushLayout);
@@ -2984,17 +2984,17 @@ nsresult nsRange::GetUsedFontFaces(nsLay
     if (!frame) {
       continue;
     }
 
     if (content->IsText()) {
       if (node == startContainer) {
         int32_t offset = startContainer == endContainer
                              ? mEnd.Offset()
-                             : content->GetText()->GetLength();
+                             : content->AsText()->TextDataLength();
         nsLayoutUtils::GetFontFacesForText(frame, mStart.Offset(), offset, true,
                                            aResult, fontFaces, aMaxRanges,
                                            aSkipCollapsedWhitespace);
         continue;
       }
       if (node == endContainer) {
         nsLayoutUtils::GetFontFacesForText(frame, 0, mEnd.Offset(), true,
                                            aResult, fontFaces, aMaxRanges,
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -501,63 +501,45 @@ static bool IsMozBR(nsIContent* aContent
 }
 
 static void ConvertToNativeNewlines(nsString& aString) {
 #if defined(XP_WIN)
   aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
 #endif
 }
 
-static void AppendString(nsAString& aString, nsIContent* aContent) {
-  NS_ASSERTION(aContent->IsText(), "aContent is not a text node!");
-  const nsTextFragment* text = aContent->GetText();
-  if (!text) {
-    return;
-  }
-  text->AppendTo(aString);
+static void AppendString(nsAString& aString, Text* aText) {
+  aText->TextFragment().AppendTo(aString);
 }
 
-static void AppendSubString(nsAString& aString, nsIContent* aContent,
-                            uint32_t aXPOffset, uint32_t aXPLength) {
-  NS_ASSERTION(aContent->IsText(), "aContent is not a text node!");
-  const nsTextFragment* text = aContent->GetText();
-  if (!text) {
-    return;
-  }
-  text->AppendTo(aString, int32_t(aXPOffset), int32_t(aXPLength));
+static void AppendSubString(nsAString& aString, Text* aText, uint32_t aXPOffset,
+                            uint32_t aXPLength) {
+  aText->TextFragment().AppendTo(aString, int32_t(aXPOffset),
+                                 int32_t(aXPLength));
 }
 
 #if defined(XP_WIN)
-static uint32_t CountNewlinesInXPLength(nsIContent* aContent,
-                                        uint32_t aXPLength) {
-  NS_ASSERTION(aContent->IsText(), "aContent is not a text node!");
-  const nsTextFragment* text = aContent->GetText();
-  if (!text) {
-    return 0;
-  }
+static uint32_t CountNewlinesInXPLength(Text* aText, uint32_t aXPLength) {
+  const nsTextFragment* text = &aText->TextFragment();
   // For automated tests, we should abort on debug build.
   MOZ_ASSERT(aXPLength == UINT32_MAX || aXPLength <= text->GetLength(),
              "aXPLength is out-of-bounds");
   const uint32_t length = std::min(aXPLength, text->GetLength());
   uint32_t newlines = 0;
   for (uint32_t i = 0; i < length; ++i) {
     if (text->CharAt(i) == '\n') {
       ++newlines;
     }
   }
   return newlines;
 }
 
-static uint32_t CountNewlinesInNativeLength(nsIContent* aContent,
+static uint32_t CountNewlinesInNativeLength(Text* aText,
                                             uint32_t aNativeLength) {
-  NS_ASSERTION(aContent->IsText(), "aContent is not a text node!");
-  const nsTextFragment* text = aContent->GetText();
-  if (!text) {
-    return 0;
-  }
+  const nsTextFragment* text = &aText->TextFragment();
   // For automated tests, we should abort on debug build.
   MOZ_ASSERT(
       (aNativeLength == UINT32_MAX || aNativeLength <= text->GetLength() * 2),
       "aNativeLength is unexpected value");
   const uint32_t xpLength = text->GetLength();
   uint32_t newlines = 0;
   for (uint32_t i = 0, nativeOffset = 0;
        i < xpLength && nativeOffset < aNativeLength; ++i, ++nativeOffset) {
@@ -579,27 +561,28 @@ uint32_t ContentEventHandler::GetNativeT
   MOZ_ASSERT(aEndOffset >= aStartOffset,
              "aEndOffset must be equals or larger than aStartOffset");
   if (NS_WARN_IF(!aContent->IsText())) {
     return 0;
   }
   if (aStartOffset == aEndOffset) {
     return 0;
   }
-  return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aEndOffset) -
-         GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aStartOffset);
+  return GetTextLength(aContent->AsText(), LINE_BREAK_TYPE_NATIVE, aEndOffset) -
+         GetTextLength(aContent->AsText(), LINE_BREAK_TYPE_NATIVE,
+                       aStartOffset);
 }
 
 /* static */
 uint32_t ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
                                                   uint32_t aMaxLength) {
   if (NS_WARN_IF(!aContent->IsText())) {
     return 0;
   }
-  return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
+  return GetTextLength(aContent->AsText(), LINE_BREAK_TYPE_NATIVE, aMaxLength);
 }
 
 /* static */
 uint32_t ContentEventHandler::GetNativeTextLengthBefore(nsIContent* aContent,
                                                         nsINode* aRootNode) {
   if (NS_WARN_IF(aContent->IsText())) {
     return 0;
   }
@@ -625,17 +608,17 @@ uint32_t ContentEventHandler::GetTextLen
   MOZ_ASSERT(aContent->IsText());
 
   uint32_t textLengthDifference =
 #if defined(XP_WIN)
       // On Windows, the length of a native newline ("\r\n") is twice the length
       // of the XP newline ("\n"), so XP length is equal to the length of the
       // native offset plus the number of newlines encountered in the string.
       (aLineBreakType == LINE_BREAK_TYPE_NATIVE)
-          ? CountNewlinesInXPLength(aContent, aMaxLength)
+          ? CountNewlinesInXPLength(aContent->AsText(), aMaxLength)
           : 0;
 #else
       // On other platforms, the native and XP newlines are the same.
       0;
 #endif
 
   const nsTextFragment* text = aContent->GetText();
   if (!text) {
@@ -646,17 +629,18 @@ uint32_t ContentEventHandler::GetTextLen
 }
 
 static uint32_t ConvertToXPOffset(nsIContent* aContent,
                                   uint32_t aNativeOffset) {
 #if defined(XP_WIN)
   // On Windows, the length of a native newline ("\r\n") is twice the length of
   // the XP newline ("\n"), so XP offset is equal to the length of the native
   // offset minus the number of newlines encountered in the string.
-  return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset);
+  return aNativeOffset - CountNewlinesInNativeLength(aContent->AsText(),
+                                                     aNativeOffset);
 #else
   // On other platforms, the native and XP newlines are the same.
   return aNativeOffset;
 #endif
 }
 
 /* static */
 bool ContentEventHandler::ShouldBreakLineBefore(nsIContent* aContent,
@@ -726,18 +710,17 @@ nsresult ContentEventHandler::GenerateFl
 
   nsINode* startNode = aRawRange.GetStartContainer();
   nsINode* endNode = aRawRange.GetEndContainer();
   if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
     return NS_ERROR_FAILURE;
   }
 
   if (startNode == endNode && startNode->IsText()) {
-    nsIContent* content = startNode->AsContent();
-    AppendSubString(aString, content, aRawRange.StartOffset(),
+    AppendSubString(aString, startNode->AsText(), aRawRange.StartOffset(),
                     aRawRange.EndOffset() - aRawRange.StartOffset());
     ConvertToNativeNewlines(aString);
     return NS_OK;
   }
 
   PreContentIterator preOrderIter;
   nsresult rv =
       preOrderIter.Init(aRawRange.Start().AsRaw(), aRawRange.End().AsRaw());
@@ -747,28 +730,27 @@ nsresult ContentEventHandler::GenerateFl
   for (; !preOrderIter.IsDone(); preOrderIter.Next()) {
     nsINode* node = preOrderIter.GetCurrentNode();
     if (NS_WARN_IF(!node)) {
       break;
     }
     if (!node->IsContent()) {
       continue;
     }
-    nsIContent* content = node->AsContent();
 
-    if (content->IsText()) {
-      if (content == startNode) {
-        AppendSubString(aString, content, aRawRange.StartOffset(),
-                        content->TextLength() - aRawRange.StartOffset());
-      } else if (content == endNode) {
-        AppendSubString(aString, content, 0, aRawRange.EndOffset());
+    if (node->IsText()) {
+      if (node == startNode) {
+        AppendSubString(aString, node->AsText(), aRawRange.StartOffset(),
+                        node->AsText()->TextLength() - aRawRange.StartOffset());
+      } else if (node == endNode) {
+        AppendSubString(aString, node->AsText(), 0, aRawRange.EndOffset());
       } else {
-        AppendString(aString, content);
+        AppendString(aString, node->AsText());
       }
-    } else if (ShouldBreakLineBefore(content, mRootContent)) {
+    } else if (ShouldBreakLineBefore(node->AsContent(), mRootContent)) {
       aString.Append(char16_t('\n'));
     }
   }
   if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
     ConvertToNativeNewlines(aString);
   }
   return NS_OK;
 }
@@ -996,17 +978,17 @@ nsresult ContentEventHandler::ExpandToCl
     if (textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame, options) ==
         nsIFrame::FOUND) {
       *aXPOffset = startOffset + newOffsetInFrame;
       return NS_OK;
     }
   }
 
   // If the frame isn't available, we only can check surrogate pair...
-  const nsTextFragment* text = aContent->GetText();
+  const nsTextFragment* text = &aContent->AsText()->TextFragment();
   NS_ENSURE_TRUE(text, NS_ERROR_FAILURE);
   if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) &&
       NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1))) {
     *aXPOffset += aForward ? 1 : -1;
   }
   return NS_OK;
 }
 
@@ -1859,17 +1841,17 @@ nsresult ContentEventHandler::OnQueryTex
     if (firstFrame->IsTextFrame()) {
       rv = firstFrame->GetCharacterRectsInRange(firstFrame.mOffsetInNode,
                                                 kEndOffset - offset, charRects);
       if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) {
         return rv;
       }
       // Assign the characters whose rects are computed by the call of
       // nsTextFrame::GetCharacterRectsInRange().
-      AppendSubString(chars, firstContent, firstFrame.mOffsetInNode,
+      AppendSubString(chars, firstContent->AsText(), firstFrame.mOffsetInNode,
                       charRects.Length());
       if (NS_WARN_IF(chars.Length() != charRects.Length())) {
         return NS_ERROR_UNEXPECTED;
       }
       if (kBRLength > 1 && chars[0] == '\n' &&
           offset == aEvent->mInput.mOffset && offset) {
         // If start of range starting from previous offset of query range is
         // same as the start of query range, the query range starts from
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -11,17 +11,17 @@
 #include "LoadedScript.h"
 #include "ModuleLoadRequest.h"
 
 #include "prsystem.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/CompilationAndEvaluation.h"
 #include "js/MemoryFunctions.h"
-#include "js/Modules.h"  // JS::CompileModule, JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
+#include "js/Modules.h"  // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
 #include "js/OffThreadScriptCompilation.h"
 #include "js/Realm.h"
 #include "js/SourceText.h"
 #include "js/Utility.h"
 #include "xpcpublic.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIContent.h"
 #include "nsJSUtils.h"
--- a/dom/svg/SVGTextContentElement.cpp
+++ b/dom/svg/SVGTextContentElement.cpp
@@ -78,17 +78,17 @@ Maybe<int32_t> SVGTextContentElement::Ge
 
   uint32_t num = 0;
 
   for (nsINode* n = Element::GetFirstChild(); n; n = n->GetNextSibling()) {
     if (!n->IsText()) {
       return Nothing();
     }
 
-    const nsTextFragment* text = static_cast<nsTextNode*>(n)->GetText();
+    const nsTextFragment* text = &n->AsText()->TextFragment();
     uint32_t length = text->GetLength();
 
     if (text->Is2b()) {
       if (FragmentHasSkippableCharacter(text->Get2b(), length)) {
         return Nothing();
       }
     } else {
       auto buffer = reinterpret_cast<const uint8_t*>(text->Get1b());
--- a/dom/webidl/WindowOrWorkerGlobalScope.webidl
+++ b/dom/webidl/WindowOrWorkerGlobalScope.webidl
@@ -30,16 +30,19 @@ interface WindowOrWorkerGlobalScope {
   long setTimeout(DOMString handler, optional long timeout = 0, any... unused);
   void clearTimeout(optional long handle = 0);
   [Throws]
   long setInterval(Function handler, optional long timeout = 0, any... arguments);
   [Throws]
   long setInterval(DOMString handler, optional long timeout = 0, any... unused);
   void clearInterval(optional long handle = 0);
 
+  // microtask queuing
+  void queueMicrotask(VoidFunction callback);
+
   // ImageBitmap
   [Throws]
   Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage);
   [Throws]
   Promise<ImageBitmap> createImageBitmap(ImageBitmapSource aImage, long aSx, long aSy, long aSw, long aSh);
 };
 
 // https://fetch.spec.whatwg.org/#fetch-method
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -2424,17 +2424,17 @@ nsresult HTMLEditRules::WillDeleteSelect
       if (aAction == nsIEditor::ePrevious) {
         if (!so) {
           return NS_ERROR_UNEXPECTED;
         }
         so--;
         eo--;
         // Bug 1068979: delete both codepoints if surrogate pair
         if (so > 0) {
-          const nsTextFragment* text = nodeAsText->GetText();
+          const nsTextFragment* text = &nodeAsText->TextFragment();
           if (NS_IS_LOW_SURROGATE(text->CharAt(so)) &&
               NS_IS_HIGH_SURROGATE(text->CharAt(so - 1))) {
             so--;
           }
         }
       } else {
         RefPtr<nsRange> range = SelectionRefPtr()->GetRangeAt(0);
         if (NS_WARN_IF(!range)) {
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -582,17 +582,17 @@ nsresult TextEditor::ExtendSelectionForD
         }
 
         // node might be anonymous DIV, so we find better text node
         EditorRawDOMPoint insertionPoint =
             FindBetterInsertionPoint(atStartOfSelection);
 
         if (insertionPoint.IsInTextNode()) {
           const nsTextFragment* data =
-              insertionPoint.GetContainerAsText()->GetText();
+              &insertionPoint.GetContainerAsText()->TextFragment();
           uint32_t offset = insertionPoint.Offset();
           if ((offset > 1 && NS_IS_LOW_SURROGATE(data->CharAt(offset - 1)) &&
                NS_IS_HIGH_SURROGATE(data->CharAt(offset - 2))) ||
               (offset > 0 &&
                gfxFontUtils::IsVarSelector(data->CharAt(offset - 1)))) {
             nsresult rv = selCont->CharacterExtendForBackspace();
             if (NS_WARN_IF(NS_FAILED(rv))) {
               return rv;
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -652,17 +652,17 @@ nsresult WSRunObject::GetWSNodes() {
   // collect up an array of nodes that are contiguous with the insertion point
   // and which contain only whitespace.  Stop if you reach non-ws text or a new
   // block boundary.
   EditorDOMPoint start(mScanStartPoint), end(mScanEndPoint);
   nsCOMPtr<nsINode> wsBoundingParent = GetWSBoundingParent();
 
   // first look backwards to find preceding ws nodes
   if (Text* textNode = mScanStartPoint.GetContainerAsText()) {
-    const nsTextFragment* textFrag = textNode->GetText();
+    const nsTextFragment* textFrag = &textNode->TextFragment();
     mNodeArray.InsertElementAt(0, textNode);
     if (!mScanStartPoint.IsStartOfContainer()) {
       for (uint32_t i = mScanStartPoint.Offset(); i; i--) {
         // sanity bounds check the char position.  bug 136165
         if (i > textFrag->GetLength()) {
           MOZ_ASSERT_UNREACHABLE("looking beyond end of text fragment");
           continue;
         }
@@ -696,20 +696,20 @@ nsresult WSRunObject::GetWSNodes() {
       if (IsBlockNode(priorNode)) {
         mStartNode = start.GetContainer();
         mStartOffset = start.Offset();
         mStartReason = WSType::otherBlock;
         mStartReasonNode = priorNode;
       } else if (priorNode->IsText() && priorNode->IsEditable()) {
         RefPtr<Text> textNode = priorNode->GetAsText();
         mNodeArray.InsertElementAt(0, textNode);
-        const nsTextFragment* textFrag;
-        if (!textNode || !(textFrag = textNode->GetText())) {
+        if (!textNode) {
           return NS_ERROR_NULL_POINTER;
         }
+        const nsTextFragment* textFrag = &textNode->TextFragment();
         uint32_t len = textNode->TextLength();
 
         if (len < 1) {
           // Zero length text node. Set start point to it
           // so we can get past it!
           start.Set(priorNode, 0);
         } else {
           for (int32_t pos = len - 1; pos >= 0; pos--) {
@@ -758,17 +758,17 @@ nsresult WSRunObject::GetWSNodes() {
       mStartReason = WSType::thisBlock;
       mStartReasonNode = wsBoundingParent;
     }
   }
 
   // then look ahead to find following ws nodes
   if (Text* textNode = mScanEndPoint.GetContainerAsText()) {
     // don't need to put it on list. it already is from code above
-    const nsTextFragment* textFrag = textNode->GetText();
+    const nsTextFragment* textFrag = &textNode->TextFragment();
     if (!mScanEndPoint.IsEndOfContainer()) {
       for (uint32_t i = mScanEndPoint.Offset(); i < textNode->TextLength();
            i++) {
         // sanity bounds check the char position.  bug 136165
         if (i >= textFrag->GetLength()) {
           MOZ_ASSERT_UNREACHABLE("looking beyond end of text fragment");
           continue;
         }
@@ -803,20 +803,20 @@ nsresult WSRunObject::GetWSNodes() {
         // we encountered a new block.  therefore no more ws.
         mEndNode = end.GetContainer();
         mEndOffset = end.Offset();
         mEndReason = WSType::otherBlock;
         mEndReasonNode = nextNode;
       } else if (nextNode->IsText() && nextNode->IsEditable()) {
         RefPtr<Text> textNode = nextNode->GetAsText();
         mNodeArray.AppendElement(textNode);
-        const nsTextFragment* textFrag;
-        if (!textNode || !(textFrag = textNode->GetText())) {
+        if (!textNode) {
           return NS_ERROR_NULL_POINTER;
         }
+        const nsTextFragment* textFrag = &textNode->TextFragment();
         uint32_t len = textNode->TextLength();
 
         if (len < 1) {
           // Zero length text node. Set end point to it
           // so we can get past it!
           end.Set(textNode, 0);
         } else {
           for (uint32_t pos = 0; pos < len; pos++) {
@@ -1505,17 +1505,17 @@ nsresult WSRunObject::InsertNBSPAndRemov
       aPoint.mOffset, true);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Now, the text node may have been modified by mutation observer.
   // So, the NBSP may have gone.
   if (aPoint.mTextNode->TextDataLength() <= aPoint.mOffset ||
-      aPoint.mTextNode->GetText()->CharAt(aPoint.mOffset) != kNBSP) {
+      aPoint.mTextNode->TextFragment().CharAt(aPoint.mOffset) != kNBSP) {
     // This is just preparation of an edit action.  Let's return NS_OK.
     // XXX Perhaps, we should return another success code which indicates
     //     mutation observer touched the DOM tree.  However, that should
     //     be returned from each transaction's DoTransaction.
     return NS_OK;
   }
 
   // Next, find range of whitespaces it will be replaced.
@@ -1635,21 +1635,21 @@ WSRunObject::WSFragment* WSRunObject::Fi
 
   return nullptr;
 }
 
 char16_t WSRunObject::GetCharAt(Text* aTextNode, int32_t aOffset) const {
   // return 0 if we can't get a char, for whatever reason
   NS_ENSURE_TRUE(aTextNode, 0);
 
-  int32_t len = int32_t(aTextNode->TextLength());
+  int32_t len = int32_t(aTextNode->TextDataLength());
   if (aOffset < 0 || aOffset >= len) {
     return 0;
   }
-  return aTextNode->GetText()->CharAt(aOffset);
+  return aTextNode->TextFragment().CharAt(aOffset);
 }
 
 template <typename PT, typename CT>
 WSRunObject::WSPoint WSRunObject::GetNextCharPointInternal(
     const EditorDOMPointBase<PT, CT>& aPoint) const {
   // Note: only to be called if aPoint.GetContainer() is not a ws node.
 
   // Binary search on wsnodes
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1816,18 +1816,18 @@ nsEventStatus AsyncPanZoomController::On
         }
       }
     }
   }
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::HandleEndOfPan() {
-  MOZ_ASSERT(GetCurrentTouchBlock());
-  GetCurrentTouchBlock()->GetOverscrollHandoffChain()->FlushRepaints();
+  MOZ_ASSERT(GetCurrentTouchBlock() || GetCurrentPanGestureBlock());
+  GetCurrentInputBlock()->GetOverscrollHandoffChain()->FlushRepaints();
   ParentLayerPoint flingVelocity = GetVelocityVector();
 
   // Clear our velocities; if DispatchFling() gives the fling to us,
   // the fling velocity gets *added* to our existing velocity in
   // AcceptFling().
   mX.SetVelocity(0);
   mY.SetVelocity(0);
   // Clear our state so that we don't stay in the PANNING state
@@ -1839,29 +1839,29 @@ nsEventStatus AsyncPanZoomController::Ha
 
   APZC_LOG("%p starting a fling animation if %f >= %f\n", this,
            flingVelocity.Length().value,
            gfxPrefs::APZFlingMinVelocityThreshold());
 
   if (flingVelocity.Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
     // Relieve overscroll now if needed, since we will not transition to a fling
     // animation and then an overscroll animation, and relieve it then.
-    GetCurrentTouchBlock()
+    GetCurrentInputBlock()
         ->GetOverscrollHandoffChain()
         ->SnapBackOverscrolledApzc(this);
     return nsEventStatus_eConsumeNoDefault;
   }
 
   // Make a local copy of the tree manager pointer and check that it's not
   // null before calling DispatchFling(). This is necessary because Destroy(),
   // which nulls out mTreeManager, could be called concurrently.
   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
     const FlingHandoffState handoffState{
-        flingVelocity, GetCurrentTouchBlock()->GetOverscrollHandoffChain(),
-        false /* not handoff */, GetCurrentTouchBlock()->GetScrolledApzc()};
+        flingVelocity, GetCurrentInputBlock()->GetOverscrollHandoffChain(),
+        false /* not handoff */, GetCurrentInputBlock()->GetScrolledApzc()};
     treeManagerLocal->DispatchFling(this, handoffState);
   }
   return nsEventStatus_eConsumeNoDefault;
 }
 
 bool AsyncPanZoomController::ConvertToGecko(const ScreenIntPoint& aPoint,
                                             LayoutDevicePoint* aOut) {
   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
@@ -2550,35 +2550,64 @@ nsEventStatus AsyncPanZoomController::On
   // Note that there is a multiplier that applies onto the "physical" pan
   // displacement (how much the user's fingers moved) that produces the
   // "logical" pan displacement (how much the page should move). For some of the
   // code below it makes more sense to use the physical displacement rather than
   // the logical displacement, and vice-versa.
   ScreenPoint physicalPanDisplacement = aEvent.mPanDisplacement;
   ParentLayerPoint logicalPanDisplacement =
       aEvent.UserMultipliedLocalPanDisplacement();
+  if (aEvent.mDeltaType == PanGestureInput::PANDELTA_PAGE) {
+    // Pan events with page units are used by Gtk, so this replicates Gtk:
+    // https://gitlab.gnome.org/GNOME/gtk/blob/c734c7e9188b56f56c3a504abee05fa40c5475ac/gtk/gtkrange.c#L3065-3073
+    CSSSize pageScrollSize;
+    CSSToParentLayerScale2D zoom;
+    {
+      // Grab the lock to access the frame metrics.
+      RecursiveMutexAutoLock lock(mRecursiveMutex);
+      pageScrollSize = mScrollMetadata.GetPageScrollAmount() /
+                       Metrics().GetDevPixelsPerCSSPixel();
+      zoom = Metrics().GetZoom();
+    }
+    // scrollUnit* is in units of "ParentLayer pixels per page proportion"...
+    auto scrollUnitWidth = std::min(std::pow(pageScrollSize.width, 2.0 / 3.0),
+                                    pageScrollSize.width / 2.0) *
+                           zoom.xScale;
+    auto scrollUnitHeight = std::min(std::pow(pageScrollSize.height, 2.0 / 3.0),
+                                     pageScrollSize.height / 2.0) *
+                            zoom.yScale;
+    // ... and pan displacements are in units of "page proportion count"
+    // here, so the products of them and scrollUnit* are in ParentLayer pixels
+    ParentLayerPoint physicalPanDisplacementPL(
+        physicalPanDisplacement.x * scrollUnitWidth,
+        physicalPanDisplacement.y * scrollUnitHeight);
+    physicalPanDisplacement = ToScreenCoordinates(physicalPanDisplacementPL,
+                                                  aEvent.mLocalPanStartPoint);
+    logicalPanDisplacement.x *= scrollUnitWidth;
+    logicalPanDisplacement.y *= scrollUnitHeight;
+  }
 
   MOZ_ASSERT(GetCurrentPanGestureBlock());
   AdjustDeltaForAllowedScrollDirections(
       logicalPanDisplacement,
       GetCurrentPanGestureBlock()->GetAllowedScrollDirections());
 
   // We need to update the axis velocity in order to get a useful display port
   // size and position. We need to do so even if this is a momentum pan (i.e.
   // aFingersOnTouchpad == false); in that case the "with touch" part is not
   // really appropriate, so we may want to rethink this at some point.
   // Note that we have to make all simulated positions relative to
   // Axis::GetPos(), because the current position is an invented position, and
   // because resetting the position to the mouse position (e.g.
   // aEvent.mLocalStartPoint) would mess up velocity calculation. (This is
   // the only caller of UpdateWithTouchAtDevicePoint() for pan events, so
   // there is no risk of other calls resetting the position.)
-  mX.UpdateWithTouchAtDevicePoint(mX.GetPos() + logicalPanDisplacement.x,
+  mX.UpdateWithTouchAtDevicePoint(mX.GetPos() - logicalPanDisplacement.x,
                                   aEvent.mTime);
-  mY.UpdateWithTouchAtDevicePoint(mY.GetPos() + logicalPanDisplacement.y,
+  mY.UpdateWithTouchAtDevicePoint(mY.GetPos() - logicalPanDisplacement.y,
                                   aEvent.mTime);
 
   HandlePanningUpdate(physicalPanDisplacement);
 
   ScreenPoint panDistance(fabs(physicalPanDisplacement.x),
                           fabs(physicalPanDisplacement.y));
   OverscrollHandoffState handoffState(
       *GetCurrentPanGestureBlock()->GetOverscrollHandoffChain(), panDistance,
@@ -2603,16 +2632,22 @@ nsEventStatus AsyncPanZoomController::On
   APZC_LOG("%p got a pan-end in state %d\n", this, mState);
 
   // Call into OnPan in order to process any delta included in this event.
   OnPan(aEvent, true);
 
   mX.EndTouch(aEvent.mTime);
   mY.EndTouch(aEvent.mTime);
 
+  // Use HandleEndOfPan for fling on platforms that don't
+  // emit momentum events (Gtk).
+  if (aEvent.mSimulateMomentum) {
+    return HandleEndOfPan();
+  }
+
   // Drop any velocity on axes where we don't have room to scroll anyways
   // (in this APZC, or an APZC further in the handoff chain).
   // This ensures that we don't enlarge the display port unnecessarily.
   MOZ_ASSERT(GetCurrentPanGestureBlock());
   RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
       GetCurrentPanGestureBlock()->GetOverscrollHandoffChain();
   if (!overscrollHandoffChain->CanScrollInDirection(
           this, ScrollDirection::eHorizontal)) {
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -155,35 +155,16 @@ struct ParamTraits<mozilla::layers::Comp
     WriteParam(msg, param.mHandle);
   }
   static bool Read(const Message* msg, PickleIterator* iter,
                    paramType* result) {
     return ReadParam(msg, iter, &result->mHandle);
   }
 };
 
-// Helper class for reading bitfields.
-// If T has bitfields members, derive ParamTraits<T> from BitfieldHelper<T>.
-template <typename ParamType>
-struct BitfieldHelper {
-  // We need this helper because we can't get the address of a bitfield to
-  // pass directly to ReadParam. So instead we read it into a temporary bool
-  // and set the bitfield using a setter function
-  static bool ReadBoolForBitfield(const Message* aMsg, PickleIterator* aIter,
-                                  ParamType* aResult,
-                                  void (ParamType::*aSetter)(bool)) {
-    bool value;
-    if (ReadParam(aMsg, aIter, &value)) {
-      (aResult->*aSetter)(value);
-      return true;
-    }
-    return false;
-  }
-};
-
 template <>
 struct ParamTraits<mozilla::layers::FrameMetrics>
     : BitfieldHelper<mozilla::layers::FrameMetrics> {
   typedef mozilla::layers::FrameMetrics paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     WriteParam(aMsg, aParam.mScrollId);
     WriteParam(aMsg, aParam.mPresShellResolution);
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -405,17 +405,17 @@ NS_IMPL_ISUPPORTS(SRGBOverrideObserver, 
 
 #define GFX_PREF_OPENTYPE_SVG "gfx.font_rendering.opentype_svg.enabled"
 
 #define GFX_PREF_WORD_CACHE_CHARLIMIT "gfx.font_rendering.wordcache.charlimit"
 #define GFX_PREF_WORD_CACHE_MAXENTRIES "gfx.font_rendering.wordcache.maxentries"
 
 #define GFX_PREF_GRAPHITE_SHAPING "gfx.font_rendering.graphite.enabled"
 #if defined(XP_MACOSX)
-#define GFX_PREF_CORETEXT_SHAPING "gfx.font_rendering.coretext.enabled"
+#  define GFX_PREF_CORETEXT_SHAPING "gfx.font_rendering.coretext.enabled"
 #endif
 
 #define BIDI_NUMERAL_PREF "bidi.numeral"
 
 #define GFX_PREF_CMS_FORCE_SRGB "gfx.color_management.force_srgb"
 
 #define FONT_VARIATIONS_PREF "layout.css.font-variations.enabled"
 
@@ -2565,25 +2565,25 @@ static FeatureState& WebRenderHardwareQu
       nsresult valid;
       int32_t deviceID = adapterDeviceID.ToInteger(&valid, 16);
       if (valid != NS_OK) {
         featureWebRenderQualified.Disable(
             FeatureStatus::BlockedDeviceUnknown, "Bad device id",
             NS_LITERAL_CSTRING("FEATURE_FAILURE_BAD_DEVICE_ID"));
       } else {
 #ifdef NIGHTLY_BUILD
-        // For Intel devices, if we have a battery, ignore it if the screen is
-        // small enough. Note that we always check for a battery with NVIDIA
+        // For AMD/Intel devices, if we have a battery, ignore it if the screen
+        // is small enough. Note that we always check for a battery with NVIDIA
         // because we do not have a limited/curated set of devices to support
         // WebRender on.
         const int32_t kMaxPixelsBattery = 1920 * 1200;  // WUXGA
         const int32_t screenPixels = aScreenSize.width * aScreenSize.height;
         bool disableForBattery = aHasBattery;
-        if (adapterVendorID == u"0x8086" && screenPixels > 0 &&
-            screenPixels <= kMaxPixelsBattery) {
+        if ((adapterVendorID == u"0x8086" || adapterVendorID == u"0x1002") &&
+            screenPixels > 0 && screenPixels <= kMaxPixelsBattery) {
           disableForBattery = false;
         }
 #else
         bool disableForBattery = aHasBattery;
 #endif
 
         if (disableForBattery) {
           featureWebRenderQualified.Disable(
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -1083,11 +1083,30 @@ struct CrossOriginPolicyValidator {
   }
 };
 
 template <>
 struct ParamTraits<nsILoadInfo::CrossOriginPolicy>
     : EnumSerializer<nsILoadInfo::CrossOriginPolicy,
                      CrossOriginPolicyValidator> {};
 
+// Helper class for reading bitfields.
+// If T has bitfields members, derive ParamTraits<T> from BitfieldHelper<T>.
+template <typename ParamType>
+struct BitfieldHelper {
+  // We need this helper because we can't get the address of a bitfield to
+  // pass directly to ReadParam. So instead we read it into a temporary bool
+  // and set the bitfield using a setter function
+  static bool ReadBoolForBitfield(const Message* aMsg, PickleIterator* aIter,
+                                  ParamType* aResult,
+                                  void (ParamType::*aSetter)(bool)) {
+    bool value;
+    if (ReadParam(aMsg, aIter, &value)) {
+      (aResult->*aSetter)(value);
+      return true;
+    }
+    return false;
+  }
+};
+
 } /* namespace IPC */
 
 #endif /* __IPC_GLUE_IPCMESSAGEUTILS_H__ */
--- a/js/public/AllocPolicy.h
+++ b/js/public/AllocPolicy.h
@@ -30,29 +30,31 @@ class AllocPolicyBase {
   T* maybe_pod_arena_malloc(arena_id_t arenaId, size_t numElems) {
     return js_pod_arena_malloc<T>(arenaId, numElems);
   }
   template <typename T>
   T* maybe_pod_arena_calloc(arena_id_t arenaId, size_t numElems) {
     return js_pod_arena_calloc<T>(arenaId, numElems);
   }
   template <typename T>
-  T* maybe_pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize, size_t newSize) {
+  T* maybe_pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize,
+                             size_t newSize) {
     return js_pod_arena_realloc<T>(arenaId, p, oldSize, newSize);
   }
   template <typename T>
   T* pod_arena_malloc(arena_id_t arenaId, size_t numElems) {
     return maybe_pod_arena_malloc<T>(arenaId, numElems);
   }
   template <typename T>
   T* pod_arena_calloc(arena_id_t arenaId, size_t numElems) {
     return maybe_pod_arena_calloc<T>(arenaId, numElems);
   }
   template <typename T>
-  T* pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize, size_t newSize) {
+  T* pod_arena_realloc(arena_id_t arenaId, T* p, size_t oldSize,
+                       size_t newSize) {
     return maybe_pod_arena_realloc<T>(arenaId, p, oldSize, newSize);
   }
 
   template <typename T>
   T* maybe_pod_malloc(size_t numElems) {
     return maybe_pod_arena_malloc<T>(js::MallocArena, numElems);
   }
   template <typename T>
@@ -138,17 +140,18 @@ class TempAllocPolicy : public AllocPoli
     T* p = this->maybe_pod_arena_calloc<T>(arenaId, numElems);
     if (MOZ_UNLIKELY(!p)) {
       p = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Calloc, numElems);
     }
     return p;
   }
 
   template <typename T>
-  T* pod_arena_realloc(arena_id_t arenaId, T* prior, size_t oldSize, size_t newSize) {
+  T* pod_arena_realloc(arena_id_t arenaId, T* prior, size_t oldSize,
+                       size_t newSize) {
     T* p2 = this->maybe_pod_arena_realloc<T>(arenaId, prior, oldSize, newSize);
     if (MOZ_UNLIKELY(!p2)) {
       p2 = onOutOfMemoryTyped<T>(arenaId, AllocFunction::Realloc, newSize,
                                  prior);
     }
     return p2;
   }
 
--- a/js/public/MemoryFunctions.h
+++ b/js/public/MemoryFunctions.h
@@ -11,16 +11,17 @@
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
 #include "mozilla/Attributes.h"  // MOZ_MUST_USE
 
 #include <stddef.h>  // size_t
 
 #include "jstypes.h"  // JS_PUBLIC_API
 
 struct JSContext;
+class JSObject;
 struct JSRuntime;
 
 struct JSFreeOp {
  protected:
   JSRuntime* runtime_;
 
   explicit JSFreeOp(JSRuntime* rt) : runtime_(rt) {}
 
--- a/js/public/Modules.h
+++ b/js/public/Modules.h
@@ -4,16 +4,18 @@
  * 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/. */
 
 /* JavaScript module (as in, the syntactic construct) operations. */
 
 #ifndef js_Modules_h
 #define js_Modules_h
 
+#include "mozilla/Utf8.h"  // mozilla::Utf8Unit
+
 #include <stdint.h>  // uint32_t
 
 #include "jstypes.h"  // JS_PUBLIC_API
 
 #include "js/CompileOptions.h"  // JS::ReadOnlyCompileOptions
 #include "js/RootingAPI.h"      // JS::{Mutable,}Handle
 #include "js/Value.h"           // JS::Value
 
@@ -82,20 +84,27 @@ extern JS_PUBLIC_API void SetModuleDynam
 extern JS_PUBLIC_API bool FinishDynamicModuleImport(
     JSContext* cx, Handle<Value> referencingPrivate,
     Handle<JSString*> specifier, Handle<JSObject*> promise);
 
 /**
  * Parse the given source buffer as a module in the scope of the current global
  * of cx and return a source text module record.
  */
-extern JS_PUBLIC_API bool CompileModule(JSContext* cx,
-                                        const ReadOnlyCompileOptions& options,
-                                        SourceText<char16_t>& srcBuf,
-                                        MutableHandle<JSObject*> moduleRecord);
+extern JS_PUBLIC_API JSObject* CompileModule(
+    JSContext* cx, const ReadOnlyCompileOptions& options,
+    SourceText<char16_t>& srcBuf);
+
+/**
+ * Parse the given source buffer as a module in the scope of the current global
+ * of cx and return a source text module record.
+ */
+extern JS_PUBLIC_API JSObject* CompileModule(
+    JSContext* cx, const ReadOnlyCompileOptions& options,
+    SourceText<mozilla::Utf8Unit>& srcBuf);
 
 /**
  * Set a private value associated with a source text module record.
  */
 extern JS_PUBLIC_API void SetModulePrivate(JSObject* module,
                                            const Value& value);
 
 /**
--- a/js/public/OffThreadScriptCompilation.h
+++ b/js/public/OffThreadScriptCompilation.h
@@ -7,16 +7,17 @@
  * Types and functions related to the compilation of JavaScript off the
  * direct JSAPI-using thread.
  */
 
 #ifndef js_OffThreadScriptCompilation_h
 #define js_OffThreadScriptCompilation_h
 
 #include "mozilla/Range.h"   // mozilla::Range
+#include "mozilla/Utf8.h"    // mozilla::Utf8Unit
 #include "mozilla/Vector.h"  // mozilla::Vector
 
 #include <stddef.h>  // size_t
 
 #include "jstypes.h"  // JS_PUBLIC_API
 
 #include "js/CompileOptions.h"  // JS::ReadOnlyCompileOptions
 #include "js/GCVector.h"        // JS::GCVector
@@ -62,27 +63,45 @@ extern JS_PUBLIC_API bool CanDecodeOffTh
  * to FinishOffThreadScript.
  */
 
 extern JS_PUBLIC_API bool CompileOffThread(
     JSContext* cx, const ReadOnlyCompileOptions& options,
     SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
     void* callbackData);
 
+// NOTE: Unlike for the normal sync compilation functions, this function NEVER
+//       INFLATES TO UTF-16.  Therefore, it is ALWAYS invoking experimental
+//       UTF-8 support.  Inflate to UTF-16 yourself and use the other overload
+//       if you're unable to take a risk using unstable functionality.
+extern JS_PUBLIC_API bool CompileOffThread(
+    JSContext* cx, const ReadOnlyCompileOptions& options,
+    SourceText<mozilla::Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
+    void* callbackData);
+
 extern JS_PUBLIC_API JSScript* FinishOffThreadScript(JSContext* cx,
                                                      OffThreadToken* token);
 
 extern JS_PUBLIC_API void CancelOffThreadScript(JSContext* cx,
                                                 OffThreadToken* token);
 
 extern JS_PUBLIC_API bool CompileOffThreadModule(
     JSContext* cx, const ReadOnlyCompileOptions& options,
     SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
     void* callbackData);
 
+// NOTE: Unlike for the normal sync compilation functions, this function NEVER
+//       INFLATES TO UTF-16.  Therefore, it is ALWAYS invoking experimental
+//       UTF-8 support.  Inflate to UTF-16 yourself and use the other overload
+//       if you're unable to take a risk using unstable functionality.
+extern JS_PUBLIC_API bool CompileOffThreadModule(
+    JSContext* cx, const ReadOnlyCompileOptions& options,
+    SourceText<mozilla::Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
+    void* callbackData);
+
 extern JS_PUBLIC_API JSObject* FinishOffThreadModule(JSContext* cx,
                                                      OffThreadToken* token);
 
 extern JS_PUBLIC_API void CancelOffThreadModule(JSContext* cx,
                                                 OffThreadToken* token);
 
 extern JS_PUBLIC_API bool DecodeOffThreadScript(
     JSContext* cx, const ReadOnlyCompileOptions& options,
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -1025,22 +1025,23 @@ class MOZ_RAII Rooted : public js::Roote
 
   // Dummy type to distinguish these constructors from Rooted(cx, initial)
   struct CtorDispatcher {};
 
   // Normal case: construct an empty Rooted holding a safely initialized but
   // empty T.
   template <typename RootingContext>
   Rooted(const RootingContext& cx, CtorDispatcher, detail::FallbackOverload)
-    : Rooted(cx, SafelyInitialized<T>()) {}
+      : Rooted(cx, SafelyInitialized<T>()) {}
 
   // If T can be constructed with a cx, then define another constructor for it
   // that will be preferred.
   template <typename RootingContext,
-            typename = typename std::enable_if<std::is_constructible<T, RootingContext>::value>::type>
+            typename = typename std::enable_if<
+                std::is_constructible<T, RootingContext>::value>::type>
   Rooted(const RootingContext& cx, CtorDispatcher, detail::PreferredOverload)
       : Rooted(cx, T(cx)) {}
 
  public:
   using ElementType = T;
 
   // Construct an empty Rooted. Delegates to an internal constructor that
   // chooses a specific meaning of "empty" depending on whether T can be
--- a/js/public/experimental/SourceHook.h
+++ b/js/public/experimental/SourceHook.h
@@ -89,11 +89,11 @@ class SourceHook {
  */
 extern JS_FRIEND_API void SetSourceHook(JSContext* cx,
                                         mozilla::UniquePtr<SourceHook> hook);
 
 /** Remove |cx|'s source hook, and return it. The caller now owns the hook. */
 extern JS_FRIEND_API mozilla::UniquePtr<SourceHook> ForgetSourceHook(
     JSContext* cx);
 
-} // namespace js
+}  // namespace js
 
-#endif // js_experimental_SourceHook_h
+#endif  // js_experimental_SourceHook_h
--- a/js/rust/src/jsglue.cpp
+++ b/js/rust/src/jsglue.cpp
@@ -549,35 +549,33 @@ bool AppendToRootedIdVector(JS::Persiste
 const jsid* SliceRootedIdVector(const JS::PersistentRootedIdVector* v,
                                 size_t* length) {
   *length = v->length();
   return v->begin();
 }
 
 void DestroyRootedIdVector(JS::PersistentRootedIdVector* v) { delete v; }
 
-JS::MutableHandleIdVector GetMutableHandleIdVector(JS::PersistentRootedIdVector* v) {
+JS::MutableHandleIdVector GetMutableHandleIdVector(
+    JS::PersistentRootedIdVector* v) {
   return JS::MutableHandleIdVector(v);
 }
 
-JS::PersistentRootedObjectVector* CreateRootedObjectVector(
-    JSContext* aCx) {
+JS::PersistentRootedObjectVector* CreateRootedObjectVector(JSContext* aCx) {
   JS::PersistentRootedObjectVector* vec =
       new JS::PersistentRootedObjectVector(aCx);
   return vec;
 }
 
 bool AppendToRootedObjectVector(JS::PersistentRootedObjectVector* v,
-                                          JSObject* obj) {
+                                JSObject* obj) {
   return v->append(obj);
 }
 
-void DeleteRootedObjectVector(JS::PersistentRootedObjectVector* v) {
-  delete v;
-}
+void DeleteRootedObjectVector(JS::PersistentRootedObjectVector* v) { delete v; }
 
 #if defined(__linux__)
 #  include <malloc.h>
 #elif defined(__APPLE__)
 #  include <malloc/malloc.h>
 #elif defined(__MINGW32__) || defined(__MINGW64__)
 // nothing needed here
 #elif defined(_MSC_VER)
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -157,17 +157,17 @@ struct ArrayOps<int64_t> {
     BigInt* bi = ToBigInt(cx, v);
     if (!bi) {
       return cx->alreadyReportedError();
     }
     return BigInt::toInt64(bi);
   }
 
   static JS::Result<int64_t> convertValue(JSContext* cx, HandleValue v,
-                                           MutableHandleValue result) {
+                                          MutableHandleValue result) {
     BigInt* bi = ToBigInt(cx, v);
     if (!bi) {
       return cx->alreadyReportedError();
     }
     result.setBigInt(bi);
     return BigInt::toInt64(bi);
   }
 
@@ -188,17 +188,17 @@ struct ArrayOps<uint64_t> {
     BigInt* bi = ToBigInt(cx, v);
     if (!bi) {
       return cx->alreadyReportedError();
     }
     return BigInt::toUint64(bi);
   }
 
   static JS::Result<uint64_t> convertValue(JSContext* cx, HandleValue v,
-                                            MutableHandleValue result) {
+                                           MutableHandleValue result) {
     BigInt* bi = ToBigInt(cx, v);
     if (!bi) {
       return cx->alreadyReportedError();
     }
     result.setBigInt(bi);
     return BigInt::toUint64(bi);
   }
 
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -1052,19 +1052,18 @@ bool ModuleObject::execute(JSContext* cx
   }
 #endif
 
   RootedScript script(cx, self->script());
 
   // The top-level script if a module is only ever executed once. Clear the
   // reference at exit to prevent us keeping this alive unnecessarily. This is
   // kept while executing so it is available to the debugger.
-  auto guardA = mozilla::MakeScopeExit([&] {
-      self->setReservedSlot(ScriptSlot, UndefinedValue());
-    });
+  auto guardA = mozilla::MakeScopeExit(
+      [&] { self->setReservedSlot(ScriptSlot, UndefinedValue()); });
 
   RootedModuleEnvironmentObject scope(cx, self->environment());
   if (!scope) {
     JS_ReportErrorASCII(cx,
                         "Module declarations have not yet been instantiated");
     return false;
   }
 
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2645,18 +2645,18 @@ bool ASTSerializer::rightAssociate(ListN
 bool ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) {
   if (!CheckRecursionLimit(cx)) {
     return false;
   }
 
   switch (pn->getKind()) {
     case ParseNodeKind::Function: {
       FunctionNode* funNode = &pn->as<FunctionNode>();
-      ASTType type = funNode->funbox()->isArrow() ? AST_ARROW_EXPR
-                                                              : AST_FUNC_EXPR;
+      ASTType type =
+          funNode->funbox()->isArrow() ? AST_ARROW_EXPR : AST_FUNC_EXPR;
       return function(funNode, type, dst);
     }
 
     case ParseNodeKind::CommaExpr: {
       NodeVector exprs(cx);
       return expressions(&pn->as<ListNode>(), exprs) &&
              builder.sequenceExpression(exprs, &pn->pn_pos, dst);
     }
@@ -3139,19 +3139,19 @@ bool ASTSerializer::property(ParseNode* 
     kind = PROP_INIT;
   }
 
   BinaryNode* node = &pn->as<BinaryNode>();
   ParseNode* keyNode = node->left();
   ParseNode* valNode = node->right();
 
   bool isShorthand = node->isKind(ParseNodeKind::Shorthand);
-  bool isMethod = valNode->is<FunctionNode>() &&
-                  valNode->as<FunctionNode>().funbox()->kind() ==
-                      JSFunction::Method;
+  bool isMethod =
+      valNode->is<FunctionNode>() &&
+      valNode->as<FunctionNode>().funbox()->kind() == JSFunction::Method;
   RootedValue key(cx), val(cx);
   return propertyName(keyNode, &key) && expression(valNode, &val) &&
          builder.propertyInitializer(key, val, kind, isShorthand, isMethod,
                                      &node->pn_pos, dst);
 }
 
 bool ASTSerializer::literal(ParseNode* pn, MutableHandleValue dst) {
   RootedValue val(cx);
--- a/js/src/builtin/Stream.cpp
+++ b/js/src/builtin/Stream.cpp
@@ -3479,19 +3479,17 @@ class MOZ_RAII AutoClearUnderlyingSource
 
   ~AutoClearUnderlyingSource() {
     if (controller_) {
       ReadableStreamController::clearUnderlyingSource(
           controller_, /* finalizeSource */ false);
     }
   }
 
-  void reset() {
-    controller_ = nullptr;
-  }
+  void reset() { controller_ = nullptr; }
 };
 
 /**
  * Version of SetUpReadableByteStreamController that's specialized for handling
  * external, embedding-provided, underlying sources.
  */
 static MOZ_MUST_USE bool SetUpExternalReadableByteStreamController(
     JSContext* cx, Handle<ReadableStream*> stream,
--- a/js/src/frontend/BinASTParser.cpp
+++ b/js/src/frontend/BinASTParser.cpp
@@ -3222,18 +3222,17 @@ JS::Result<Ok> BinASTParser<Tok>::parseI
   MOZ_TRY(tokenizer_->checkFields(kind, fields, expected_fields));
 #endif  // defined(DEBUG)
 
   BINJS_MOZ_TRY_DECL(isFunctionNameCaptured,
                      tokenizer_->readBool(fieldContext++));
   // Per spec, isFunctionNameCaptured can be true for anonymous
   // function.  Check isFunctionNameCaptured only for named
   // function.
-  if (pc_->functionBox()->isNamedLambda() &&
-      isFunctionNameCaptured) {
+  if (pc_->functionBox()->isNamedLambda() && isFunctionNameCaptured) {
     captureFunctionName();
   }
   BINJS_MOZ_TRY_DECL(isThisCaptured, tokenizer_->readBool(fieldContext++));
   // TODO: Use this in BinASTParser::buildFunction.
   (void)isThisCaptured;
   Rooted<GCVector<JSAtom*>> positionalParams(cx_, GCVector<JSAtom*>(cx_));
   MOZ_TRY(parseAssertedParameterScope(&positionalParams, fieldContext++));
 
--- a/js/src/frontend/BinASTParserPerTokenizer.cpp
+++ b/js/src/frontend/BinASTParserPerTokenizer.cpp
@@ -270,17 +270,17 @@ JS::Result<FunctionBox*> BinASTParserPer
       cx_, traceListHead_, fun, /* toStringStart = */ 0, *directives,
       /* extraWarning = */ false, generatorKind, functionAsyncKind);
   if (!funbox) {
     return raiseOOM();
   }
 
   traceListHead_ = funbox;
   if (pc_) {
-    funbox->initWithEnclosingParseContext(pc_, fun,syntax);
+    funbox->initWithEnclosingParseContext(pc_, fun, syntax);
   } else {
     funbox->initFromLazyFunction(fun);
   }
   return funbox;
 }
 
 FunctionSyntaxKind BinASTKindToFunctionSyntaxKind(const BinASTKind kind) {
   // FIXME: this doesn't cover FunctionSyntaxKind::ClassConstructor and
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "frontend/BytecodeCompiler.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Maybe.h"
-#include "mozilla/Utf8.h"
+#include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 
 #include "builtin/ModuleObject.h"
 #if defined(JS_BUILD_BINAST)
 #  include "frontend/BinASTParser.h"
 #endif  // JS_BUILD_BINAST
 #include "frontend/BytecodeCompilation.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/EitherParser.h"
@@ -799,61 +799,68 @@ JSScript* frontend::CompileGlobalBinASTS
 
   assertException.reset();
   return script;
 }
 
 #endif  // JS_BUILD_BINAST
 
 template <typename Unit>
-static ModuleObject* CreateModule(JSContext* cx,
-                                  const ReadOnlyCompileOptions& optionsInput,
-                                  SourceText<Unit>& srcBuf,
-                                  ScriptSourceObject** sourceObjectOut) {
+static ModuleObject* InternalParseModule(
+    JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
+    SourceText<Unit>& srcBuf, ScriptSourceObject** sourceObjectOut) {
   MOZ_ASSERT(srcBuf.get());
   MOZ_ASSERT_IF(sourceObjectOut, *sourceObjectOut == nullptr);
 
   AutoAssertReportedException assertException(cx);
 
   CompileOptions options(cx, optionsInput);
   options.maybeMakeStrictMode(
       true);  // ES6 10.2.1 Module code is always strict mode code.
   options.setIsRunOnce(true);
   options.allowHTMLComments = false;
 
   ModuleInfo info(cx, options);
   AutoInitializeSourceObject autoSSO(info, sourceObjectOut);
 
-  ModuleCompiler<char16_t> compiler(srcBuf);
+  ModuleCompiler<Unit> compiler(srcBuf);
   ModuleObject* module = compiler.compile(info);
   if (!module) {
     return nullptr;
   }
 
   assertException.reset();
   return module;
 }
 
-ModuleObject* frontend::CompileModule(
-    JSContext* cx, const ReadOnlyCompileOptions& optionsInput,
-    SourceText<char16_t>& srcBuf, ScriptSourceObject** sourceObjectOut) {
-  return CreateModule(cx, optionsInput, srcBuf, sourceObjectOut);
+ModuleObject* frontend::ParseModule(JSContext* cx,
+                                    const ReadOnlyCompileOptions& optionsInput,
+                                    SourceText<char16_t>& srcBuf,
+                                    ScriptSourceObject** sourceObjectOut) {
+  return InternalParseModule(cx, optionsInput, srcBuf, sourceObjectOut);
+}
+
+ModuleObject* frontend::ParseModule(JSContext* cx,
+                                    const ReadOnlyCompileOptions& optionsInput,
+                                    SourceText<Utf8Unit>& srcBuf,
+                                    ScriptSourceObject** sourceObjectOut) {
+  return InternalParseModule(cx, optionsInput, srcBuf, sourceObjectOut);
 }
 
 template <typename Unit>
 static ModuleObject* CreateModule(JSContext* cx,
                                   const JS::ReadOnlyCompileOptions& options,
                                   SourceText<Unit>& srcBuf) {
   AutoAssertReportedException assertException(cx);
 
   if (!GlobalObject::ensureModulePrototypesCreated(cx, cx->global())) {
     return nullptr;
   }
 
-  RootedModuleObject module(cx, CompileModule(cx, options, srcBuf, nullptr));
+  RootedModuleObject module(cx, ParseModule(cx, options, srcBuf, nullptr));
   if (!module) {
     return nullptr;
   }
 
   // This happens in GlobalHelperThreadState::finishModuleParseTask() when a
   // module is compiled off thread.
   if (!ModuleObject::Freeze(cx, module)) {
     return nullptr;
@@ -864,16 +871,22 @@ static ModuleObject* CreateModule(JSCont
 }
 
 ModuleObject* frontend::CompileModule(JSContext* cx,
                                       const JS::ReadOnlyCompileOptions& options,
                                       SourceText<char16_t>& srcBuf) {
   return CreateModule(cx, options, srcBuf);
 }
 
+ModuleObject* frontend::CompileModule(JSContext* cx,
+                                      const JS::ReadOnlyCompileOptions& options,
+                                      SourceText<Utf8Unit>& srcBuf) {
+  return CreateModule(cx, options, srcBuf);
+}
+
 // When leaving this scope, the given function should either:
 //   * be linked to a fully compiled script
 //   * remain linking to a lazy script
 class MOZ_STACK_CLASS AutoAssertFunctionDelazificationCompletion {
 #ifdef DEBUG
   RootedFunction fun_;
 #endif
 
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -3,16 +3,17 @@
  * 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/. */
 
 #ifndef frontend_BytecodeCompiler_h
 #define frontend_BytecodeCompiler_h
 
 #include "mozilla/Maybe.h"
+#include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 
 #include "NamespaceImports.h"
 
 #include "js/CompileOptions.h"
 #include "js/SourceText.h"
 #include "vm/Scope.h"
 #include "vm/TraceLogging.h"
 
@@ -115,24 +116,34 @@ JSScript* CompileGlobalBinASTScript(
     ScriptSourceObject** sourceObjectOut = nullptr);
 
 MOZ_MUST_USE bool CompileLazyBinASTFunction(JSContext* cx,
                                             Handle<LazyScript*> lazy,
                                             const uint8_t* buf, size_t length);
 
 #endif  // JS_BUILD_BINAST
 
+// Compile a module of the given source using the given options.
 ModuleObject* CompileModule(JSContext* cx,
                             const JS::ReadOnlyCompileOptions& options,
                             JS::SourceText<char16_t>& srcBuf);
-
 ModuleObject* CompileModule(JSContext* cx,
                             const JS::ReadOnlyCompileOptions& options,
-                            JS::SourceText<char16_t>& srcBuf,
-                            ScriptSourceObject** sourceObjectOut);
+                            JS::SourceText<mozilla::Utf8Unit>& srcBuf);
+
+// Parse a module of the given source.  This is an internal API; if you want to
+// compile a module as a user, use CompileModule above.
+ModuleObject* ParseModule(JSContext* cx,
+                          const JS::ReadOnlyCompileOptions& options,
+                          JS::SourceText<char16_t>& srcBuf,
+                          ScriptSourceObject** sourceObjectOut);
+ModuleObject* ParseModule(JSContext* cx,
+                          const JS::ReadOnlyCompileOptions& options,
+                          JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+                          ScriptSourceObject** sourceObjectOut);
 
 //
 // Compile a single function. The source in srcBuf must match the ECMA-262
 // FunctionExpression production.
 //
 // If nonzero, parameterListEnd is the offset within srcBuf where the parameter
 // list is expected to end. During parsing, if we find that it ends anywhere
 // else, it's a SyntaxError. This is used to implement the Function constructor;
--- a/js/src/frontend/BytecodeSection.h
+++ b/js/src/frontend/BytecodeSection.h
@@ -99,17 +99,16 @@ struct CGResumeOffsetList {
   Vector<uint32_t> list;
   explicit CGResumeOffsetList(JSContext* cx) : list(cx) {}
 
   MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
   size_t length() const { return list.length(); }
   void finish(mozilla::Span<uint32_t> array);
 };
 
-
 static constexpr size_t MaxBytecodeLength = INT32_MAX;
 static constexpr size_t MaxSrcNotesLength = INT32_MAX;
 
 // Have a few inline elements, so as to avoid heap allocation for tiny
 // sequences.  See bug 1390526.
 typedef Vector<jsbytecode, 64> BytecodeVector;
 typedef Vector<jssrcnote, 64> SrcNotesVector;
 
--- a/js/src/frontend/FunctionEmitter.cpp
+++ b/js/src/frontend/FunctionEmitter.cpp
@@ -470,18 +470,17 @@ bool FunctionScriptEmitter::prepareForBo
   }
 
   if (funbox_->needsPromiseResult()) {
     if (!emitAsyncFunctionRejectPrologue()) {
       return false;
     }
   }
 
-  if (funbox_->kind() ==
-      JSFunction::FunctionKind::ClassConstructor) {
+  if (funbox_->kind() == JSFunction::FunctionKind::ClassConstructor) {
     if (!funbox_->isDerivedClassConstructor()) {
       if (!bce_->emitInitializeInstanceFields()) {
         //          [stack]
         return false;
       }
     }
   }
 
--- a/js/src/frontend/ParseContext.cpp
+++ b/js/src/frontend/ParseContext.cpp
@@ -291,18 +291,17 @@ bool ParseContext::init() {
 
   return true;
 }
 
 bool ParseContext::annexBAppliesToLexicalFunctionInInnermostScope(
     FunctionBox* funbox) {
   MOZ_ASSERT(!sc()->strict());
 
-  RootedPropertyName name(sc()->cx_,
-                          funbox->explicitName()->asPropertyName());
+  RootedPropertyName name(sc()->cx_, funbox->explicitName()->asPropertyName());
   Maybe<DeclarationKind> redeclaredKind = isVarRedeclaredInInnermostScope(
       name, DeclarationKind::VarForAnnexBLexicalFunction);
 
   if (!redeclaredKind && isFunctionBox()) {
     Scope& funScope = functionScope();
     if (&funScope != &varScope()) {
       // Annex B.3.3.1 disallows redeclaring parameter names. In the
       // presence of parameter expressions, parameter names are on the
--- a/js/src/frontend/ParseContext.h
+++ b/js/src/frontend/ParseContext.h
@@ -618,19 +618,18 @@ class ParseContext : public Nestable<Par
     return sc_->isFunctionBox() && sc_->asFunctionBox()->isArrow();
   }
 
   bool isMethod() const {
     return sc_->isFunctionBox() && sc_->asFunctionBox()->isMethod();
   }
 
   bool isGetterOrSetter() const {
-    return sc_->isFunctionBox() &&
-           (sc_->asFunctionBox()->isGetter() ||
-            sc_->asFunctionBox()->isSetter());
+    return sc_->isFunctionBox() && (sc_->asFunctionBox()->isGetter() ||
+                                    sc_->asFunctionBox()->isSetter());
   }
 
   uint32_t scriptId() const { return scriptId_; }
 
   bool annexBAppliesToLexicalFunctionInInnermostScope(FunctionBox* funbox);
 
   bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
                      uint32_t beginPos,
--- a/js/src/frontend/SharedContext.cpp
+++ b/js/src/frontend/SharedContext.cpp
@@ -158,18 +158,17 @@ FunctionBox::FunctionBox(JSContext* cx, 
       hasThisBinding_(false),
       hasInnerFunctions_(false),
       isArrow_(fun->isArrow()),
       isNamedLambda_(fun->isNamedLambda()),
       isGetter_(fun->isGetter()),
       isSetter_(fun->isSetter()),
       isMethod_(fun->isMethod()),
       kind_(fun->kind()),
-      explicitName_(fun->explicitName())
-{
+      explicitName_(fun->explicitName()) {
   // Functions created at parse time may be set singleton after parsing and
   // baked into JIT code, so they must be allocated tenured. They are held by
   // the JSScript so cannot be collected during a minor GC anyway.
   MOZ_ASSERT(fun->isTenured());
 }
 
 void FunctionBox::initFromLazyFunction(JSFunction* fun) {
   if (fun->lazyScript()->isDerivedClassConstructor()) {
@@ -190,17 +189,18 @@ void FunctionBox::initStandaloneFunction
   // Standalone functions are Function or Generator constructors and are
   // always scoped to the global.
   MOZ_ASSERT(enclosingScope->is<GlobalScope>());
   enclosingScope_ = enclosingScope;
   allowNewTarget_ = true;
   thisBinding_ = ThisBinding::Function;
 }
 
-void FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, JSFunction* fun,
+void FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing,
+                                                JSFunction* fun,
                                                 FunctionSyntaxKind kind) {
   SharedContext* sc = enclosing->sc();
   useAsm = sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm();
 
   // Arrow functions don't have their own `this` binding.
   if (fun->isArrow()) {
     allowNewTarget_ = sc->allowNewTarget();
     allowSuperProperty_ = sc->allowSuperProperty();
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -430,17 +430,18 @@ class FunctionBox : public ObjectBox, pu
     return MutableHandle<VarScope::Data*>::fromMarkedLocation(
         &extraVarScopeBindings_);
   }
 
   void initFromLazyFunction(JSFunction* fun);
   void initStandaloneFunction(Scope* enclosingScope);
   void initWithEnclosingParseContext(ParseContext* enclosing, JSFunction* fun,
                                      FunctionSyntaxKind kind);
-  void initFieldInitializer(ParseContext* enclosing, JSFunction* fun, HasHeritage hasHeritage);
+  void initFieldInitializer(ParseContext* enclosing, JSFunction* fun,
+                            HasHeritage hasHeritage);
 
   inline bool isLazyFunctionWithoutEnclosingScope() const {
     return function()->isInterpretedLazy() &&
            !function()->lazyScript()->hasEnclosingScope();
   }
   void setEnclosingScopeForInnerLazyFunction(Scope* enclosingScope);
   void finish();
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -420,19 +420,18 @@ class MOZ_NON_MEMMOVABLE BarrieredBase {
   // instantiation. Friending to the generic template leads to a number of
   // unintended consequences, including template resolution ambiguity and a
   // circular dependency with Tracing.h.
   T* unsafeUnbarrieredForTracing() { return &value; }
 };
 
 // Base class for barriered pointer types that intercept only writes.
 template <class T>
-class WriteBarriered
-    : public BarrieredBase<T>,
-      public WrappedPtrOperations<T, WriteBarriered<T>> {
+class WriteBarriered : public BarrieredBase<T>,
+                       public WrappedPtrOperations<T, WriteBarriered<T>> {
  protected:
   using BarrieredBase<T>::value;
 
   // WriteBarriered is not directly instantiable.
   explicit WriteBarriered(const T& v) : BarrieredBase<T>(v) {}
 
  public:
   using ElementType = T;
--- a/js/src/gc/FreeOp-inl.h
+++ b/js/src/gc/FreeOp-inl.h
@@ -37,11 +37,11 @@ inline void FreeOp::freeLater(void* p) {
   // It's not safe to free this allocation immediately, so we must crash if we
   // can't append to the list.
   AutoEnterOOMUnsafeRegion oomUnsafe;
   if (!freeLaterList.append(p)) {
     oomUnsafe.crash("FreeOp::freeLater");
   }
 }
 
-} // namespace js
+}  // namespace js
 
-#endif // gc_FreeOp_inl_h
+#endif  // gc_FreeOp_inl_h
--- a/js/src/gc/FreeOp.h
+++ b/js/src/gc/FreeOp.h
@@ -4,28 +4,27 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_FreeOp_h
 #define gc_FreeOp_h
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
 
+#include "gc/GCEnum.h"                // js::MemoryUse
 #include "jit/ExecutableAllocator.h"  // jit::JitPoisonRangeVector
 #include "js/AllocPolicy.h"           // SystemAllocPolicy
 #include "js/MemoryFunctions.h"       // JSFreeOp
 #include "js/Utility.h"               // AutoEnterOOMUnsafeRegion, js_free
 #include "js/Vector.h"                // js::Vector
 
 struct JSRuntime;
 
 namespace js {
 
-enum class MemoryUse : uint8_t;
-
 /*
  * A FreeOp can do one thing: free memory. For convenience, it has delete_
  * convenience methods that also call destructors.
  *
  * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not
  * need to pass a JSContext to those hooks.
  */
 class FreeOp : public JSFreeOp {
--- a/js/src/gc/GC-inl.h
+++ b/js/src/gc/GC-inl.h
@@ -105,20 +105,17 @@ class ArenaCellIter {
   ArenaCellIter()
       : firstThingOffset(0),
         thingSize(0),
         arenaAddr(nullptr),
         thing(0),
         traceKind(JS::TraceKind::Null),
         initialized(false) {}
 
-  explicit ArenaCellIter(Arena* arena)
-      : initialized(false) {
-    init(arena);
-  }
+  explicit ArenaCellIter(Arena* arena) : initialized(false) { init(arena); }
 
   void init(Arena* arena) {
     MOZ_ASSERT(!initialized);
     MOZ_ASSERT(arena);
     initialized = true;
     AllocKind kind = arena->getAllocKind();
     firstThingOffset = Arena::firstThingOffset(kind);
     thingSize = Arena::thingSize(kind);
@@ -390,19 +387,17 @@ class ZoneCellIter : protected ZoneAllCe
     if (!JS::RuntimeHeapIsCollecting(rt->heapState())) {
       JS::TraceKind traceKind = JS::MapTypeToTraceKind<T>::kind;
       ExposeGCThingToActiveJS(JS::GCCellPtr(cell, traceKind));
     }
 
     return cell;
   }
 
-  T* get() const {
-    return reinterpret_cast<T*>(getCell());
-  }
+  T* get() const { return reinterpret_cast<T*>(getCell()); }
 
   TenuredCell* unbarrieredGetCell() const { return Base::getCell(); }
   T* unbarrieredGet() const { return Base::get(); }
   operator T*() const { return get(); }
   T* operator->() const { return get(); }
 
  private:
   void skipDying() {
--- a/js/src/gc/GCEnum.h
+++ b/js/src/gc/GCEnum.h
@@ -8,16 +8,18 @@
  * GC-internal enum definitions.
  */
 
 #ifndef gc_GCEnum_h
 #define gc_GCEnum_h
 
 #include <stdint.h>
 
+#include "js/MemoryFunctions.h"  // JS_FOR_EACH_PUBLIC_MEMORY_USE
+
 namespace js {
 namespace gc {
 
 // Mark colors. Order is important here: the greater value the 'more marked' a
 // cell is.
 enum class MarkColor : uint8_t { Gray = 1, Black = 2 };
 
 // The phases of an incremental GC.
@@ -83,11 +85,26 @@ enum class ZealMode {
 #define ZEAL_MODE(name, value) name = value,
   JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
 #undef ZEAL_MODE
       Count,
   Limit = Count - 1
 };
 
 } /* namespace gc */
+
+#define JS_FOR_EACH_INTERNAL_MEMORY_USE(_) \
+  _(ArrayBufferContents)                   \
+  _(StringContents)
+
+#define JS_FOR_EACH_MEMORY_USE(_)  \
+  JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \
+  JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
+
+enum class MemoryUse : uint8_t {
+#define DEFINE_MEMORY_USE(Name) Name,
+  JS_FOR_EACH_MEMORY_USE(DEFINE_MEMORY_USE)
+#undef DEFINE_MEMORY_USE
+};
+
 } /* namespace js */
 
 #endif /* gc_GCEnum_h */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -436,19 +436,19 @@ JS_PUBLIC_API void JS::UnsafeTraceRoot(J
       JSTracer*, type*, const char*);
 FOR_EACH_PUBLIC_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_PUBLIC_TRACE_FUNCTIONS)
 #undef INSTANTIATE_PUBLIC_TRACE_FUNCTIONS
 
 namespace js {
 namespace gc {
 
-#define INSTANTIATE_INTERNAL_TRACE_FUNCTIONS(type)                          \
-  template void TraceEdgeInternal<type>(JSTracer*, type*, const char*);     \
-  template void TraceRangeInternal<type>(JSTracer*, size_t len, type*,      \
+#define INSTANTIATE_INTERNAL_TRACE_FUNCTIONS(type)                      \
+  template void TraceEdgeInternal<type>(JSTracer*, type*, const char*); \
+  template void TraceRangeInternal<type>(JSTracer*, size_t len, type*,  \
                                          const char*);
 
 #define INSTANTIATE_INTERNAL_TRACE_FUNCTIONS_FROM_TRACEKIND(_1, type, _2, _3) \
   INSTANTIATE_INTERNAL_TRACE_FUNCTIONS(type*)
 
 JS_FOR_EACH_TRACEKIND(INSTANTIATE_INTERNAL_TRACE_FUNCTIONS_FROM_TRACEKIND)
 FOR_EACH_PUBLIC_TAGGED_GC_POINTER_TYPE(INSTANTIATE_INTERNAL_TRACE_FUNCTIONS)
 
@@ -733,20 +733,20 @@ JS_PUBLIC_API void js::gc::PerformIncrem
   Zone* zone = cell->zone();
   MOZ_ASSERT(zone->needsIncrementalBarrier());
 
   // Skip disptaching on known tracer type.
   GCMarker* gcmarker = GCMarker::fromTracer(zone->barrierTracer());
 
   // Mark the argument, as DoMarking above.
   ApplyGCThingTyped(thing, [gcmarker](auto thing) {
-                             MOZ_ASSERT(ShouldMark(gcmarker, thing));
-                             CheckTracedThing(gcmarker, thing);
-                             gcmarker->traverse(thing);
-                           });
+    MOZ_ASSERT(ShouldMark(gcmarker, thing));
+    CheckTracedThing(gcmarker, thing);
+    gcmarker->traverse(thing);
+  });
 }
 
 // The simplest traversal calls out to the fully generic traceChildren function
 // to visit the child edges. In the absence of other traversal mechanisms, this
 // function will rapidly grow the stack past its bounds and crash the process.
 // Thus, this generic tracing should only be used in cases where subsequent
 // tracing will not recurse.
 template <typename T>
@@ -3099,18 +3099,17 @@ size_t js::TenuringTracer::moveStringToT
   if (!src->isInline() && src->isLinear()) {
     if (src->isUndepended() || !src->hasBase()) {
       void* chars = src->asLinear().nonInlineCharsRaw();
       nursery().removeMallocedBuffer(chars);
     }
   }
 
   if (dst->isFlat() && !dst->isInline()) {
-    AddCellMemory(dst, dst->asFlat().allocSize(),
-                  MemoryUse::StringContents);
+    AddCellMemory(dst, dst->asFlat().allocSize(), MemoryUse::StringContents);
   }
 
   return size;
 }
 
 /*** IsMarked / IsAboutToBeFinalized ****************************************/
 
 template <typename T>
--- a/js/src/gc/NurseryAwareHashMap.h
+++ b/js/src/gc/NurseryAwareHashMap.h
@@ -17,18 +17,17 @@ namespace js {
 
 namespace detail {
 // This class only handles the incremental case and does not deal with nursery
 // pointers. The only users should be for NurseryAwareHashMap; it is defined
 // externally because we need a GCPolicy for its use in the contained map.
 template <typename T>
 class UnsafeBareWeakHeapPtr : public ReadBarriered<T> {
  public:
-  UnsafeBareWeakHeapPtr()
-      : ReadBarriered<T>(JS::SafelyInitialized<T>()) {}
+  UnsafeBareWeakHeapPtr() : ReadBarriered<T>(JS::SafelyInitialized<T>()) {}
   MOZ_IMPLICIT UnsafeBareWeakHeapPtr(const T& v) : ReadBarriered<T>(v) {}
   explicit UnsafeBareWeakHeapPtr(const UnsafeBareWeakHeapPtr& v)
       : ReadBarriered<T>(v) {}
   UnsafeBareWeakHeapPtr(UnsafeBareWeakHeapPtr&& v)
       : ReadBarriered<T>(std::move(v)) {}
 
   UnsafeBareWeakHeapPtr& operator=(const UnsafeBareWeakHeapPtr& v) {
     this->value = v.value;
@@ -176,18 +175,17 @@ class NurseryAwareHashMap {
   bool hasNurseryEntries() const { return !nurseryEntries.empty(); }
 };
 
 }  // namespace js
 
 namespace JS {
 template <typename T>
 struct GCPolicy<js::detail::UnsafeBareWeakHeapPtr<T>> {
-  static void trace(JSTracer* trc,
-                    js::detail::UnsafeBareWeakHeapPtr<T>* thingp,
+  static void trace(JSTracer* trc, js::detail::UnsafeBareWeakHeapPtr<T>* thingp,
                     const char* name) {
     js::TraceEdge(trc, thingp, name);
   }
   static bool needsSweep(js::detail::UnsafeBareWeakHeapPtr<T>* thingp) {
     return js::gc::IsAboutToBeFinalized(thingp);
   }
 };
 }  // namespace JS
--- a/js/src/gc/PrivateIterators-inl.h
+++ b/js/src/gc/PrivateIterators-inl.h
@@ -15,26 +15,24 @@
 
 #include "gc/GC-inl.h"
 
 namespace js {
 namespace gc {
 
 class ArenaCellIterUnderGC : public ArenaCellIter {
  public:
-  explicit ArenaCellIterUnderGC(Arena* arena)
-      : ArenaCellIter(arena) {
+  explicit ArenaCellIterUnderGC(Arena* arena) : ArenaCellIter(arena) {
     MOZ_ASSERT(CurrentThreadIsPerformingGC());
   }
 };
 
 class ArenaCellIterUnderFinalize : public ArenaCellIter {
  public:
-  explicit ArenaCellIterUnderFinalize(Arena* arena)
-      : ArenaCellIter(arena) {
+  explicit ArenaCellIterUnderFinalize(Arena* arena) : ArenaCellIter(arena) {
     MOZ_ASSERT(CurrentThreadIsGCSweeping());
   }
 };
 
 class GrayObjectIter : public ZoneAllCellIter<js::gc::TenuredCell> {
  public:
   explicit GrayObjectIter(JS::Zone* zone, AllocKind kind)
       : ZoneAllCellIter<js::gc::TenuredCell>() {
--- a/js/src/gc/Scheduling.h
+++ b/js/src/gc/Scheduling.h
@@ -308,31 +308,16 @@
 #define gc_Scheduling_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 
 #include "js/HashTable.h"
 
 namespace js {
-
-#define JS_FOR_EACH_INTERNAL_MEMORY_USE(_)      \
-  _(ArrayBufferContents)                        \
-  _(StringContents)
-
-#define JS_FOR_EACH_MEMORY_USE(_)               \
-  JS_FOR_EACH_PUBLIC_MEMORY_USE(_)              \
-  JS_FOR_EACH_INTERNAL_MEMORY_USE(_)
-
-enum class MemoryUse : uint8_t {
-#define DEFINE_MEMORY_USE(Name) Name,
-  JS_FOR_EACH_MEMORY_USE(DEFINE_MEMORY_USE)
-#undef DEFINE_MEMORY_USE
-};
-
 namespace gc {
 
 struct Cell;
 
 enum TriggerKind { NoTrigger = 0, IncrementalTrigger, NonIncrementalTrigger };
 
 /*
  * Encapsulates all of the GC tunables. These are effectively constant and
@@ -706,18 +691,29 @@ class MemoryTracker {
                   mozilla::recordreplay::Behavior::DontPreserve>
       bytes_;
 
 #ifdef DEBUG
   void trackMemory(Cell* cell, size_t nbytes, MemoryUse use);
   void untrackMemory(Cell* cell, size_t nbytes, MemoryUse use);
 
   struct Key {
-    Cell* cell;
-    MemoryUse use;
+    Key(Cell* cell, MemoryUse use);
+    Cell* cell() const;
+    MemoryUse use() const;
+
+   private:
+#  ifdef JS_64BIT
+    // Pack this into a single word on 64 bit platforms.
+    uintptr_t cell_ : 56;
+    uintptr_t use_ : 8;
+#  else
+    uintptr_t cell_ : 32;
+    uintptr_t use_ : 8;
+#  endif
   };
 
   struct Hasher {
     using Lookup = Key;
     static HashNumber hash(const Lookup& l);
     static bool match(const Key& key, const Lookup& l);
     static void rekey(Key& k, const Key& newKey);
   };
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -121,18 +121,17 @@ inline void AssertRootMarkingPhase(JSTra
 template <typename T>
 inline void TraceEdge(JSTracer* trc, WriteBarriered<T>* thingp,
                       const char* name) {
   gc::TraceEdgeInternal(
       trc, gc::ConvertToBase(thingp->unsafeUnbarrieredForTracing()), name);
 }
 
 template <typename T>
-inline void TraceEdge(JSTracer* trc, WeakHeapPtr<T>* thingp,
-                      const char* name) {
+inline void TraceEdge(JSTracer* trc, WeakHeapPtr<T>* thingp, const char* name) {
   gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp->unsafeGet()), name);
 }
 
 // Trace through a possibly-null edge in the live object graph on behalf of
 // tracing.
 
 template <typename T>
 inline void TraceNullableEdge(JSTracer* trc, WriteBarriered<T>* thingp,
@@ -156,18 +155,17 @@ inline void TraceNullableEdge(JSTracer* 
 
 template <typename T>
 inline void TraceRoot(JSTracer* trc, T* thingp, const char* name) {
   gc::AssertRootMarkingPhase(trc);
   gc::TraceEdgeInternal(trc, gc::ConvertToBase(thingp), name);
 }
 
 template <typename T>
-inline void TraceRoot(JSTracer* trc, WeakHeapPtr<T>* thingp,
-                      const char* name) {
+inline void TraceRoot(JSTracer* trc, WeakHeapPtr<T>* thingp, const char* name) {
   TraceRoot(trc, thingp->unsafeGet(), name);
 }
 
 // Idential to TraceRoot, except that this variant will not crash if |*thingp|
 // is null.
 
 template <typename T>
 inline void TraceNullableRoot(JSTracer* trc, T* thingp, const char* name) {
@@ -208,18 +206,17 @@ template <typename T>
 void TraceRootRange(JSTracer* trc, size_t len, T* vec, const char* name) {
   gc::AssertRootMarkingPhase(trc);
   gc::TraceRangeInternal(trc, len, gc::ConvertToBase(vec), name);
 }
 
 // Trace an edge that crosses compartment boundaries. If the compartment of the
 // destination thing is not being GC'd, then the edge will not be traced.
 void TraceCrossCompartmentEdge(JSTracer* trc, JSObject* src,
-                               WriteBarriered<Value>* dst,
-                               const char* name);
+                               WriteBarriered<Value>* dst, const char* name);
 
 // As above but with manual barriers.
 template <typename T>
 void TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src,
                                                 T* dst, const char* name);
 
 // Permanent atoms and well-known symbols are shared between runtimes and must
 // use a separate marking path so that we can filter them out of normal heap
--- a/js/src/gc/Verifier.h
+++ b/js/src/gc/Verifier.h
@@ -19,23 +19,21 @@ namespace gc {
 // Like gc::MarkColor but allows the possibility of the cell being
 // unmarked.
 enum class CellColor : uint8_t {
   White = 0,
   Gray = uint8_t(MarkColor::Gray),
   Black = uint8_t(MarkColor::Black)
 };
 
-static constexpr CellColor AllCellColors[] = {
-  CellColor::White, CellColor::Gray, CellColor::Black
-};
+static constexpr CellColor AllCellColors[] = {CellColor::White, CellColor::Gray,
+                                              CellColor::Black};
 
-static constexpr CellColor MarkedCellColors[] = {
-  CellColor::Gray, CellColor::Black
-};
+static constexpr CellColor MarkedCellColors[] = {CellColor::Gray,
+                                                 CellColor::Black};
 
 inline CellColor GetCellColor(Cell* cell) {
   if (cell->isMarkedBlack()) {
     return CellColor::Black;
   }
 
   if (cell->isMarkedGray()) {
     return CellColor::Gray;
@@ -54,12 +52,12 @@ inline CellColor ExpectedWeakMapKeyColor
   return Min(mapColor, delegateColor);
 }
 
 inline CellColor ExpectedKeyAndDelegateColor(CellColor keyColor,
                                              CellColor delegateColor) {
   return Max(keyColor, delegateColor);
 }
 
-} // namespace gc
-} // namespace js
+}  // namespace gc
+}  // namespace js
 
 #endif /* gc_Verifier_h */
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -545,18 +545,18 @@ MemoryTracker::~MemoryTracker() {
 
   if (map.empty()) {
     MOZ_ASSERT(bytes() == 0);
     return;
   }
 
   fprintf(stderr, "Missing calls to JS::RemoveAssociatedMemory:\n");
   for (auto r = map.all(); !r.empty(); r.popFront()) {
-    fprintf(stderr, "  %p 0x%zx %s\n", r.front().key().cell, r.front().value(),
-            MemoryUseName(r.front().key().use));
+    fprintf(stderr, "  %p 0x%zx %s\n", r.front().key().cell(),
+            r.front().value(), MemoryUseName(r.front().key().use()));
   }
 
   MOZ_CRASH();
 }
 
 void MemoryTracker::trackMemory(Cell* cell, size_t nbytes, MemoryUse use) {
   MOZ_ASSERT(cell->isTenured());
 
@@ -595,31 +595,48 @@ void MemoryTracker::untrackMemory(Cell* 
   map.remove(ptr);
 }
 
 void MemoryTracker::fixupAfterMovingGC() {
   // Update the table after we move GC things. We don't use MovableCellHasher
   // because that would create a difference between debug and release builds.
   for (Map::Enum e(map); !e.empty(); e.popFront()) {
     const Key& key = e.front().key();
-    Cell* cell = key.cell;
+    Cell* cell = key.cell();
     if (cell->isForwarded()) {
       cell = gc::RelocationOverlay::fromCell(cell)->forwardingAddress();
-      e.rekeyFront(Key{cell, key.use});
+      e.rekeyFront(Key{cell, key.use()});
     }
   }
 }
 
+inline MemoryTracker::Key::Key(Cell* cell, MemoryUse use)
+    : cell_(uint64_t(cell)), use_(uint64_t(use)) {
+#  ifdef JS_64BIT
+  static_assert(sizeof(Key) == 8,
+                "MemoryTracker::Key should be packed into 8 bytes");
+#  endif
+  MOZ_ASSERT(this->cell() == cell);
+  MOZ_ASSERT(this->use() == use);
+}
+
+inline Cell* MemoryTracker::Key::cell() const {
+  return reinterpret_cast<Cell*>(cell_);
+}
+inline MemoryUse MemoryTracker::Key::use() const {
+  return static_cast<MemoryUse>(use_);
+}
+
 inline HashNumber MemoryTracker::Hasher::hash(const Lookup& l) {
-  return mozilla::HashGeneric(DefaultHasher<Cell*>::hash(l.cell),
-                              DefaultHasher<unsigned>::hash(unsigned(l.use)));
+  return mozilla::HashGeneric(DefaultHasher<Cell*>::hash(l.cell()),
+                              DefaultHasher<unsigned>::hash(unsigned(l.use())));
 }
 
 inline bool MemoryTracker::Hasher::match(const Key& k, const Lookup& l) {
-  return k.cell == l.cell && k.use == l.use;
+  return k.cell() == l.cell() && k.use() == l.use();
 }
 
 inline void MemoryTracker::Hasher::rekey(Key& k, const Key& newKey) {
   k = newKey;
 }
 
 #endif  // DEBUG
 
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h
@@ -25,19 +25,17 @@ void MacroAssembler::moveGPRToFloat32(Re
 void MacroAssembler::move8SignExtend(Register src, Register dest) {
   ma_seb(dest, src);
 }
 
 void MacroAssembler::move16SignExtend(Register src, Register dest) {
   ma_seh(dest, src);
 }
 
-void MacroAssembler::loadAbiReturnAddress(Register dest) {
-  movePtr(ra, dest);
-}
+void MacroAssembler::loadAbiReturnAddress(Register dest) { movePtr(ra, dest); }
 
 // ===============================================================
 // Logical instructions
 
 void MacroAssembler::not32(Register reg) { ma_not(reg, reg); }
 
 void MacroAssembler::and32(Register src, Register dest) {
   as_and(dest, dest, src);
--- a/js/src/jsapi-tests/testGCGrayMarking.cpp
+++ b/js/src/jsapi-tests/testGCGrayMarking.cpp
@@ -157,25 +157,24 @@ bool TestMarking() {
 
 static const CellColor DontMark = CellColor::White;
 
 enum MarkKeyOrDelegate : bool { MarkKey = true, MarkDelegate = false };
 
 bool TestJSWeakMaps() {
   for (auto keyOrDelegateColor : MarkedCellColors) {
     for (auto mapColor : MarkedCellColors) {
-      for (auto markKeyOrDelegate : { MarkKey, MarkDelegate }) {
-        CellColor expected = ExpectedWeakMapValueColor(keyOrDelegateColor,
-                                                       mapColor);
+      for (auto markKeyOrDelegate : {MarkKey, MarkDelegate}) {
+        CellColor expected =
+            ExpectedWeakMapValueColor(keyOrDelegateColor, mapColor);
         CHECK(TestJSWeakMap(markKeyOrDelegate, keyOrDelegateColor, mapColor,
                             expected));
 #ifdef JS_GC_ZEAL
-        CHECK(TestJSWeakMapWithGrayUnmarking(markKeyOrDelegate,
-                                             keyOrDelegateColor, mapColor,
-                                             expected));
+        CHECK(TestJSWeakMapWithGrayUnmarking(
+            markKeyOrDelegate, keyOrDelegateColor, mapColor, expected));
 #endif
       }
     }
   }
 
   return true;
 }
 
@@ -184,24 +183,23 @@ bool TestInternalWeakMaps() {
     for (auto delegateMarkColor : AllCellColors) {
       if (keyMarkColor == CellColor::White &&
           delegateMarkColor == CellColor::White) {
         continue;
       }
 
       CellColor keyOrDelegateColor =
           ExpectedKeyAndDelegateColor(keyMarkColor, delegateMarkColor);
-      CellColor expected = ExpectedWeakMapValueColor(keyOrDelegateColor,
-                                                     CellColor::Black);
+      CellColor expected =
+          ExpectedWeakMapValueColor(keyOrDelegateColor, CellColor::Black);
       CHECK(TestInternalWeakMap(keyMarkColor, delegateMarkColor, expected));
 
 #ifdef JS_GC_ZEAL
       CHECK(TestInternalWeakMapWithGrayUnmarking(keyMarkColor,
-                                                 delegateMarkColor,
-                                                 expected));
+                                                 delegateMarkColor, expected));
 #endif
     }
   }
 
   return true;
 }
 
 bool TestJSWeakMap(MarkKeyOrDelegate markKey, CellColor weakMapMarkColor,
@@ -305,21 +303,21 @@ bool TestJSWeakMapWithGrayUnmarking(Mark
 
   JS_UnsetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking));
 
   return true;
 }
 
 static void MaybeExposeObject(JSObject* object, CellColor color) {
   if (color == CellColor::Black) {
-      JS::ExposeObjectToActiveJS(object);
+    JS::ExposeObjectToActiveJS(object);
   }
 }
 
-#endif // JS_GC_ZEAL
+#endif  // JS_GC_ZEAL
 
 bool CreateJSWeakMapObjects(JSObject** weakMapOut, JSObject** keyOut,
                             JSObject** valueOut) {
   RootedObject key(cx, AllocWeakmapKeyObject());
   CHECK(key);
 
   RootedObject value(cx, AllocPlainObject());
   CHECK(value);
@@ -428,17 +426,17 @@ bool TestInternalWeakMapWithGrayUnmarkin
     CHECK(GetCellColor(value) == expectedColor);
   }
 
   JS_UnsetGCZeal(cx, uint8_t(ZealMode::YieldWhileGrayMarking));
 
   return true;
 }
 
-#endif // JS_GC_ZEAL
+#endif  // JS_GC_ZEAL
 
 bool CreateInternalWeakMapObjects(UniquePtr<GCManagedObjectWeakMap>* weakMapOut,
                                   JSObject** keyOut, JSObject** valueOut) {
   RootedObject key(cx, AllocWeakmapKeyObject());
   CHECK(key);
 
   RootedObject value(cx, AllocPlainObject());
   CHECK(value);
--- a/js/src/jsapi-tests/testScriptObject.cpp
+++ b/js/src/jsapi-tests/testScriptObject.cpp
@@ -3,17 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 
 #include "js/CompilationAndEvaluation.h"  // JS::Compile{,{,Utf8{File,Path}}DontInflate}
-#include "js/SourceText.h"  // JS::Source{Ownership,Text}
+#include "js/SourceText.h"                // JS::Source{Ownership,Text}
 #include "jsapi-tests/tests.h"
 
 struct ScriptObjectFixture : public JSAPITest {
   static const int code_size;
   static const char code[];
   static char16_t uc_code[];
 
   ScriptObjectFixture() {
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -951,18 +951,17 @@ void ArrayBufferObject::releaseData(Free
       // There's nothing to release if there's no data.
       MOZ_ASSERT(dataPointer() == nullptr);
       break;
     case USER_OWNED:
       // User-owned data is released by, well, the user.
       break;
     case MAPPED:
       gc::DeallocateMappedContent(dataPointer(), byteLength());
-      RemoveCellMemory(this, associatedBytes(),
-                       MemoryUse::ArrayBufferContents);
+      RemoveCellMemory(this, associatedBytes(), MemoryUse::ArrayBufferContents);
       break;
     case WASM:
       WasmArrayRawBuffer::Release(dataPointer());
       RemoveCellMemory(this, byteLength(), MemoryUse::ArrayBufferContents);
       break;
     case EXTERNAL:
       if (freeInfo()->freeFunc) {
         // The analyzer can't know for sure whether the embedder-supplied
--- a/js/src/vm/AtomsTable.h
+++ b/js/src/vm/AtomsTable.h
@@ -41,18 +41,18 @@ class MOZ_RAII AutoLockAllAtoms {
 
 struct AtomHasher {
   struct Lookup;
   static inline HashNumber hash(const Lookup& l);
   static MOZ_ALWAYS_INLINE bool match(const WeakHeapPtr<JSAtom*>& entry,
                                       const Lookup& lookup);
 };
 
-using AtomSet = JS::GCHashSet<WeakHeapPtr<JSAtom*>, AtomHasher,
-                              SystemAllocPolicy>;
+using AtomSet =
+    JS::GCHashSet<WeakHeapPtr<JSAtom*>, AtomHasher, SystemAllocPolicy>;
 
 // This class is a wrapper for AtomSet that is used to ensure the AtomSet is
 // not modified. It should only expose read-only methods from AtomSet.
 // Note however that the atoms within the table can be marked during GC.
 class FrozenAtomSet {
   AtomSet* mSet;
 
  public:
--- a/js/src/vm/CompilationAndEvaluation.cpp
+++ b/js/src/vm/CompilationAndEvaluation.cpp
@@ -558,18 +558,19 @@ static bool EvaluateSourceBuffer(JSConte
 JS_PUBLIC_API bool JS::Evaluate(JSContext* cx,
                                 const ReadOnlyCompileOptions& options,
                                 SourceText<Utf8Unit>& srcBuf,
                                 MutableHandle<Value> rval) {
   RootedObject globalLexical(cx, &cx->global()->lexicalEnvironment());
 
   size_t length = srcBuf.length();
   auto chars = UniqueTwoByteChars(
-      UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(srcBuf.get(), length),
-                                  &length, js::MallocArena).get());
+      UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(srcBuf.get(), length), &length,
+                                  js::MallocArena)
+          .get());
   if (!chars) {
     return false;
   }
 
   SourceText<char16_t> inflatedSrc;
   if (!inflatedSrc.init(cx, std::move(chars), length)) {
     return false;
   }
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -51,18 +51,18 @@
 /* static */ inline bool js::Debugger::checkNoExecute(JSContext* cx,
                                                       HandleScript script) {
   if (!cx->realm()->isDebuggee() || !cx->noExecuteDebuggerTop) {
     return true;
   }
   return slowPathCheckNoExecute(cx, script);
 }
 
-/* static */ inline js::ResumeMode js::Debugger::onEnterFrame(JSContext* cx,
-                                                       AbstractFramePtr frame) {
+/* static */ inline js::ResumeMode js::Debugger::onEnterFrame(
+    JSContext* cx, AbstractFramePtr frame) {
   MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(),
                 frame.isDebuggee());
   if (!frame.isDebuggee()) {
     return ResumeMode::Continue;
   }
   return slowPathOnEnterFrame(cx, frame);
 }
 
@@ -94,18 +94,18 @@
 
 /* static */ inline void js::Debugger::onNewWasmInstance(
     JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
   if (cx->realm()->isDebuggee()) {
     slowPathOnNewWasmInstance(cx, wasmInstance);
   }
 }
 
-/* static */ inline void js::Debugger::onNewPromise(JSContext* cx,
-                                             Handle<PromiseObject*> promise) {
+/* static */ inline void js::Debugger::onNewPromise(
+    JSContext* cx, Handle<PromiseObject*> promise) {
   if (MOZ_UNLIKELY(cx->realm()->isDebuggee())) {
     slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
   }
 }
 
 /* static */ inline void js::Debugger::onPromiseSettled(
     JSContext* cx, Handle<PromiseObject*> promise) {
   if (MOZ_UNLIKELY(promise->realm()->isDebuggee())) {
--- a/js/src/vm/EnvironmentObject.cpp
+++ b/js/src/vm/EnvironmentObject.cpp
@@ -1782,17 +1782,18 @@ class DebugEnvironmentProxyHandler : pub
            !env.as<LexicalEnvironmentObject>().isExtensible();
   }
 
   static Scope* getEnvironmentScope(const JSObject& env) {
     if (isFunctionEnvironment(env)) {
       return env.as<CallObject>().callee().nonLazyScript()->bodyScope();
     }
     if (env.is<ModuleEnvironmentObject>()) {
-      JSScript* script = env.as<ModuleEnvironmentObject>().module().maybeScript();
+      JSScript* script =
+          env.as<ModuleEnvironmentObject>().module().maybeScript();
       return script ? script->bodyScope() : nullptr;
     }
     if (isNonExtensibleLexicalEnvironment(env)) {
       return &env.as<LexicalEnvironmentObject>().scope();
     }
     if (env.is<VarEnvironmentObject>()) {
       return &env.as<VarEnvironmentObject>().scope();
     }
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -959,18 +959,17 @@ class DebugEnvironments {
    * The map from environment objects of live frames to the live frame. This
    * map updated lazily whenever the debugger needs the information. In
    * between two lazy updates, liveEnvs becomes incomplete (but not invalid,
    * onPop* removes environments as they are popped). Thus, two consecutive
    * debugger lazy updates of liveEnvs need only fill in the new
    * environments.
    */
   typedef GCHashMap<WeakHeapPtr<JSObject*>, LiveEnvironmentVal,
-                    MovableCellHasher<WeakHeapPtr<JSObject*>>,
-                    ZoneAllocPolicy>
+                    MovableCellHasher<WeakHeapPtr<JSObject*>>, ZoneAllocPolicy>
       LiveEnvironmentMap;
   LiveEnvironmentMap liveEnvs;
 
  public:
   DebugEnvironments(JSContext* cx, Zone* zone);
   ~DebugEnvironments();
 
   Zone* zone() const { return zone_; }
--- a/js/src/vm/GeneratorObject.h
+++ b/js/src/vm/GeneratorObject.h
@@ -127,17 +127,18 @@ class AbstractGeneratorObject : public N
   }
   bool isRunning() const {
     return getFixedSlot(RESUME_INDEX_SLOT) == Int32Value(RESUME_INDEX_RUNNING);
   }
   bool isSuspended() const {
     // Note: also update Baseline's IsSuspendedGenerator code if this
     // changes.
     Value resumeIndex = getFixedSlot(RESUME_INDEX_SLOT);
-    return resumeIndex.isInt32() && resumeIndex.toInt32() < RESUME_INDEX_RUNNING;
+    return resumeIndex.isInt32() &&
+           resumeIndex.toInt32() < RESUME_INDEX_RUNNING;
   }
   void setRunning() {
     MOZ_ASSERT(isSuspended());
     setFixedSlot(RESUME_INDEX_SLOT, Int32Value(RESUME_INDEX_RUNNING));
   }
   void setResumeIndex(jsbytecode* pc) {
     MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD ||
                *pc == JSOP_AWAIT);
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/HelperThreads.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
+#include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 
 #include "builtin/Promise.h"
 #include "frontend/BytecodeCompilation.h"
 #include "gc/GCInternals.h"
 #include "jit/IonBuilder.h"
 #include "js/ContextOptions.h"  // JS::ContextOptions
 #include "js/SourceText.h"
 #include "js/UniquePtr.h"
@@ -36,16 +37,17 @@
 #include "vm/Realm-inl.h"
 
 using namespace js;
 
 using mozilla::Maybe;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 using mozilla::Unused;
+using mozilla::Utf8Unit;
 
 using JS::CompileOptions;
 using JS::ReadOnlyCompileOptions;
 
 namespace js {
 
 GlobalHelperThreadState* gHelperThreadState = nullptr;
 
@@ -503,24 +505,35 @@ void ParseTask::runTask() {
   parse(cx);
 
   MOZ_ASSERT(cx->tempLifoAlloc().isEmpty());
   cx->tempLifoAlloc().freeAll();
   cx->frontendCollectionPool().purge();
   cx->atomsZoneFreeLists().clear();
 }
 
-ScriptParseTask::ScriptParseTask(JSContext* cx,
-                                 JS::SourceText<char16_t>& srcBuf,
-                                 JS::OffThreadCompileCallback callback,
-                                 void* callbackData)
+template <typename Unit>
+struct ScriptParseTask : public ParseTask {
+  JS::SourceText<Unit> data;
+
+  ScriptParseTask(JSContext* cx, JS::SourceText<Unit>& srcBuf,
+                  JS::OffThreadCompileCallback callback, void* callbackData);
+  void parse(JSContext* cx) override;
+};
+
+template <typename Unit>
+ScriptParseTask<Unit>::ScriptParseTask(JSContext* cx,
+                                       JS::SourceText<Unit>& srcBuf,
+                                       JS::OffThreadCompileCallback callback,
+                                       void* callbackData)
     : ParseTask(ParseTaskKind::Script, cx, callback, callbackData),
       data(std::move(srcBuf)) {}
 
-void ScriptParseTask::parse(JSContext* cx) {
+template <typename Unit>
+void ScriptParseTask<Unit>::parse(JSContext* cx) {
   MOZ_ASSERT(cx->helperThread());
 
   JSScript* script;
   Rooted<ScriptSourceObject*> sourceObject(cx);
 
   {
     ScopeKind scopeKind =
         options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
@@ -538,30 +551,41 @@ void ScriptParseTask::parse(JSContext* c
   // we must finish initializing the SSO.  This is because there may be valid
   // inner scripts observable by the debugger which reference the partially-
   // initialized SSO.
   if (sourceObject) {
     sourceObjects.infallibleAppend(sourceObject);
   }
 }
 
-ModuleParseTask::ModuleParseTask(JSContext* cx,
-                                 JS::SourceText<char16_t>& srcBuf,
-                                 JS::OffThreadCompileCallback callback,
-                                 void* callbackData)
+template <typename Unit>
+struct ModuleParseTask : public ParseTask {
+  JS::SourceText<Unit> data;
+
+  ModuleParseTask(JSContext* cx, JS::SourceText<Unit>& srcBuf,
+                  JS::OffThreadCompileCallback callback, void* callbackData);
+  void parse(JSContext* cx) override;
+};
+
+template <typename Unit>
+ModuleParseTask<Unit>::ModuleParseTask(JSContext* cx,
+                                       JS::SourceText<Unit>& srcBuf,
+                                       JS::OffThreadCompileCallback callback,
+                                       void* callbackData)
     : ParseTask(ParseTaskKind::Module, cx, callback, callbackData),
       data(std::move(srcBuf)) {}
 
-void ModuleParseTask::parse(JSContext* cx) {
+template <typename Unit>
+void ModuleParseTask<Unit>::parse(JSContext* cx) {
   MOZ_ASSERT(cx->helperThread());
 
   Rooted<ScriptSourceObject*> sourceObject(cx);
 
   ModuleObject* module =
-      frontend::CompileModule(cx, options, data, &sourceObject.get());
+      frontend::ParseModule(cx, options, data, &sourceObject.get());
   if (module) {
     scripts.infallibleAppend(module->script());
     if (sourceObject) {
       sourceObjects.infallibleAppend(sourceObject);
     }
   }
 }
 
@@ -816,38 +840,43 @@ static JSObject* CreateGlobalForOffThrea
   // Don't falsely inherit the host's global trace hook.
   creationOptions.setTrace(nullptr);
 
   return JS_NewGlobalObject(cx, &parseTaskGlobalClass,
                             currentRealm->principals(),
                             JS::DontFireOnNewGlobalHook, realmOptions);
 }
 
-static bool QueueOffThreadParseTask(JSContext* cx, ParseTask* task) {
+static bool QueueOffThreadParseTask(JSContext* cx, UniquePtr<ParseTask> task) {
   AutoLockHelperThreadState lock;
 
   bool mustWait = OffThreadParsingMustWaitForGC(cx->runtime());
 
+  // Append null first, then overwrite it on  success, to avoid having two
+  // |task| pointers (one ostensibly "unique") in flight at once.  (Obviously it
+  // would be better if these vectors stored unique pointers themselves....)
   auto& queue = mustWait ? HelperThreadState().parseWaitingOnGC(lock)
                          : HelperThreadState().parseWorklist(lock);
-  if (!queue.append(task)) {
+  if (!queue.append(nullptr)) {
     ReportOutOfMemory(cx);
     return false;
   }
 
+  queue.back() = task.release();
+
   if (!mustWait) {
-    task->activate(cx->runtime());
+    queue.back()->activate(cx->runtime());
     HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER, lock);
   }
 
   return true;
 }
 
-bool StartOffThreadParseTask(JSContext* cx, ParseTask* task,
-                             const ReadOnlyCompileOptions& options) {
+static bool StartOffThreadParseTask(JSContext* cx, UniquePtr<ParseTask> task,
+                                    const ReadOnlyCompileOptions& options) {
   // Suppress GC so that calls below do not trigger a new incremental GC
   // which could require barriers on the atoms zone.
   gc::AutoSuppressGC nogc(cx);
   gc::AutoSuppressNurseryCellAlloc noNurseryAlloc(cx);
   AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
 
   JSObject* global = CreateGlobalForOffThreadParse(cx, nogc);
   if (!global) {
@@ -859,103 +888,134 @@ bool StartOffThreadParseTask(JSContext* 
   // parsing is complete. If this function exits due to error this state is
   // cleared automatically.
   AutoSetCreatedForHelperThread createdForHelper(global);
 
   if (!task->init(cx, options, global)) {
     return false;
   }
 
-  if (!QueueOffThreadParseTask(cx, task)) {
+  if (!QueueOffThreadParseTask(cx, std::move(task))) {
     return false;
   }
 
   createdForHelper.forget();
   return true;
 }
 
+template <typename Unit>
+static bool StartOffThreadParseScriptInternal(
+    JSContext* cx, const ReadOnlyCompileOptions& options,
+    JS::SourceText<Unit>& srcBuf, JS::OffThreadCompileCallback callback,
+    void* callbackData) {
+  auto task = cx->make_unique<ScriptParseTask<Unit>>(cx, srcBuf, callback,
+                                                     callbackData);
+  if (!task) {
+    return false;
+  }
+
+  return StartOffThreadParseTask(cx, std::move(task), options);
+}
+
 bool js::StartOffThreadParseScript(JSContext* cx,
                                    const ReadOnlyCompileOptions& options,
                                    JS::SourceText<char16_t>& srcBuf,
                                    JS::OffThreadCompileCallback callback,
                                    void* callbackData) {
-  auto task =
-      cx->make_unique<ScriptParseTask>(cx, srcBuf, callback, callbackData);
-  if (!task || !StartOffThreadParseTask(cx, task.get(), options)) {
+  return StartOffThreadParseScriptInternal(cx, options, srcBuf, callback,
+                                           callbackData);
+}
+
+bool js::StartOffThreadParseScript(JSContext* cx,
+                                   const ReadOnlyCompileOptions& options,
+                                   JS::SourceText<Utf8Unit>& srcBuf,
+                                   JS::OffThreadCompileCallback callback,
+                                   void* callbackData) {
+  return StartOffThreadParseScriptInternal(cx, options, srcBuf, callback,
+                                           callbackData);
+}
+
+template <typename Unit>
+static bool StartOffThreadParseModuleInternal(
+    JSContext* cx, const ReadOnlyCompileOptions& options,
+    JS::SourceText<Unit>& srcBuf, JS::OffThreadCompileCallback callback,
+    void* callbackData) {
+  auto task = cx->make_unique<ModuleParseTask<Unit>>(cx, srcBuf, callback,
+                                                     callbackData);
+  if (!task) {
     return false;
   }
 
-  Unused << task.release();
-  return true;
+  return StartOffThreadParseTask(cx, std::move(task), options);
 }
 
 bool js::StartOffThreadParseModule(JSContext* cx,
                                    const ReadOnlyCompileOptions& options,
                                    JS::SourceText<char16_t>& srcBuf,
                                    JS::OffThreadCompileCallback callback,
                                    void* callbackData) {
-  auto task =
-      cx->make_unique<ModuleParseTask>(cx, srcBuf, callback, callbackData);
-  if (!task || !StartOffThreadParseTask(cx, task.get(), options)) {
-    return false;
-  }
-
-  Unused << task.release();
-  return true;
+  return StartOffThreadParseModuleInternal(cx, options, srcBuf, callback,
+                                           callbackData);
+}
+
+bool js::StartOffThreadParseModule(JSContext* cx,
+                                   const ReadOnlyCompileOptions& options,
+                                   JS::SourceText<Utf8Unit>& srcBuf,
+                                   JS::OffThreadCompileCallback callback,
+                                   void* callbackData) {
+  return StartOffThreadParseModuleInternal(cx, options, srcBuf, callback,
+                                           callbackData);
 }
 
 bool js::StartOffThreadDecodeScript(JSContext* cx,
                                     const ReadOnlyCompileOptions& options,
                                     const JS::TranscodeRange& range,
                                     JS::OffThreadCompileCallback callback,
                                     void* callbackData) {
   auto task =
       cx->make_unique<ScriptDecodeTask>(cx, range, callback, callbackData);
-  if (!task || !StartOffThreadParseTask(cx, task.get(), options)) {
+  if (!task) {
     return false;
   }
 
-  Unused << task.release();
-  return true;
+  return StartOffThreadParseTask(cx, std::move(task), options);
 }
 
 bool js::StartOffThreadDecodeMultiScripts(JSContext* cx,
                                           const ReadOnlyCompileOptions& options,
                                           JS::TranscodeSources& sources,
                                           JS::OffThreadCompileCallback callback,
                                           void* callbackData) {
   auto task = cx->make_unique<MultiScriptsDecodeTask>(cx, sources, callback,
                                                       callbackData);
-  if (!task || !StartOffThreadParseTask(cx, task.get(), options)) {
+  if (!task) {
     return false;
   }
 
-  Unused << task.release();
-  return true;
+  return StartOffThreadParseTask(cx, std::move(task), options);
 }
 
 #if defined(JS_BUILD_BINAST)
 
 bool js::StartOffThreadDecodeBinAST(JSContext* cx,
                                     const ReadOnlyCompileOptions& options,
                                     const uint8_t* buf, size_t length,
                                     JS::OffThreadCompileCallback callback,
                                     void* callbackData) {
   if (!cx->runtime()->binast().ensureBinTablesInitialized(cx)) {
     return false;
   }
 
   auto task = cx->make_unique<BinASTDecodeTask>(cx, buf, length, callback,
                                                 callbackData);
-  if (!task || !StartOffThreadParseTask(cx, task.get(), options)) {
+  if (!task) {
     return false;
   }
 
-  Unused << task.release();
-  return true;
+  return StartOffThreadParseTask(cx, std::move(task), options);
 }
 
 #endif /* JS_BUILD_BINAST */
 
 void js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt) {
   MOZ_ASSERT(!OffThreadParsingMustWaitForGC(rt));
 
   GlobalHelperThreadState::ParseTaskVector newTasks;
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -13,16 +13,17 @@
 #ifndef vm_HelperThreads_h
 #define vm_HelperThreads_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/Utf8.h"  // mozilla::Utf8Unit
 #include "mozilla/Variant.h"
 
 #include "jsapi.h"
 
 #include "ds/Fifo.h"
 #include "jit/Ion.h"
 #include "js/CompileOptions.h"
 #include "js/SourceText.h"
@@ -606,22 +607,32 @@ void CancelOffThreadParses(JSRuntime* ru
  * Start a parse/emit cycle for a stream of source. The characters must stay
  * alive until the compilation finishes.
  */
 bool StartOffThreadParseScript(JSContext* cx,
                                const JS::ReadOnlyCompileOptions& options,
                                JS::SourceText<char16_t>& srcBuf,
                                JS::OffThreadCompileCallback callback,
                                void* callbackData);
+bool StartOffThreadParseScript(JSContext* cx,
+                               const JS::ReadOnlyCompileOptions& options,
+                               JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+                               JS::OffThreadCompileCallback callback,
+                               void* callbackData);
 
 bool StartOffThreadParseModule(JSContext* cx,
                                const JS::ReadOnlyCompileOptions& options,
                                JS::SourceText<char16_t>& srcBuf,
                                JS::OffThreadCompileCallback callback,
                                void* callbackData);
+bool StartOffThreadParseModule(JSContext* cx,
+                               const JS::ReadOnlyCompileOptions& options,
+                               JS::SourceText<mozilla::Utf8Unit>& srcBuf,
+                               JS::OffThreadCompileCallback callback,
+                               void* callbackData);
 
 bool StartOffThreadDecodeScript(JSContext* cx,
                                 const JS::ReadOnlyCompileOptions& options,
                                 const JS::TranscodeRange& range,
                                 JS::OffThreadCompileCallback callback,
                                 void* callbackData);
 
 #if defined(JS_BUILD_BINAST)
@@ -738,32 +749,16 @@ struct ParseTask : public mozilla::Linke
   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
   size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
   }
 
   void runTask() override;
 };
 
-struct ScriptParseTask : public ParseTask {
-  JS::SourceText<char16_t> data;
-
-  ScriptParseTask(JSContext* cx, JS::SourceText<char16_t>& srcBuf,
-                  JS::OffThreadCompileCallback callback, void* callbackData);
-  void parse(JSContext* cx) override;
-};
-
-struct ModuleParseTask : public ParseTask {
-  JS::SourceText<char16_t> data;
-
-  ModuleParseTask(JSContext* cx, JS::SourceText<char16_t>& srcBuf,
-                  JS::OffThreadCompileCallback callback, void* callbackData);
-  void parse(JSContext* cx) override;
-};
-
 struct ScriptDecodeTask : public ParseTask {
   const JS::TranscodeRange range;
 
   ScriptDecodeTask(JSContext* cx, const JS::TranscodeRange& range,
                    JS::OffThreadCompileCallback callback, void* callbackData);
   void parse(JSContext* cx) override;
 };
 
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -79,17 +79,17 @@ class Shape;
 namespace frontend {
 struct BytecodeEmitter;
 class FunctionBox;
 class ModuleSharedContext;
 }  // namespace frontend
 
 namespace gc {
 void SweepLazyScripts(GCParallelTask* task);
-} // namespace gc
+}  // namespace gc
 
 namespace detail {
 
 // Do not call this directly! It is exposed for the friend declarations in
 // this file.
 JSScript* CopyScript(JSContext* cx, HandleScript src,
                      HandleScriptSourceObject sourceObject,
                      MutableHandle<GCVector<Scope*>> scopes);
--- a/js/src/vm/Modules.cpp
+++ b/js/src/vm/Modules.cpp
@@ -4,32 +4,35 @@
  * 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/. */
 
 /* JavaScript modules (as in, the syntactic construct) implementation. */
 
 #include "js/Modules.h"
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
+#include "mozilla/Utf8.h"        // mozilla::Utf8Unit
 
 #include <stdint.h>  // uint32_t
 
 #include "jsapi.h"    // js::AssertHeapIsIdle
 #include "jstypes.h"  // JS_PUBLIC_API
 
 #include "builtin/ModuleObject.h"  // js::FinishDynamicModuleImport, js::{,Requested}ModuleObject
 #include "frontend/BytecodeCompiler.h"  // js::frontend::CompileModule
 #include "js/RootingAPI.h"              // JS::MutableHandle
 #include "js/Value.h"                   // JS::Value
 #include "vm/JSContext.h"               // CHECK_THREAD, JSContext
 #include "vm/JSObject.h"                // JSObject
 #include "vm/Runtime.h"                 // JSRuntime
 
 #include "vm/JSContext-inl.h"  // JSContext::{c,releaseC}heck
 
+using mozilla::Utf8Unit;
+
 using js::AssertHeapIsIdle;
 using js::ModuleObject;
 using js::RequestedModuleObject;
 
 JS_PUBLIC_API JS::ModuleResolveHook JS::GetModuleResolveHook(JSRuntime* rt) {
   AssertHeapIsIdle();
 
   return rt->moduleResolveHook;
@@ -75,26 +78,37 @@ JS_PUBLIC_API bool JS::FinishDynamicModu
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
   cx->check(referencingPrivate, promise);
 
   return js::FinishDynamicModuleImport(cx, referencingPrivate, specifier,
                                        promise);
 }
 
-JS_PUBLIC_API bool JS::CompileModule(JSContext* cx,
-                                     const ReadOnlyCompileOptions& options,
-                                     SourceText<char16_t>& srcBuf,
-                                     MutableHandle<JSObject*> module) {
+template <typename Unit>
+static JSObject* CompileModuleHelper(JSContext* cx,
+                                     const JS::ReadOnlyCompileOptions& options,
+                                     JS::SourceText<Unit>& srcBuf) {
   MOZ_ASSERT(!cx->zone()->isAtomsZone());
   AssertHeapIsIdle();
   CHECK_THREAD(cx);
 
-  module.set(js::frontend::CompileModule(cx, options, srcBuf));
-  return !!module;
+  return js::frontend::CompileModule(cx, options, srcBuf);
+}
+
+JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx,
+                                          const ReadOnlyCompileOptions& options,
+                                          SourceText<char16_t>& srcBuf) {
+  return CompileModuleHelper(cx, options, srcBuf);
+}
+
+JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx,
+                                          const ReadOnlyCompileOptions& options,
+                                          SourceText<Utf8Unit>& srcBuf) {
+  return CompileModuleHelper(cx, options, srcBuf);
 }
 
 JS_PUBLIC_API void JS::SetModulePrivate(JSObject* module, const Value& value) {
   JSRuntime* rt = module->zone()->runtimeFromMainThread();
   module->as<ModuleObject>().scriptSourceObject()->setPrivate(rt, value);
 }
 
 JS_PUBLIC_API JS::Value JS::GetModulePrivate(JSObject* module) {
--- a/js/src/vm/OffThreadScriptCompilation.cpp
+++ b/js/src/vm/OffThreadScriptCompilation.cpp
@@ -3,30 +3,33 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/OffThreadScriptCompilation.h"
 
 #include "mozilla/Assertions.h"  // MOZ_ASSERT
 #include "mozilla/Range.h"       // mozilla::Range
+#include "mozilla/Utf8.h"        // mozilla::Utf8Unit
 #include "mozilla/Vector.h"      // mozilla::Vector
 
 #include <stddef.h>  // size_t
 
 #include "jspubtd.h"  // js::CurrentThreadCanAccessRuntime
 #include "jstypes.h"  // JS_PUBLIC_API
 
 #include "js/CompileOptions.h"  // JS::ReadOnlyCompileOptions
 #include "vm/HelperThreads.h"  // js::OffThreadParsingMustWaitForGC, js::StartOffThreadParseScript
 #include "vm/JSContext.h"  // JSContext
 #include "vm/Runtime.h"    // js::CanUseExtraThreads
 
 using namespace js;
 
+using mozilla::Utf8Unit;
+
 using JS::ReadOnlyCompileOptions;
 
 enum class OffThread { Compile, Decode, DecodeBinAST };
 
 static bool CanDoOffThread(JSContext* cx, const ReadOnlyCompileOptions& options,
                            size_t length, OffThread what) {
   static const size_t TINY_LENGTH = 5 * 1000;
   static const size_t HUGE_SRC_LENGTH = 100 * 1000;
@@ -84,16 +87,25 @@ JS_PUBLIC_API bool JS::CompileOffThread(
                                         const ReadOnlyCompileOptions& options,
                                         JS::SourceText<char16_t>& srcBuf,
                                         OffThreadCompileCallback callback,
                                         void* callbackData) {
   MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
   return StartOffThreadParseScript(cx, options, srcBuf, callback, callbackData);
 }
 
+JS_PUBLIC_API bool JS::CompileOffThread(JSContext* cx,
+                                        const ReadOnlyCompileOptions& options,
+                                        JS::SourceText<Utf8Unit>& srcBuf,
+                                        OffThreadCompileCallback callback,
+                                        void* callbackData) {
+  MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
+  return StartOffThreadParseScript(cx, options, srcBuf, callback, callbackData);
+}
+
 JS_PUBLIC_API JSScript* JS::FinishOffThreadScript(JSContext* cx,
                                                   JS::OffThreadToken* token) {
   MOZ_ASSERT(cx);
   MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
   return HelperThreadState().finishScriptParseTask(cx, token);
 }
 
 JS_PUBLIC_API void JS::CancelOffThreadScript(JSContext* cx,
@@ -107,16 +119,24 @@ JS_PUBLIC_API void JS::CancelOffThreadSc
 JS_PUBLIC_API bool JS::CompileOffThreadModule(
     JSContext* cx, const ReadOnlyCompileOptions& options,
     JS::SourceText<char16_t>& srcBuf, OffThreadCompileCallback callback,
     void* callbackData) {
   MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
   return StartOffThreadParseModule(cx, options, srcBuf, callback, callbackData);
 }
 
+JS_PUBLIC_API bool JS::CompileOffThreadModule(
+    JSContext* cx, const ReadOnlyCompileOptions& options,
+    JS::SourceText<Utf8Unit>& srcBuf, OffThreadCompileCallback callback,
+    void* callbackData) {
+  MOZ_ASSERT(CanCompileOffThread(cx, options, srcBuf.length()));
+  return StartOffThreadParseModule(cx, options, srcBuf, callback, callbackData);
+}
+
 JS_PUBLIC_API JSObject* JS::FinishOffThreadModule(JSContext* cx,
                                                   JS::OffThreadToken* token) {
   MOZ_ASSERT(cx);
   MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
   return HelperThreadState().finishModuleParseTask(cx, token);
 }
 
 JS_PUBLIC_API void JS::CancelOffThreadModule(JSContext* cx,
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -530,19 +530,17 @@ struct JSRuntime : public js::MallocProv
 
   // Number of debuggee realms in the runtime observing code coverage.
   js::MainThreadData<size_t> numDebuggeeRealmsObservingCoverage_;
 
  public:
   void incrementNumDebuggeeRealms();
   void decrementNumDebuggeeRealms();
 
-  size_t numDebuggeeRealms() const {
-    return numDebuggeeRealms_;
-  }
+  size_t numDebuggeeRealms() const { return numDebuggeeRealms_; }
 
   void incrementNumDebuggeeRealmsObservingCoverage();
   void decrementNumDebuggeeRealmsObservingCoverage();
 
   /* Locale-specific callbacks for string conversion. */
   js::MainThreadData<const JSLocaleCallbacks*> localeCallbacks;
 
   /* Default locale for Internationalization API */
--- a/js/src/vm/SavedFrame.h
+++ b/js/src/vm/SavedFrame.h
@@ -101,18 +101,17 @@ class SavedFrame : public NativeObject {
     RootedRange(JSContext* cx, HandleSavedFrame frame) : frame_(cx, frame) {}
     RootedIterator begin() { return RootedIterator(*this); }
     RootedIterator end() { return RootedIterator(); }
   };
 
   struct Lookup;
   struct HashPolicy;
 
-  typedef JS::GCHashSet<WeakHeapPtr<SavedFrame*>, HashPolicy,
-                        SystemAllocPolicy>
+  typedef JS::GCHashSet<WeakHeapPtr<SavedFrame*>, HashPolicy, SystemAllocPolicy>
       Set;
 
  private:
   static SavedFrame* create(JSContext* cx);
   static MOZ_MUST_USE bool finishSavedFrameInit(JSContext* cx,
                                                 HandleObject ctor,
                                                 HandleObject proto);
   void initFromLookup(JSContext* cx, Handle<Lookup> lookup);
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1090,18 +1090,19 @@ JS_PUBLIC_API bool IsUnwrappedSavedFrame
 
 static bool AssignProperty(JSContext* cx, HandleObject dst, HandleObject src,
                            const char* property) {
   RootedValue v(cx);
   return JS_GetProperty(cx, src, property, &v) &&
          JS_DefineProperty(cx, dst, property, v, JSPROP_ENUMERATE);
 }
 
-JS_PUBLIC_API JSObject* ConvertSavedFrameToPlainObject
-    (JSContext* cx, HandleObject savedFrameArg, SavedFrameSelfHosted selfHosted) {
+JS_PUBLIC_API JSObject* ConvertSavedFrameToPlainObject(
+    JSContext* cx, HandleObject savedFrameArg,
+    SavedFrameSelfHosted selfHosted) {
   MOZ_ASSERT(savedFrameArg);
 
   RootedObject savedFrame(cx, savedFrameArg);
   RootedObject baseConverted(cx), lastConverted(cx);
   RootedValue v(cx);
 
   baseConverted = lastConverted = JS_NewObject(cx, nullptr);
   if (!baseConverted) {
@@ -1114,17 +1115,17 @@ JS_PUBLIC_API JSObject* ConvertSavedFram
         !AssignProperty(cx, lastConverted, savedFrame, "sourceId") ||
         !AssignProperty(cx, lastConverted, savedFrame, "line") ||
         !AssignProperty(cx, lastConverted, savedFrame, "column") ||
         !AssignProperty(cx, lastConverted, savedFrame, "functionDisplayName") ||
         !AssignProperty(cx, lastConverted, savedFrame, "asyncCause")) {
       return nullptr;
     }
 
-    const char* parentProperties[] = { "parent", "asyncParent" };
+    const char* parentProperties[] = {"parent", "asyncParent"};
     foundParent = false;
     for (const char* prop : parentProperties) {
       if (!JS_GetProperty(cx, savedFrame, prop, &v)) {
         return nullptr;
       }
       if (v.isObject()) {
         RootedObject nextConverted(cx, JS_NewObject(cx, nullptr));
         if (!nextConverted ||
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -891,18 +891,18 @@ struct DefaultHasher<jsid> {
   static bool match(jsid id1, jsid id2) { return id1 == id2; }
 };
 
 }  // namespace mozilla
 
 namespace js {
 
 using BaseShapeSet =
-    JS::WeakCache<JS::GCHashSet<WeakHeapPtr<UnownedBaseShape*>,
-                                StackBaseShape, SystemAllocPolicy>>;
+    JS::WeakCache<JS::GCHashSet<WeakHeapPtr<UnownedBaseShape*>, StackBaseShape,
+                                SystemAllocPolicy>>;
 
 class Shape : public gc::TenuredCell {
   friend class ::JSObject;
   friend class ::JSFunction;
   friend class NativeObject;
   friend class PropertyTree;
   friend class TenuringTracer;
   friend struct StackBaseShape;
--- a/js/src/vm/StringType-inl.h
+++ b/js/src/vm/StringType-inl.h
@@ -432,18 +432,18 @@ inline void JSFlatString::finalize(js::F
     fop->free_(this, nonInlineCharsRaw(), allocSize(),
                js::MemoryUse::StringContents);
   }
 }
 
 inline size_t JSFlatString::allocSize() const {
   MOZ_ASSERT(!isInline());
 
-  size_t charSize = hasLatin1Chars() ? sizeof(JS::Latin1Char)
-                                     : sizeof(char16_t);
+  size_t charSize =
+      hasLatin1Chars() ? sizeof(JS::Latin1Char) : sizeof(char16_t);
   size_t count = isExtensible() ? asExtensible().capacity() : length();
   return (count + 1) * charSize;
 }
 
 inline void JSFatInlineString::finalize(js::FreeOp* fop) {
   MOZ_ASSERT(getAllocKind() == js::gc::AllocKind::FAT_INLINE_STRING);
   MOZ_ASSERT(isInline());
 
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -572,18 +572,17 @@ JSFlatString* JSRope::flattenInternal(JS
       }
       str->setNonInlineChars(wholeChars);
       uint32_t left_len = left.length();
       pos = wholeChars + left_len;
 
       // Remove memory association for left node we're about to make into a
       // dependent string.
       if (left.isTenured()) {
-        RemoveCellMemory(&left, left.allocSize(),
-                         MemoryUse::StringContents);
+        RemoveCellMemory(&left, left.allocSize(), MemoryUse::StringContents);
       }
 
       if (IsSame<CharT, char16_t>::value) {
         left.setLengthAndFlags(left_len, DEPENDENT_FLAGS);
       } else {
         left.setLengthAndFlags(left_len, DEPENDENT_FLAGS | LATIN1_CHARS_BIT);
       }
       left.d.s.u3.base = (JSLinearString*)this; /* will be true on exit */
@@ -660,18 +659,17 @@ finish_node : {
       str->setLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS);
     } else {
       str->setLengthAndFlags(wholeLength, EXTENSIBLE_FLAGS | LATIN1_CHARS_BIT);
     }
     str->setNonInlineChars(wholeChars);
     str->d.s.u3.capacity = wholeCapacity;
 
     if (str->isTenured()) {
-      AddCellMemory(str, str->asFlat().allocSize(),
-                    MemoryUse::StringContents);
+      AddCellMemory(str, str->asFlat().allocSize(), MemoryUse::StringContents);
     }
 
     return &this->asFlat();
   }
   uintptr_t flattenData;
   uint32_t len = pos - str->nonInlineCharsRaw<CharT>();
   if (IsSame<CharT, char16_t>::value) {
     flattenData = str->unsetFlattenData(len, DEPENDENT_FLAGS);
@@ -1471,18 +1469,17 @@ JSFlatString* JSExternalString::ensureFl
     AutoCheckCannotGC nogc;
     FillAndTerminate(s.get(), nonInlineChars<char16_t>(nogc), n);
   }
 
   // Release the external chars.
   finalize(cx->runtime()->defaultFreeOp());
 
   MOZ_ASSERT(isTenured());
-  AddCellMemory(this, (n + 1) * sizeof(char16_t),
-                MemoryUse::StringContents);
+  AddCellMemory(this, (n + 1) * sizeof(char16_t), MemoryUse::StringContents);
 
   // Transform the string into a non-external, flat string. Note that the
   // resulting string will still be in an AllocKind::EXTERNAL_STRING arena,
   // but will no longer be an external string.
   setLengthAndFlags(n, INIT_FLAT_FLAGS);
   setNonInlineChars<char16_t>(s.release());
 
   return &this->asFlat();
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -567,17 +567,16 @@ static PropertyName* DotMember(ParseNode
 static ParseNode* ElemBase(ParseNode* pn) {
   return &pn->as<PropertyByValue>().expression();
 }
 
 static ParseNode* ElemIndex(ParseNode* pn) {
   return &pn->as<PropertyByValue>().key();
 }
 
-
 static inline PropertyName* FunctionName(FunctionNode* funNode) {
   if (JSAtom* name = funNode->funbox()->explicitName()) {
     return name->asPropertyName();
   }
   return nullptr;
 }
 
 static inline ParseNode* FunctionStatementList(FunctionNode* funNode) {
@@ -6029,17 +6028,18 @@ static bool ParseFunction(ModuleValidato
   ParseContext* outerpc = m.parser().pc_;
   Directives directives(outerpc);
   FunctionBox* funbox = m.parser().newFunctionBox(
       funNode, fun, toStringStart, directives, GeneratorKind::NotGenerator,
       FunctionAsyncKind::SyncFunction);
   if (!funbox) {
     return false;
   }
-  funbox->initWithEnclosingParseContext(outerpc, fun, FunctionSyntaxKind::Statement);
+  funbox->initWithEnclosingParseContext(outerpc, fun,
+                                        FunctionSyntaxKind::Statement);
 
   Directives newDirectives = directives;
   SourceParseContext funpc(&m.parser(), funbox, &newDirectives);
   if (!funpc.init()) {
     return false;
   }
 
   if (!m.parser().functionFormalParametersAndBody(
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -287,19 +287,20 @@ class WasmMemoryObject : public NativeOb
   static const ClassOps classOps_;
   static void finalize(FreeOp* fop, JSObject* obj);
   static bool bufferGetterImpl(JSContext* cx, const CallArgs& args);
   static bool bufferGetter(JSContext* cx, unsigned argc, Value* vp);
   static bool growImpl(JSContext* cx, const CallArgs& args);
   static bool grow(JSContext* cx, unsigned argc, Value* vp);
   static uint32_t growShared(HandleWasmMemoryObject memory, uint32_t delta);
 
-  using InstanceSet = JS::WeakCache<GCHashSet<
-      WeakHeapPtrWasmInstanceObject,
-      MovableCellHasher<WeakHeapPtrWasmInstanceObject>, SystemAllocPolicy>>;
+  using InstanceSet =
+      JS::WeakCache<GCHashSet<WeakHeapPtrWasmInstanceObject,
+                              MovableCellHasher<WeakHeapPtrWasmInstanceObject>,
+                              SystemAllocPolicy>>;
   bool hasObservers() const;
   InstanceSet& observers() const;
   InstanceSet* getOrCreateObservers(JSContext* cx);
 
  public:
   static const unsigned RESERVED_SLOTS = 2;
   static const Class class_;
   static const JSPropertySpec properties[];
--- a/js/src/wasm/WasmTable.h
+++ b/js/src/wasm/WasmTable.h
@@ -36,19 +36,20 @@ namespace wasm {
 
 // TODO/AnyRef-boxing: With boxed immediates and strings, JSObject* is no longer
 // the most appropriate representation for Cell::anyref.
 STATIC_ASSERT_ANYREF_IS_JSOBJECT;
 
 typedef GCVector<HeapPtr<JSObject*>, 0, SystemAllocPolicy> TableAnyRefVector;
 
 class Table : public ShareableBase<Table> {
-  using InstanceSet = JS::WeakCache<GCHashSet<
-      WeakHeapPtrWasmInstanceObject,
-      MovableCellHasher<WeakHeapPtrWasmInstanceObject>, SystemAllocPolicy>>;
+  using InstanceSet =
+      JS::WeakCache<GCHashSet<WeakHeapPtrWasmInstanceObject,
+                              MovableCellHasher<WeakHeapPtrWasmInstanceObject>,
+                              SystemAllocPolicy>>;
   using UniqueFuncRefArray = UniquePtr<FunctionTableElem[], JS::FreePolicy>;
 
   WeakHeapPtrWasmTableObject maybeObject_;
   InstanceSet observers_;
   UniqueFuncRefArray functions_;  // either functions_ has data
   TableAnyRefVector objects_;     //   or objects_, but not both
   const TableKind kind_;
   uint32_t length_;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1822,20 +1822,19 @@ static void ReportRealmStats(const JS::R
       realmJSPathPrefix + NS_LITERAL_CSTRING("baseline/fallback-stubs"),
       realmStats.baselineStubsFallback,
       "The Baseline JIT's fallback IC stubs (excluding code).");
 
   ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("ion-data"),
                  realmStats.ionData,
                  "The IonMonkey JIT's compilation data (IonScripts).");
 
-  ZRREPORT_BYTES(
-      realmJSPathPrefix + NS_LITERAL_CSTRING("jit-scripts"),
-      realmStats.jitScripts,
-      "JIT and Type Inference data associated with scripts.");
+  ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("jit-scripts"),
+                 realmStats.jitScripts,
+                 "JIT and Type Inference data associated with scripts.");
 
   ZRREPORT_BYTES(
       realmJSPathPrefix +
           NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
       realmStats.typeInferenceAllocationSiteTables,
       "Tables of type objects associated with allocation sites.");
 
   ZRREPORT_BYTES(realmJSPathPrefix +
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -281,17 +281,17 @@ void RestyleManager::CharacterDataChange
     // child of the parent element. Fortunately, it's uncommon to have such
     // nodes and this not being an append.
     //
     // See the testcase in bug 1427625 for a test-case that triggers this.
     RestyleForInsertOrChange(aContent);
     return;
   }
 
-  const nsTextFragment* text = aContent->GetText();
+  const nsTextFragment* text = &aContent->AsText()->TextFragment();
 
   const size_t oldLength = aInfo.mChangeStart;
   const size_t newLength = text->GetLength();
 
   const bool emptyChanged = !oldLength && newLength;
 
   const bool whitespaceOnlyChanged =
       text->Is2b()
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -1272,20 +1272,20 @@ bool nsBidiPresUtils::ChildListMayRequir
         // we might need to remove the property set by a previous reflow, if
         // content has changed; see bug 1366623).
         if (frame->HasProperty(nsIFrame::BidiDataProperty())) {
           return true;
         }
 
         // Check whether the text frame has any RTL characters; if so, bidi
         // resolution will be needed.
-        nsIContent* content = frame->GetContent();
+        dom::Text* content = frame->GetContent()->AsText();
         if (content != *aCurrContent) {
           *aCurrContent = content;
-          const nsTextFragment* txt = content->GetText();
+          const nsTextFragment* txt = &content->TextFragment();
           if (txt->Is2b() &&
               HasRTLChars(MakeSpan(txt->Get2b(), txt->GetLength()))) {
             return true;
           }
         }
       }
     } else if (ChildListMayRequireBidi(frame->PrincipalChildList().FirstChild(),
                                        aCurrContent)) {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -9984,45 +9984,34 @@ static int32_t FirstLetterCount(const ns
       count++;
       break;
     }
   }
 
   return count;
 }
 
-static bool NeedFirstLetterContinuation(nsIContent* aContent) {
-  MOZ_ASSERT(aContent, "null ptr");
-
-  bool result = false;
-  if (aContent) {
-    const nsTextFragment* frag = aContent->GetText();
-    if (frag) {
-      int32_t flc = FirstLetterCount(frag);
-      int32_t tl = frag->GetLength();
-      if (flc < tl) {
-        result = true;
-      }
-    }
-  }
-  return result;
-}
-
-static bool IsFirstLetterContent(nsIContent* aContent) {
-  return aContent->TextLength() && !aContent->TextIsOnlyWhitespace();
+static bool NeedFirstLetterContinuation(Text* aText) {
+  MOZ_ASSERT(aText, "null ptr");
+  int32_t flc = FirstLetterCount(&aText->TextFragment());
+  int32_t tl = aText->TextDataLength();
+  return flc < tl;
+}
+
+static bool IsFirstLetterContent(Text* aText) {
+  return aText->TextDataLength() && !aText->TextIsOnlyWhitespace();
 }
 
 /**
  * Create a letter frame, only make it a floating frame.
  */
 nsFirstLetterFrame* nsCSSFrameConstructor::CreateFloatingLetterFrame(
-    nsFrameConstructorState& aState, nsIContent* aTextContent,
-    nsIFrame* aTextFrame, nsContainerFrame* aParentFrame,
-    ComputedStyle* aParentComputedStyle, ComputedStyle* aComputedStyle,
-    nsFrameList& aResult) {
+    nsFrameConstructorState& aState, Text* aTextContent, nsIFrame* aTextFrame,
+    nsContainerFrame* aParentFrame, ComputedStyle* aParentComputedStyle,
+    ComputedStyle* aComputedStyle, nsFrameList& aResult) {
   MOZ_ASSERT(aParentComputedStyle);
 
   nsFirstLetterFrame* letterFrame =
       NS_NewFirstLetterFrame(mPresShell, aComputedStyle);
   // We don't want to use a text content for a non-text frame (because we want
   // its primary frame to be a text frame).
   nsIContent* letterContent = aParentFrame->GetContent();
   nsContainerFrame* containingBlock =
@@ -10076,19 +10065,17 @@ nsFirstLetterFrame* nsCSSFrameConstructo
 }
 
 /**
  * Create a new letter frame for aTextFrame. The letter frame will be
  * a child of aParentFrame.
  */
 void nsCSSFrameConstructor::CreateLetterFrame(
     nsContainerFrame* aBlockFrame, nsContainerFrame* aBlockContinuation,
-    nsIContent* aTextContent, nsContainerFrame* aParentFrame,
-    nsFrameList& aResult) {
-  MOZ_ASSERT(aTextContent->IsText(), "aTextContent isn't text");
+    Text* aTextContent, nsContainerFrame* aParentFrame, nsFrameList& aResult) {
   NS_ASSERTION(aBlockFrame->IsBlockFrameOrSubclass(), "Not a block frame?");
 
   // Get a ComputedStyle for the first-letter-frame.
   //
   // Keep this in sync with nsBlockFrame::UpdatePseudoElementStyles.
   nsIFrame* parentFrame = nsFrame::CorrectStyleParentFrame(
       aParentFrame, PseudoStyleType::firstLetter);
 
@@ -10212,17 +10199,17 @@ void nsCSSFrameConstructor::WrapFramesIn
   nsIFrame* frame = aParentFrameList;
 
   while (frame) {
     nsIFrame* nextFrame = frame->GetNextSibling();
 
     LayoutFrameType frameType = frame->Type();
     if (LayoutFrameType::Text == frameType) {
       // Wrap up first-letter content in a letter frame
-      nsIContent* textContent = frame->GetContent();
+      Text* textContent = frame->GetContent()->AsText();
       if (IsFirstLetterContent(textContent)) {
         // Create letter frame to wrap up the text
         CreateLetterFrame(aBlockFrame, aBlockContinuation, textContent,
                           aParentFrame, aLetterFrames);
 
         // Provide adjustment information for parent
         *aModifiedParent = aParentFrame;
         *aTextFrame = frame;
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1915,24 +1915,24 @@ class nsCSSFrameConstructor final : publ
 
   void ReframeContainingBlock(nsIFrame* aFrame);
 
   //----------------------------------------
 
   // Methods support :first-letter style
 
   nsFirstLetterFrame* CreateFloatingLetterFrame(
-      nsFrameConstructorState& aState, nsIContent* aTextContent,
+      nsFrameConstructorState& aState, mozilla::dom::Text* aTextContent,
       nsIFrame* aTextFrame, nsContainerFrame* aParentFrame,
       ComputedStyle* aParentComputedStyle, ComputedStyle* aComputedStyle,
       nsFrameList& aResult);
 
   void CreateLetterFrame(nsContainerFrame* aBlockFrame,
                          nsContainerFrame* aBlockContinuation,
-                         nsIContent* aTextContent,
+                         mozilla::dom::Text* aTextContent,
                          nsContainerFrame* aParentFrame, nsFrameList& aResult);
 
   void WrapFramesInFirstLetterFrame(nsContainerFrame* aBlockFrame,
                                     nsFrameList& aBlockFrames);
 
   /**
    * Looks in the block aBlockFrame for a text frame that contains the
    * first-letter of the block and creates the necessary first-letter frames
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -9358,17 +9358,17 @@ void nsLayoutUtils::GetFrameTextContent(
 
 /* static */
 void nsLayoutUtils::AppendFrameTextContent(nsIFrame* aFrame,
                                            nsAString& aResult) {
   if (aFrame->IsTextFrame()) {
     auto textFrame = static_cast<nsTextFrame*>(aFrame);
     auto offset = textFrame->GetContentOffset();
     auto length = textFrame->GetContentLength();
-    textFrame->GetContent()->GetText()->AppendTo(aResult, offset, length);
+    textFrame->TextFragment()->AppendTo(aResult, offset, length);
   } else {
     for (nsIFrame* child : aFrame->PrincipalChildList()) {
       AppendFrameTextContent(child, aResult);
     }
   }
 }
 
 /* static */
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -885,18 +885,17 @@ nsresult nsTextControlFrame::SelectAllOr
         // Editor won't remove text node when empty value.
         --numChildren;
       }
     }
     if (!aSelect && numChildren) {
       child = child->GetPreviousSibling();
       if (child && child->IsText()) {
         rootNode = child;
-        const nsTextFragment* fragment = child->GetText();
-        numChildren = fragment ? fragment->GetLength() : 0;
+        numChildren = child->AsText()->TextDataLength();
       }
     }
   }
 
   rv = SetSelectionInternal(rootNode, aSelect ? 0 : numChildren, rootNode,
                             numChildren);
   NS_ENSURE_SUCCESS(rv, rv);
 
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1553824.html
@@ -0,0 +1,86 @@
+<body onload="test()">
+<script>
+function test() {
+  document.body.offsetWidth;
+  let grid = document.querySelector(".container");
+  grid.style = "grid-template-columns:none";
+}
+</script>
+
+<style>
+body {
+  font: 1.2em Arial, Verdana, sans-serif;
+  background-color: #fff;
+}
+
+.container {
+  display: grid;
+  gap: 10px;
+  grid-template-columns: 1fr 1fr 1fr 1fr;
+  grid-template-rows: auto auto auto;
+}
+
+header,
+footer,
+aside,
+li,.box {
+  background-color: rgb(120, 70, 123);
+  border: 5px solid rgb(88, 55, 112);
+  color: #fff;
+  border-radius: 5px;
+  padding: 20px;
+}
+
+aside {
+  grid-row: 1 / -1;
+  grid-column: 1;
+}
+
+header {
+  grid-column: 2 / -2;
+}
+
+ul {
+  gap: 10px;
+  grid-row: 2;
+  grid-column: 2 / -1;
+  margin: 0;
+  padding: 0;
+  list-style: none;
+  display: grid;
+  grid-template-columns: subgrid;
+}
+
+footer {
+  grid-row: 3;
+  grid-column: 2 / -1;
+}
+
+.box {
+  grid-column: -2;
+  grid-row:1;
+}
+
+</style>
+
+
+<div class="container">
+  <header>This is my header</header>
+  <div class="box"></div>
+  <aside>I should stretch from the top to the bottom of the grid</aside>
+  <ul>
+    <li>A</li>
+    <li>B</li>
+    <li>C</li>
+    <li>D</li>
+    <li>E</li>
+    <li>F</li>
+    <li>G</li>
+    <li>H</li>
+    <li>I</li>
+    <li>J</li>
+    <li>K</li>
+  </ul>
+  <footer>I am a footer</footer>
+</div>
+
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -726,8 +726,9 @@ load 1515124.html
 pref(layout.css.column-span.enabled,true) load 1517033.html
 pref(layout.css.column-span.enabled,true) load 1517297.html
 load 1520798-1.xul
 load 1520798-2.html
 load 1539656.html
 load 1544060-1.html
 load 1544060-2.html
 load 1542441.html
+pref(layout.css.grid-template-subgrid-value.enabled,true) load 1553824.html
--- a/layout/generic/nsFontInflationData.cpp
+++ b/layout/generic/nsFontInflationData.cpp
@@ -253,17 +253,17 @@ void nsFontInflationData::UpdateISize(co
         continue;
       }
 
       if (kid->IsTextFrame()) {
         nsIContent* content = kid->GetContent();
         if (content && kid == content->GetPrimaryFrame()) {
           uint32_t len = nsTextFrameUtils::
               ComputeApproximateLengthWithWhitespaceCompression(
-                  content, kid->StyleText());
+                  content->AsText(), kid->StyleText());
           if (len != 0) {
             return kid;
           }
         }
       } else {
         nsIFrame* kidResult = FindEdgeInflatableFrameIn(kid, aDirection);
         if (kidResult) {
           return kidResult;
@@ -290,17 +290,18 @@ static uint32_t DoCharCountOfLargestOpti
       optionResult = DoCharCountOfLargestOption(option);
     } else {
       // REVIEW: Check the frame structure for this!
       optionResult = 0;
       for (nsIFrame* optionChild : option->PrincipalChildList()) {
         if (optionChild->IsTextFrame()) {
           optionResult += nsTextFrameUtils::
               ComputeApproximateLengthWithWhitespaceCompression(
-                  optionChild->GetContent(), optionChild->StyleText());
+                  optionChild->GetContent()->AsText(),
+                  optionChild->StyleText());
         }
       }
     }
     if (optionResult > result) {
       result = optionResult;
     }
   }
   return result;
@@ -329,17 +330,17 @@ void nsFontInflationData::ScanTextIn(nsI
       }
 
       LayoutFrameType fType = kid->Type();
       if (fType == LayoutFrameType::Text) {
         nsIContent* content = kid->GetContent();
         if (content && kid == content->GetPrimaryFrame()) {
           uint32_t len = nsTextFrameUtils::
               ComputeApproximateLengthWithWhitespaceCompression(
-                  content, kid->StyleText());
+                  content->AsText(), kid->StyleText());
           if (len != 0) {
             nscoord fontSize = kid->StyleFont()->mFont.size;
             if (fontSize > 0) {
               mTextAmount += fontSize * len;
             }
           }
         }
       } else if (fType == LayoutFrameType::TextInput) {
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -3857,17 +3857,17 @@ void nsGridContainerFrame::Grid::Subgrid
 }
 
 void nsGridContainerFrame::Grid::PlaceGridItems(
     GridReflowInput& aState, const RepeatTrackSizingInput& aSizes) {
   MOZ_ASSERT(mCellMap.mCells.IsEmpty(), "unexpected entries in cell map");
 
   mAreas = aState.mFrame->GetImplicitNamedAreas();
 
-  if (aState.mFrame->HasSubgridItems()) {
+  if (aState.mFrame->HasSubgridItems() || aState.mFrame->IsSubgrid()) {
     if (auto* uts = aState.mFrame->GetUsedTrackSizes()) {
       uts->mCanResolveLineRangeSize = {false, false};
       uts->mSizes[eLogicalAxisInline].ClearAndRetainStorage();
       uts->mSizes[eLogicalAxisBlock].ClearAndRetainStorage();
     }
   }
 
   // SubgridPlaceGridItems will set these if we find any subgrid items.
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -671,17 +671,17 @@ void GlyphObserver::NotifyGlyphsChanged(
   TextRunMappedFlow* userMappedFlows = GetMappedFlows(mTextRun);
   for (uint32_t i = 0; i < data->mMappedFlowCount; ++i) {
     InvalidateFrameDueToGlyphsChanged(userMappedFlows[i].mStartFrame);
   }
 }
 
 int32_t nsTextFrame::GetContentEnd() const {
   nsTextFrame* next = GetNextContinuation();
-  return next ? next->GetContentOffset() : mContent->GetText()->GetLength();
+  return next ? next->GetContentOffset() : TextFragment()->GetLength();
 }
 
 struct FlowLengthProperty {
   int32_t mStartOffset;
   // The offset of the next fixed continuation after mStartOffset, or
   // of the end of the text if there is none
   int32_t mEndFlowOffset;
 };
@@ -711,17 +711,17 @@ int32_t nsTextFrame::GetInFlowContentLen
     NS_ASSERTION(flowLength->mEndFlowOffset >= GetContentEnd(),
                  "frame crosses fixed continuation boundary");
 #endif
     return flowLength->mEndFlowOffset - mContentOffset;
   }
 
   nsTextFrame* nextBidi = LastInFlow()->GetNextContinuation();
   int32_t endFlow =
-      nextBidi ? nextBidi->GetContentOffset() : mContent->TextLength();
+      nextBidi ? nextBidi->GetContentOffset() : GetContent()->TextLength();
 
   if (!flowLength) {
     flowLength = new FlowLengthProperty;
     if (NS_FAILED(mContent->SetProperty(
             nsGkAtoms::flowlength, flowLength,
             nsINode::DeleteProperty<FlowLengthProperty>))) {
       delete flowLength;
       flowLength = nullptr;
@@ -1042,17 +1042,17 @@ class BuildTextRunsScanner {
     // ancestor of the elements containing the characters is the one whose
     // CSS 'white-space' property governs. So this records the nearest common
     // ancestor of mStartFrame and the previous text frame, or null if there
     // was no previous text frame on this line.
     nsIFrame* mAncestorControllingInitialBreak;
 
     int32_t GetContentEnd() {
       return mEndFrame ? mEndFrame->GetContentOffset()
-                       : mStartFrame->GetContent()->GetText()->GetLength();
+                       : mStartFrame->TextFragment()->GetLength();
     }
   };
 
   class BreakSink final : public nsILineBreakSink {
    public:
     BreakSink(gfxTextRun* aTextRun, DrawTarget* aDrawTarget,
               uint32_t aOffsetIntoTextRun)
         : mTextRun(aTextRun),
@@ -1301,17 +1301,17 @@ BuildTextRunsScanner::FindBoundaryResult
   }
 
   if (aFrame == aState->mStopAtFrame) return FB_STOPPED_AT_STOP_FRAME;
 
   if (textFrame) {
     if (aState->mSeenSpaceForLineBreakingOnThisLine) {
       return FB_CONTINUE;
     }
-    const nsTextFragment* frag = textFrame->GetContent()->GetText();
+    const nsTextFragment* frag = textFrame->TextFragment();
     uint32_t start = textFrame->GetContentOffset();
     uint32_t length = textFrame->GetContentLength();
     const void* text;
     if (frag->Is2b()) {
       // It is possible that we may end up removing all whitespace in
       // a piece of text because of The White Space Processing Rules,
       // so we need to transform it before we can check existence of
       // such whitespaces.
@@ -1679,17 +1679,17 @@ void BuildTextRunsScanner::AccumulateRun
     NS_ASSERTION(mMaxTextLength < UINT32_MAX - aFrame->GetContentLength(),
                  "integer overflow");
     if (mMaxTextLength >= UINT32_MAX - aFrame->GetContentLength()) {
       mMaxTextLength = UINT32_MAX;
     } else {
       mMaxTextLength += aFrame->GetContentLength();
     }
   }
-  mDoubleByteText |= aFrame->GetContent()->GetText()->Is2b();
+  mDoubleByteText |= aFrame->TextFragment()->Is2b();
   mLastFrame = aFrame;
   mCommonAncestorWithLastFrame = aFrame->GetParent();
 
   MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
   NS_ASSERTION(mappedFlow->mStartFrame == aFrame ||
                    mappedFlow->GetContentEnd() == aFrame->GetContentOffset(),
                "Overlapping or discontiguous frames => BAD");
   mappedFlow->mEndFrame = aFrame->GetNextContinuation();
@@ -1700,17 +1700,17 @@ void BuildTextRunsScanner::AccumulateRun
   if (mStartOfLine) {
     mLineBreakBeforeFrames.AppendElement(aFrame);
     mStartOfLine = false;
   }
 }
 
 static bool HasTerminalNewline(const nsTextFrame* aFrame) {
   if (aFrame->GetContentLength() == 0) return false;
-  const nsTextFragment* frag = aFrame->GetContent()->GetText();
+  const nsTextFragment* frag = aFrame->TextFragment();
   return frag->CharAt(aFrame->GetContentEnd() - 1) == '\n';
 }
 
 static gfxFont::Metrics GetFirstFontMetrics(gfxFontGroup* aFontGroup,
                                             bool aVerticalMetrics) {
   if (!aFontGroup) return gfxFont::Metrics();
   gfxFont* font = aFontGroup->GetFirstValidFont();
   return font->GetMetrics(aVerticalMetrics ? nsFontMetrics::eVertical
@@ -2239,17 +2239,17 @@ already_AddRefed<gfxTextRun> BuildTextRu
       oldScriptLevel = sstyScriptLevel;
     }
     if (sstyScriptLevel) {
       anyMathMLStyling = true;
     }
 
     // Figure out what content is included in this flow.
     nsIContent* content = f->GetContent();
-    const nsTextFragment* frag = content->GetText();
+    const nsTextFragment* frag = f->TextFragment();
     int32_t contentStart = mappedFlow->mStartFrame->GetContentOffset();
     int32_t contentEnd = mappedFlow->GetContentEnd();
     int32_t contentLength = contentEnd - contentStart;
 
     TextRunMappedFlow* newFlow = &userMappedFlows[i];
     newFlow->mStartFrame = mappedFlow->mStartFrame;
     newFlow->mDOMOffsetToBeforeTransformOffset =
         skipChars.GetOriginalCharCount() -
@@ -2518,18 +2518,17 @@ bool BuildTextRunsScanner::SetupLineBrea
     MappedFlow* mappedFlow = &mMappedFlows[i];
     nsTextFrame* f = mappedFlow->mStartFrame;
 
     const nsStyleText* textStyle = f->StyleText();
     nsTextFrameUtils::CompressionMode compression =
         GetCSSWhitespaceToCompressionMode(f, textStyle);
 
     // Figure out what content is included in this flow.
-    nsIContent* content = f->GetContent();
-    const nsTextFragment* frag = content->GetText();
+    const nsTextFragment* frag = f->TextFragment();
     int32_t contentStart = mappedFlow->mStartFrame->GetContentOffset();
     int32_t contentEnd = mappedFlow->GetContentEnd();
     int32_t contentLength = contentEnd - contentStart;
 
     nsTextFrameUtils::Flags analysisFlags;
     if (frag->Is2b()) {
       NS_ASSERTION(mDoubleByteText, "Wrong buffer char size!");
       char16_t* bufStart = static_cast<char16_t*>(textPtr);
@@ -2574,17 +2573,17 @@ bool BuildTextRunsScanner::SetupLineBrea
 
 static bool HasCompressedLeadingWhitespace(
     nsTextFrame* aFrame, const nsStyleText* aStyleText,
     int32_t aContentEndOffset, const gfxSkipCharsIterator& aIterator) {
   if (!aIterator.IsOriginalCharSkipped()) return false;
 
   gfxSkipCharsIterator iter = aIterator;
   int32_t frameContentOffset = aFrame->GetContentOffset();
-  const nsTextFragment* frag = aFrame->GetContent()->GetText();
+  const nsTextFragment* frag = aFrame->TextFragment();
   while (frameContentOffset < aContentEndOffset &&
          iter.IsOriginalCharSkipped()) {
     if (IsTrimmableSpace(frag, frameContentOffset, aStyleText)) return true;
     ++frameContentOffset;
     iter.AdvanceOriginal(1);
   }
   return false;
 }
@@ -3183,17 +3182,17 @@ class MOZ_STACK_CLASS PropertyProvider f
    */
   PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart,
                    nsTextFrame::TextRunType aWhichTextRun,
                    nsFontMetrics* aFontMetrics)
       : mTextRun(aFrame->GetTextRun(aWhichTextRun)),
         mFontGroup(nullptr),
         mFontMetrics(aFontMetrics),
         mTextStyle(aFrame->StyleText()),
-        mFrag(aFrame->GetContent()->GetText()),
+        mFrag(aFrame->TextFragment()),
         mLineContainer(nullptr),
         mFrame(aFrame),
         mStart(aStart),
         mTempIterator(aStart),
         mTabWidths(nullptr),
         mTabWidthsAnalyzedLimit(0),
         mLength(aFrame->GetContentLength()),
         mWordSpacing(WordSpacing(aFrame, mTextRun)),
@@ -4763,17 +4762,17 @@ void nsTextFrame::NotifyNativeAnonymousT
     f->mReflowRequestedForCharDataChange = true;
   }
 
   // Pretend that all the text changed.
   CharacterDataChangeInfo info;
   info.mAppend = false;
   info.mChangeStart = 0;
   info.mChangeEnd = aOldLength;
-  info.mReplaceLength = mContent->TextLength();
+  info.mReplaceLength = GetContent()->TextLength();
   CharacterDataChanged(info);
 }
 
 nsresult nsTextFrame::CharacterDataChanged(
     const CharacterDataChangeInfo& aInfo) {
   if (mContent->HasFlag(NS_HAS_NEWLINE_PROPERTY)) {
     mContent->DeleteProperty(nsGkAtoms::newline);
     mContent->UnsetFlags(NS_HAS_NEWLINE_PROPERTY);
@@ -7061,17 +7060,17 @@ nsIFrame::ContentOffsets nsTextFrame::Ge
     gfxSkipCharsIterator extraCluster(provider.GetStart());
     extraCluster.AdvanceSkipped(charsFit);
 
     bool allowSplitLigature = true;  // Allow selection of partial ligature...
 
     // ...but don't let selection/insertion-point split two Regional Indicator
     // chars that are ligated in the textrun to form a single flag symbol.
     uint32_t offs = extraCluster.GetOriginalOffset();
-    const nsTextFragment* frag = GetContent()->GetText();
+    const nsTextFragment* frag = TextFragment();
     if (offs + 1 < frag->GetLength() &&
         NS_IS_HIGH_SURROGATE(frag->CharAt(offs)) &&
         NS_IS_LOW_SURROGATE(frag->CharAt(offs + 1)) &&
         gfxFontUtils::IsRegionalIndicator(
             SURROGATE_TO_UCS4(frag->CharAt(offs), frag->CharAt(offs + 1)))) {
       allowSplitLigature = false;
       if (extraCluster.GetSkippedOffset() > 1 &&
           !mTextRun->IsLigatureGroupStart(extraCluster.GetSkippedOffset())) {
@@ -7478,17 +7477,17 @@ nsresult nsTextFrame::GetChildFrameConta
 nsIFrame::FrameSearchResult nsTextFrame::PeekOffsetNoAmount(bool aForward,
                                                             int32_t* aOffset) {
   NS_ASSERTION(aOffset && *aOffset <= GetContentLength(),
                "aOffset out of range");
 
   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
   if (!mTextRun) return CONTINUE_EMPTY;
 
-  TrimmedOffsets trimmed = GetTrimmedOffsets(mContent->GetText());
+  TrimmedOffsets trimmed = GetTrimmedOffsets(TextFragment());
   // Check whether there are nonskipped characters in the trimmmed range
   return (iter.ConvertOriginalToSkipped(trimmed.GetEnd()) >
           iter.ConvertOriginalToSkipped(trimmed.mStart))
              ? FOUND
              : CONTINUE;
 }
 
 /**
@@ -7539,29 +7538,29 @@ class MOZ_STACK_CLASS ClusterIterator {
   nsTextFrame::TrimmedOffsets mTrimmed;
   nsTArray<bool> mWordBreaks;
   bool mHaveWordBreak;
 };
 
 static bool IsAcceptableCaretPosition(const gfxSkipCharsIterator& aIter,
                                       bool aRespectClusters,
                                       const gfxTextRun* aTextRun,
-                                      nsIFrame* aFrame) {
+                                      nsTextFrame* aFrame) {
   if (aIter.IsOriginalCharSkipped()) return false;
   uint32_t index = aIter.GetSkippedOffset();
   if (aRespectClusters && !aTextRun->IsClusterStart(index)) return false;
   if (index > 0) {
     // Check whether the proposed position is in between the two halves of a
     // surrogate pair, before a Variation Selector character, or within a
     // ligated emoji sequence; if so, this is not a valid character boundary.
     // (In the case where we are respecting clusters, we won't actually get
     // this far because the low surrogate is also marked as non-clusterStart
     // so we'll return FALSE above.)
     uint32_t offs = aIter.GetOriginalOffset();
-    const nsTextFragment* frag = aFrame->GetContent()->GetText();
+    const nsTextFragment* frag = aFrame->TextFragment();
     uint32_t ch = frag->CharAt(offs);
 
     if (gfxFontUtils::IsVarSelector(ch) ||
         (NS_IS_LOW_SURROGATE(ch) && offs > 0 &&
          NS_IS_HIGH_SURROGATE(frag->CharAt(offs - 1))) ||
         (!aTextRun->IsLigatureGroupStart(index) &&
          (unicode::GetEmojiPresentation(ch) == unicode::EmojiDefault ||
           (unicode::GetEmojiPresentation(ch) == unicode::TextDefault &&
@@ -7603,17 +7602,17 @@ nsIFrame::FrameSearchResult nsTextFrame:
       return CONTINUE_UNSELECTABLE;
     }
   }
 
   gfxSkipCharsIterator iter = EnsureTextRun(nsTextFrame::eInflated);
   if (!mTextRun) return CONTINUE_EMPTY;
 
   TrimmedOffsets trimmed =
-      GetTrimmedOffsets(mContent->GetText(), TrimmedOffsetFlags::NoTrimAfter);
+      GetTrimmedOffsets(TextFragment(), TrimmedOffsetFlags::NoTrimAfter);
 
   // A negative offset means "end of frame".
   int32_t startOffset =
       GetContentOffset() + (*aOffset < 0 ? contentLength : *aOffset);
 
   if (!aForward) {
     // If at the beginning of the line, look at the previous continuation
     for (int32_t i = std::min(trimmed.GetEnd(), startOffset) - 1;
@@ -7736,17 +7735,17 @@ ClusterIterator::ClusterIterator(nsTextF
       mHaveWordBreak(false) {
   mIterator = aTextFrame->EnsureTextRun(nsTextFrame::eInflated);
   if (!aTextFrame->GetTextRun(nsTextFrame::eInflated)) {
     mDirection = 0;  // signal failure
     return;
   }
   mIterator.SetOriginalOffset(aPosition);
 
-  mFrag = aTextFrame->GetContent()->GetText();
+  mFrag = aTextFrame->TextFragment();
   mTrimmed = aTextFrame->GetTrimmedOffsets(
       mFrag, aTrimSpaces ? nsTextFrame::TrimmedOffsetFlags::Default
                          : nsTextFrame::TrimmedOffsetFlags::NoTrimAfter |
                                nsTextFrame::TrimmedOffsetFlags::NoTrimBefore);
 
   int32_t textOffset = aTextFrame->GetContentOffset();
   int32_t textLen = aTextFrame->GetContentLength();
   if (!mWordBreaks.AppendElements(textLen + 1)) {
@@ -8044,17 +8043,17 @@ void nsTextFrame::AddInlineMinISizeForFl
       EnsureTextRun(aTextRunType, aRenderingContext->GetDrawTarget(),
                     aData->LineContainer(), aData->mLine, &flowEndInTextRun);
   gfxTextRun* textRun = GetTextRun(aTextRunType);
   if (!textRun) return;
 
   // Pass null for the line container. This will disable tab spacing, but that's
   // OK since we can't really handle tabs for intrinsic sizing anyway.
   const nsStyleText* textStyle = StyleText();
-  const nsTextFragment* frag = mContent->GetText();
+  const nsTextFragment* frag = TextFragment();
 
   // If we're hyphenating, the PropertyProvider needs the actual length;
   // otherwise we can just pass INT32_MAX to mean "all the text"
   int32_t len = INT32_MAX;
   bool hyphenating = frag->GetLength() > 0 &&
                      (textStyle->mHyphens == StyleHyphens::Auto ||
                       (textStyle->mHyphens == StyleHyphens::Manual &&
                        !!(textRun->GetFlags() &
@@ -8241,17 +8240,17 @@ void nsTextFrame::AddInlinePrefISizeForF
                     aData->LineContainer(), aData->mLine, &flowEndInTextRun);
   gfxTextRun* textRun = GetTextRun(aTextRunType);
   if (!textRun) return;
 
   // Pass null for the line container. This will disable tab spacing, but that's
   // OK since we can't really handle tabs for intrinsic sizing anyway.
 
   const nsStyleText* textStyle = StyleText();
-  const nsTextFragment* frag = mContent->GetText();
+  const nsTextFragment* frag = TextFragment();
   PropertyProvider provider(textRun, textStyle, frag, this, iter, INT32_MAX,
                             nullptr, 0, aTextRunType);
 
   // text-combine-upright frame is constantly 1em on inline-axis.
   if (Style()->IsTextCombined()) {
     aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
     aData->mTrailingWhitespace = 0;
     aData->mLineIsEmpty = false;
@@ -8770,17 +8769,17 @@ void nsTextFrame::ReflowText(nsLineLayou
 
   bool atStartOfLine = aLineLayout.LineAtStart();
   if (atStartOfLine) {
     AddStateBits(TEXT_START_OF_LINE);
   }
 
   uint32_t flowEndInTextRun;
   nsIFrame* lineContainer = aLineLayout.LineContainerFrame();
-  const nsTextFragment* frag = mContent->GetText();
+  const nsTextFragment* frag = TextFragment();
 
   // DOM offsets of the text range we need to measure, after trimming
   // whitespace, restricting to first-letter, and restricting preformatted text
   // to nearest newline
   int32_t length = maxContentLength;
   int32_t offset = GetContentOffset();
 
   // Restrict preformatted text to the nearest newline
@@ -8794,17 +8793,17 @@ void nsTextFrame::ReflowText(nsLineLayou
                                     mContent->GetProperty(nsGkAtoms::newline))
                               : nullptr;
     if (cachedNewlineOffset && cachedNewlineOffset->mStartOffset <= offset &&
         (cachedNewlineOffset->mNewlineOffset == -1 ||
          cachedNewlineOffset->mNewlineOffset >= offset)) {
       contentNewLineOffset = cachedNewlineOffset->mNewlineOffset;
     } else {
       contentNewLineOffset =
-          FindChar(frag, offset, mContent->TextLength() - offset, '\n');
+          FindChar(frag, offset, GetContent()->TextLength() - offset, '\n');
     }
     if (contentNewLineOffset < offset + length) {
       /*
         The new line offset could be outside this frame if the frame has been
         split by bidi resolution. In that case we won't use it in this reflow
         (newLineOffset will remain -1), but we will still cache it in mContent
       */
       newLineOffset = contentNewLineOffset;
@@ -9344,17 +9343,17 @@ nsTextFrame::TrimOutput nsTextFrame::Tri
   if (!contentLength) return result;
 
   gfxSkipCharsIterator start =
       EnsureTextRun(nsTextFrame::eInflated, aDrawTarget);
   NS_ENSURE_TRUE(mTextRun, result);
 
   uint32_t trimmedStart = start.GetSkippedOffset();
 
-  const nsTextFragment* frag = mContent->GetText();
+  const nsTextFragment* frag = TextFragment();
   TrimmedOffsets trimmed = GetTrimmedOffsets(frag);
   gfxSkipCharsIterator trimmedEndIter = start;
   const nsStyleText* textStyle = StyleText();
   gfxFloat delta = 0;
   uint32_t trimmedEnd =
       trimmedEndIter.ConvertOriginalToSkipped(trimmed.GetEnd());
 
   if (!(GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) &&
@@ -9512,17 +9511,17 @@ nsIFrame::RenderedText nsTextFrame::GetR
                   aEndOffset <= (uint32_t)GetContentEnd()),
              "Must be called on first-in-flow, or content offsets must be "
              "given and be within this frame.");
 
   // The handling of offsets could be more efficient...
   RenderedText result;
   nsBlockFrame* lineContainer = nullptr;
   nsTextFrame* textFrame;
-  const nsTextFragment* textFrag = mContent->GetText();
+  const nsTextFragment* textFrag = TextFragment();
   uint32_t offsetInRenderedString = 0;
   bool haveOffsets = false;
 
   Maybe<nsBlockFrame::AutoLineCursorSetup> autoLineCursor;
   for (textFrame = this; textFrame;
        textFrame = textFrame->GetNextContinuation()) {
     if (textFrame->GetStateBits() & NS_FRAME_IS_DIRTY) {
       // We don't trust dirty frames, especially when computing rendered text.
@@ -9688,29 +9687,29 @@ bool nsTextFrame::IsEmpty() {
   if (mState & TEXT_ISNOT_ONLY_WHITESPACE) {
     return false;
   }
 
   if (mState & TEXT_IS_ONLY_WHITESPACE) {
     return true;
   }
 
-  bool isEmpty = IsAllWhitespace(
-      mContent->GetText(),
-      textStyle->mWhiteSpace != mozilla::StyleWhiteSpace::PreLine);
+  bool isEmpty =
+      IsAllWhitespace(TextFragment(), textStyle->mWhiteSpace !=
+                                           mozilla::StyleWhiteSpace::PreLine);
   AddStateBits(isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
   return isEmpty;
 }
 
 #ifdef DEBUG_FRAME_DUMP
 // Translate the mapped content into a string that's printable
 void nsTextFrame::ToCString(nsCString& aBuf,
                             int32_t* aTotalContentLength) const {
   // Get the frames text content
-  const nsTextFragment* frag = mContent->GetText();
+  const nsTextFragment* frag = TextFragment();
   if (!frag) {
     return;
   }
 
   // Compute the total length of the text content.
   *aTotalContentLength = frag->GetLength();
 
   int32_t contentLength = GetContentLength();
@@ -9881,17 +9880,17 @@ mozilla::JustificationAssignment nsTextF
   int32_t encoded = GetProperty(JustificationAssignmentProperty());
   mozilla::JustificationAssignment result;
   result.mGapsAtStart = encoded >> 8;
   result.mGapsAtEnd = encoded & 0xFF;
   return result;
 }
 
 uint32_t nsTextFrame::CountGraphemeClusters() const {
-  const nsTextFragment* frag = GetContent()->GetText();
+  const nsTextFragment* frag = TextFragment();
   MOZ_ASSERT(frag, "Text frame must have text fragment");
   nsAutoString content;
   frag->AppendTo(content, GetContentOffset(), GetContentLength());
   return unicode::CountGraphemeClusters(content.Data(), content.Length());
 }
 
 bool nsTextFrame::HasNonSuppressedText() {
   if (HasAnyStateBits(TEXT_ISNOT_ONLY_WHITESPACE |
@@ -9901,11 +9900,11 @@ bool nsTextFrame::HasNonSuppressedText()
     return true;
   }
 
   if (!GetTextRun(nsTextFrame::eInflated)) {
     return false;
   }
 
   TrimmedOffsets offsets =
-      GetTrimmedOffsets(mContent->GetText(), TrimmedOffsetFlags::NoTrimAfter);
+      GetTrimmedOffsets(TextFragment(), TrimmedOffsetFlags::NoTrimAfter);
   return offsets.mLength != 0;
 }
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -6,17 +6,17 @@
 
 #ifndef nsTextFrame_h__
 #define nsTextFrame_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/dom/CharacterData.h"
+#include "mozilla/dom/Text.h"
 #include "nsFrame.h"
 #include "nsFrameSelection.h"
 #include "nsSplittableFrame.h"
 #include "nsLineBox.h"
 #include "gfxSkipChars.h"
 #include "gfxTextRun.h"
 #include "nsDisplayList.h"
 #include "nsFontMetrics.h"
@@ -152,16 +152,23 @@ class nsTextFrame : public nsFrame {
 
 #ifdef DEBUG_FRAME_DUMP
   void List(FILE* out = stderr, const char* aPrefix = "",
             uint32_t aFlags = 0) const final;
   nsresult GetFrameName(nsAString& aResult) const final;
   void ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const;
 #endif
 
+  // Returns this text frame's content's text fragment.
+  //
+  // Assertions in Init() ensure we only ever get a Text node as content.
+  const nsTextFragment* TextFragment() const {
+    return &mContent->AsText()->TextFragment();
+  }
+
   ContentOffsets CalcContentOffsetsFromFramePoint(const nsPoint& aPoint) final;
   ContentOffsets GetCharacterOffsetAtFramePoint(const nsPoint& aPoint);
 
   /**
    * This is called only on the primary text frame. It indicates that
    * the selection state of the given character range has changed.
    * Text in the range is unconditionally invalidated
    * (Selection::Repaint depends on this).
--- a/layout/generic/nsTextFrameUtils.cpp
+++ b/layout/generic/nsTextFrameUtils.cpp
@@ -335,18 +335,18 @@ template char16_t* nsTextFrameUtils::Tra
     CompressionMode aCompression, uint8_t* aIncomingFlags,
     gfxSkipChars* aSkipChars, Flags* aAnalysisFlags);
 template bool nsTextFrameUtils::IsSkippableCharacterForTransformText(
     uint8_t aChar);
 template bool nsTextFrameUtils::IsSkippableCharacterForTransformText(
     char16_t aChar);
 
 uint32_t nsTextFrameUtils::ComputeApproximateLengthWithWhitespaceCompression(
-    nsIContent* aContent, const nsStyleText* aStyleText) {
-  const nsTextFragment* frag = aContent->GetText();
+    Text* aText, const nsStyleText* aStyleText) {
+  const nsTextFragment* frag = &aText->TextFragment();
   // This is an approximation so we don't really need anything
   // too fancy here.
   uint32_t len;
   if (aStyleText->WhiteSpaceIsSignificant()) {
     len = frag->GetLength();
   } else {
     bool is2b = frag->Is2b();
     union {
--- a/layout/generic/nsTextFrameUtils.h
+++ b/layout/generic/nsTextFrameUtils.h
@@ -8,16 +8,22 @@
 #define NSTEXTFRAMEUTILS_H_
 
 #include "gfxSkipChars.h"
 #include "nsBidiUtils.h"
 
 class nsIContent;
 struct nsStyleText;
 
+namespace mozilla {
+namespace dom {
+class Text;
+}
+}  // namespace mozilla
+
 #define BIG_TEXT_NODE_SIZE 4096
 
 #define CH_NBSP 160
 #define CH_SHY 173
 #define CH_CJKSP 12288  // U+3000 IDEOGRAPHIC SPACE (CJK Full-Width Space)
 
 class nsTextFrameUtils {
  public:
@@ -128,17 +134,17 @@ class nsTextFrameUtils {
   static void AppendLineBreakOffset(nsTArray<uint32_t>* aArray,
                                     uint32_t aOffset) {
     if (aArray->Length() > 0 && (*aArray)[aArray->Length() - 1] == aOffset)
       return;
     aArray->AppendElement(aOffset);
   }
 
   static uint32_t ComputeApproximateLengthWithWhitespaceCompression(
-      nsIContent* aContent, const nsStyleText* aStyleText);
+      mozilla::dom::Text* aText, const nsStyleText* aStyleText);
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(nsTextFrameUtils::Flags)
 
 class nsSkipCharsRunIterator {
  public:
   enum LengthMode {
     LENGTH_UNSKIPPED_ONLY = false,
--- a/layout/printing/nsPrintJob.cpp
+++ b/layout/printing/nsPrintJob.cpp
@@ -148,19 +148,18 @@ static uint32_t gDumpLOFileNameCnt = 0;
 
 #define PRT_YESNO(_p) ((_p) ? "YES" : "NO")
 static const char* gFrameTypesStr[] = {"eDoc", "eFrame", "eIFrame",
                                        "eFrameSet"};
 static const char* gPrintFrameTypeStr[] = {"kNoFrames", "kFramesAsIs",
                                            "kSelectedFrame", "kEachFrameSep"};
 static const char* gFrameHowToEnableStr[] = {
     "kFrameEnableNone", "kFrameEnableAll", "kFrameEnableAsIsAndEach"};
-static const char* gPrintRangeStr[] = {"kRangeAllPages",
-                                       "kRangeSpecifiedPageRange",
-                                       "kRangeSelection", "kRangeFocusFrame"};
+static const char* gPrintRangeStr[] = {
+    "kRangeAllPages", "kRangeSpecifiedPageRange", "kRangeSelection"};
 
 // This processes the selection on aOrigDoc and creates an inverted selection on
 // aDoc, which it then deletes. If the start or end of the inverted selection
 // ranges occur in text nodes then an ellipsis is added.
 static nsresult DeleteUnselectedNodes(Document* aOrigDoc, Document* aDoc);
 
 #ifdef EXTENDED_DEBUG_PRINTING
 // Forward Declarations
@@ -982,65 +981,17 @@ nsresult nsPrintJob::DoCommonPrint(bool 
 
   if (aIsPrintPreview) {
     printData->mPrintSettings->SetPrintFrameType(nsIPrintSettings::kFramesAsIs);
 
     // override any UI that wants to PrintPreview any selection or page range
     // we want to view every page in PrintPreview each time
     printData->mPrintSettings->SetPrintRange(nsIPrintSettings::kRangeAllPages);
   } else {
-    // Always check and set the print settings first and then fall back
-    // onto the PrintService if there isn't a PrintSettings
-    //
-    // Posiible Usage values:
-    //   nsIPrintSettings::kUseInternalDefault
-    //   nsIPrintSettings::kUseSettingWhenPossible
-    //
-    // NOTE: The consts are the same for PrintSettings and PrintSettings
-    int16_t printFrameTypeUsage = nsIPrintSettings::kUseSettingWhenPossible;
-    printData->mPrintSettings->GetPrintFrameTypeUsage(&printFrameTypeUsage);
-
-    // Ok, see if we are going to use our value and override the default
-    if (printFrameTypeUsage == nsIPrintSettings::kUseSettingWhenPossible) {
-      // Get the Print Options/Settings PrintFrameType to see what is preferred
-      int16_t printFrameType = nsIPrintSettings::kEachFrameSep;
-      printData->mPrintSettings->GetPrintFrameType(&printFrameType);
-
-      // Don't let anybody do something stupid like try to set it to
-      // kNoFrames when we are printing a FrameSet
-      if (printFrameType == nsIPrintSettings::kNoFrames) {
-        printData->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
-        printData->mPrintSettings->SetPrintFrameType(
-            printData->mPrintFrameType);
-      } else {
-        // First find out from the PrinService what options are available
-        // to us for Printing FrameSets
-        int16_t howToEnableFrameUI;
-        printData->mPrintSettings->GetHowToEnableFrameUI(&howToEnableFrameUI);
-        if (howToEnableFrameUI != nsIPrintSettings::kFrameEnableNone) {
-          switch (howToEnableFrameUI) {
-            case nsIPrintSettings::kFrameEnableAll:
-              printData->mPrintFrameType = printFrameType;
-              break;
-
-            case nsIPrintSettings::kFrameEnableAsIsAndEach:
-              if (printFrameType != nsIPrintSettings::kSelectedFrame) {
-                printData->mPrintFrameType = printFrameType;
-              } else {  // revert back to a good value
-                printData->mPrintFrameType = nsIPrintSettings::kEachFrameSep;
-              }
-              break;
-          }  // switch
-          printData->mPrintSettings->SetPrintFrameType(
-              printData->mPrintFrameType);
-        }
-      }
-    } else {
-      printData->mPrintSettings->GetPrintFrameType(&printData->mPrintFrameType);
-    }
+    printData->mPrintSettings->GetPrintFrameType(&printData->mPrintFrameType);
   }
 
   if (printData->mPrintFrameType == nsIPrintSettings::kEachFrameSep) {
     CheckForChildFrameSets(printData->mPrintObject);
   }
 
   if (NS_FAILED(EnablePOsForPrinting())) {
     return NS_ERROR_FAILURE;
--- a/layout/svg/SVGObserverUtils.cpp
+++ b/layout/svg/SVGObserverUtils.cpp
@@ -10,32 +10,93 @@
 // Keep others in (case-insensitive) order:
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/RestyleManager.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsHashKeys.h"
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
+#include "nsInterfaceHashtable.h"
 #include "nsIReflowCallback.h"
 #include "nsISupportsImpl.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGFilterFrame.h"
 #include "nsSVGMarkerFrame.h"
 #include "nsSVGMaskFrame.h"
 #include "nsSVGPaintServerFrame.h"
+#include "nsTHashtable.h"
+#include "nsURIHashKey.h"
 #include "SVGGeometryElement.h"
 #include "SVGTextPathElement.h"
 #include "SVGUseElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
+bool URLAndReferrerInfo::operator==(const URLAndReferrerInfo& aRHS) const {
+  bool uriEqual = false, referrerEqual = false;
+  this->mURI->Equals(aRHS.mURI, &uriEqual);
+  this->mReferrer->Equals(aRHS.mReferrer, &referrerEqual);
+
+  return uriEqual && referrerEqual &&
+         this->mReferrerPolicy == aRHS.mReferrerPolicy;
+}
+
+class URLAndReferrerInfoHashKey : public PLDHashEntryHdr {
+ public:
+  using KeyType = const URLAndReferrerInfo*;
+  using KeyTypePointer = const URLAndReferrerInfo*;
+
+  explicit URLAndReferrerInfoHashKey(const URLAndReferrerInfo* aKey)
+      : mKey(aKey) {
+    MOZ_COUNT_CTOR(URLAndReferrerInfoHashKey);
+  }
+  URLAndReferrerInfoHashKey(URLAndReferrerInfoHashKey&& aToMove)
+      : PLDHashEntryHdr(std::move(aToMove)), mKey(std::move(aToMove.mKey)) {
+    MOZ_COUNT_CTOR(URLAndReferrerInfoHashKey);
+  }
+  ~URLAndReferrerInfoHashKey() { MOZ_COUNT_DTOR(URLAndReferrerInfoHashKey); }
+
+  const URLAndReferrerInfo* GetKey() const { return mKey; }
+
+  bool KeyEquals(const URLAndReferrerInfo* aKey) const {
+    if (!mKey) {
+      return !aKey;
+    }
+    return *mKey == *aKey;
+  }
+
+  static const URLAndReferrerInfo* KeyToPointer(
+      const URLAndReferrerInfo* aKey) {
+    return aKey;
+  }
+
+  static PLDHashNumber HashKey(const URLAndReferrerInfo* aKey) {
+    if (!aKey) {
+      // If the key is null, return hash for empty string.
+      return HashString(EmptyCString());
+    }
+    nsAutoCString urlSpec, referrerSpec;
+    // nsURIHashKey ignores GetSpec() failures, so we do too:
+    Unused << aKey->GetURI()->GetSpec(urlSpec);
+    Unused << aKey->GetReferrer()->GetSpec(referrerSpec);
+    auto refPolicy = aKey->GetReferrerPolicy();
+    return AddToHash(HashString(urlSpec), HashString(referrerSpec), refPolicy);
+  }
+
+  enum { ALLOW_MEMMOVE = true };
+
+ protected:
+  RefPtr<const URLAndReferrerInfo> mKey;
+};
+
 static already_AddRefed<URLAndReferrerInfo> ResolveURLUsingLocalRef(
     nsIFrame* aFrame, const css::URLValue* aURL) {
   MOZ_ASSERT(aFrame);
 
   if (!aURL) {
     return nullptr;
   }
 
@@ -983,18 +1044,17 @@ void SVGRenderingObserver::DebugObserver
                "failed to track whether we're in our referenced element's "
                "observer set!");
   } else {
     MOZ_ASSERT(!mInObserverList, "In whose observer set are we, then?");
   }
 }
 #endif
 
-typedef nsInterfaceHashtable<nsRefPtrHashKey<URLAndReferrerInfo>,
-                             nsIMutationObserver>
+typedef nsInterfaceHashtable<URLAndReferrerInfoHashKey, nsIMutationObserver>
     URIObserverHashtable;
 
 using PaintingPropertyDescriptor =
     const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>*;
 
 static void DestroyFilterProperty(SVGFilterObserverListForCSSProp* aProp) {
   // SVGFilterObserverListForCSSProp is cycle-collected, so dropping the last
   // reference doesn't necessarily destroy it. We need to tell it that the
@@ -1371,18 +1431,16 @@ Element* SVGObserverUtils::GetAndObserve
   nsCOMPtr<nsIURI> base = aFrame->GetContent()->GetBaseURI();
   nsContentUtils::NewURIWithDocumentCharset(
       getter_AddRefs(targetURI), elementId,
       aFrame->GetContent()->GetUncomposedDoc(), base);
   RefPtr<URLAndReferrerInfo> url = new URLAndReferrerInfo(
       targetURI, aFrame->GetContent()->OwnerDoc()->GetDocumentURI(),
       aFrame->GetContent()->OwnerDoc()->GetReferrerPolicy());
 
-  // XXXjwatt: this is broken - we're using the address of a new
-  // URLAndReferrerInfo as the hash key every time!
   SVGMozElementObserver* observer =
       static_cast<SVGMozElementObserver*>(hashtable->GetWeak(url));
   if (!observer) {
     observer = new SVGMozElementObserver(url, aFrame, /* aWatchImage */ true);
     hashtable->Put(url, observer);
   }
   return observer->GetAndObserveReferencedElement();
 }
--- a/layout/svg/SVGObserverUtils.h
+++ b/layout/svg/SVGObserverUtils.h
@@ -6,28 +6,24 @@
 
 #ifndef NSSVGEFFECTS_H_
 #define NSSVGEFFECTS_H_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/IDTracker.h"
 #include "FrameProperties.h"
 #include "mozilla/dom/Element.h"
-#include "nsHashKeys.h"
 #include "nsID.h"
 #include "nsIFrame.h"
 #include "nsIMutationObserver.h"
-#include "nsInterfaceHashtable.h"
 #include "nsISupportsBase.h"
 #include "nsISupportsImpl.h"
 #include "nsStringFwd.h"
 #include "nsStubMutationObserver.h"
 #include "nsSVGUtils.h"
-#include "nsTHashtable.h"
-#include "nsURIHashKey.h"
 #include "nsCycleCollectionParticipant.h"
 
 class nsAtom;
 class nsIURI;
 class nsSVGClipPathFrame;
 class nsSVGMarkerFrame;
 class nsSVGPaintServerFrame;
 class nsSVGFilterFrame;
@@ -59,19 +55,23 @@ class URLAndReferrerInfo {
       : mURI(aURI),
         mReferrer(aExtraData->GetReferrer()),
         mReferrerPolicy(aExtraData->GetReferrerPolicy()) {
     MOZ_ASSERT(aURI);
   }
 
   NS_INLINE_DECL_REFCOUNTING(URLAndReferrerInfo)
 
-  nsIURI* GetURI() { return mURI; }
-  nsIURI* GetReferrer() { return mReferrer; }
-  mozilla::net::ReferrerPolicy GetReferrerPolicy() { return mReferrerPolicy; }
+  nsIURI* GetURI() const { return mURI; }
+  nsIURI* GetReferrer() const { return mReferrer; }
+  mozilla::net::ReferrerPolicy GetReferrerPolicy() const {
+    return mReferrerPolicy;
+  }
+
+  bool operator==(const URLAndReferrerInfo& aRHS) const;
 
  private:
   ~URLAndReferrerInfo() = default;
 
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIURI> mReferrer;
   mozilla::net::ReferrerPolicy mReferrerPolicy;
 };
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -975,17 +975,17 @@ void TextRenderedRun::GetClipEdges(nscoo
   // Get the offset/length of the whole nsTextFrame.
   uint32_t frameOffset = mFrame->GetContentOffset();
   uint32_t frameLength = mFrame->GetContentLength();
 
   // Trim the whole-nsTextFrame offset/length to remove any leading/trailing
   // white space, as the nsTextFrame when painting does not include them when
   // interpreting clip edges.
   nsTextFrame::TrimmedOffsets trimmedOffsets =
-      mFrame->GetTrimmedOffsets(mFrame->GetContent()->GetText());
+      mFrame->GetTrimmedOffsets(mFrame->TextFragment());
   TrimOffsets(frameOffset, frameLength, trimmedOffsets);
 
   // Convert the trimmed whole-nsTextFrame offset/length into skipped
   // characters.
   Range frameRange = ConvertOriginalToSkipped(it, frameOffset, frameLength);
 
   // Measure the advance width in the text run between the start of
   // frame's content and the start of the rendered run's content,
@@ -1880,17 +1880,17 @@ TextRenderedRun TextRenderedRunIterator:
     baseline = GetBaselinePosition(
         frame, frame->GetTextRun(nsTextFrame::eInflated),
         mFrameIterator.DominantBaseline(), mFontSizeScaleFactor);
 
     // Trim the offset/length to remove any leading/trailing white space.
     uint32_t untrimmedOffset = offset;
     uint32_t untrimmedLength = length;
     nsTextFrame::TrimmedOffsets trimmedOffsets =
-        frame->GetTrimmedOffsets(frame->GetContent()->GetText());
+        frame->GetTrimmedOffsets(frame->TextFragment());
     TrimOffsets(offset, length, trimmedOffsets);
     charIndex += offset - untrimmedOffset;
 
     // Get the position and rotation of the character that begins this
     // rendered run.
     pt = Root()->mPositions[charIndex].mPosition;
     rotate = Root()->mPositions[charIndex].mAngle;
 
@@ -2359,35 +2359,34 @@ bool CharIterator::IsClusterAndLigatureG
 
 bool CharIterator::IsOriginalCharTrimmed() const {
   if (mFrameForTrimCheck != TextFrame()) {
     // Since we do a lot of trim checking, we cache the trimmed offsets and
     // lengths while we are in the same frame.
     mFrameForTrimCheck = TextFrame();
     uint32_t offset = mFrameForTrimCheck->GetContentOffset();
     uint32_t length = mFrameForTrimCheck->GetContentLength();
-    nsIContent* content = mFrameForTrimCheck->GetContent();
     nsTextFrame::TrimmedOffsets trim = mFrameForTrimCheck->GetTrimmedOffsets(
-        content->GetText(),
+        mFrameForTrimCheck->TextFragment(),
         (mPostReflow ? nsTextFrame::TrimmedOffsetFlags::Default
                      : nsTextFrame::TrimmedOffsetFlags::NotPostReflow));
     TrimOffsets(offset, length, trim);
     mTrimmedOffset = offset;
     mTrimmedLength = length;
   }
 
   // A character is trimmed if it is outside the mTrimmedOffset/mTrimmedLength
   // range and it is not a significant newline character.
   uint32_t index = mSkipCharsIterator.GetOriginalOffset();
   return !(
       (index >= mTrimmedOffset && index < mTrimmedOffset + mTrimmedLength) ||
       (index >= mTrimmedOffset + mTrimmedLength &&
        mFrameForTrimCheck->StyleText()->NewlineIsSignificant(
            mFrameForTrimCheck) &&
-       mFrameForTrimCheck->GetContent()->GetText()->CharAt(index) == '\n'));
+       mFrameForTrimCheck->TextFragment()->CharAt(index) == '\n'));
 }
 
 void CharIterator::GetOriginalGlyphOffsets(uint32_t& aOriginalOffset,
                                            uint32_t& aOriginalLength) const {
   gfxSkipCharsIterator it = TextFrame()->EnsureTextRun(nsTextFrame::eInflated);
   it.SetOriginalOffset(mSkipCharsIterator.GetOriginalOffset() -
                        (mTextElementCharIndex -
                         mGlyphStartTextElementCharIndex -
@@ -3799,18 +3798,17 @@ nsresult SVGTextFrame::GetSubStringLengt
     // Offset into frame's nsTextNode:
     const uint32_t untrimmedOffset = frame->GetContentOffset();
     const uint32_t untrimmedLength = frame->GetContentEnd() - untrimmedOffset;
 
     // Trim the offset/length to remove any leading/trailing white space.
     uint32_t trimmedOffset = untrimmedOffset;
     uint32_t trimmedLength = untrimmedLength;
     nsTextFrame::TrimmedOffsets trimmedOffsets = frame->GetTrimmedOffsets(
-        frame->GetContent()->GetText(),
-        nsTextFrame::TrimmedOffsetFlags::NotPostReflow);
+        frame->TextFragment(), nsTextFrame::TrimmedOffsetFlags::NotPostReflow);
     TrimOffsets(trimmedOffset, trimmedLength, trimmedOffsets);
 
     textElementCharIndex += trimmedOffset - untrimmedOffset;
 
     if (textElementCharIndex >= charnum + nchars) {
       break;  // we're past the end of the substring
     }
 
@@ -4409,17 +4407,17 @@ void SVGTextFrame::DetermineCharPosition
 
     // Any characters not in a frame, e.g. when display:none.
     for (uint32_t i = 0; i < frit.UndisplayedCharacters(); i++) {
       aPositions.AppendElement(position);
     }
 
     // Any white space characters trimmed at the start of the line of text.
     nsTextFrame::TrimmedOffsets trimmedOffsets =
-        frame->GetTrimmedOffsets(frame->GetContent()->GetText());
+        frame->GetTrimmedOffsets(frame->TextFragment());
     while (it.GetOriginalOffset() < trimmedOffsets.mStart) {
       aPositions.AppendElement(position);
       it.AdvanceOriginal(1);
     }
 
     // If a ligature was started in the previous frame, we should record
     // the ligature's start position, not any partial position.
     while (it.GetOriginalOffset() < frame->GetContentEnd() &&
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -571,43 +571,58 @@ VARCACHE_PREF(
 // Other values might lead to experimental strategies. For more details, have a
 // look at: ScriptLoader::ShouldCacheBytecode function.
 VARCACHE_PREF(
   "dom.script_loader.bytecode_cache.strategy",
    dom_script_loader_bytecode_cache_strategy,
   int32_t, 0
 )
 
-// IMPORTANT: Keep this in condition in sync with all.js. The value
-// of MOZILLA_OFFICIAL is different between full and artifact builds, so without
-// it being specified there, dump is disabled in artifact builds (see Bug 1490412).
-#ifdef MOZILLA_OFFICIAL
+// Is support for compiling DOM worker scripts directly from UTF-8 (without ever
+// inflating to UTF-16) enabled?
+#ifdef RELEASE_OR_BETA
 # define PREF_VALUE false
 #else
 # define PREF_VALUE true
 #endif
 VARCACHE_PREF(
-  "browser.dom.window.dump.enabled",
-   browser_dom_window_dump_enabled,
+  "dom.worker.script_loader.utf8_parsing.enabled",
+   dom_worker_script_loader_utf8_parsing_enabled,
   RelaxedAtomicBool, PREF_VALUE
 )
 #undef PREF_VALUE
 
 VARCACHE_PREF(
   "dom.worker.canceling.timeoutMilliseconds",
    dom_worker_canceling_timeoutMilliseconds,
   RelaxedAtomicUint32, 30000 /* 30 seconds */
 )
 
 VARCACHE_PREF(
   "dom.worker.use_medium_high_event_queue",
    dom_worker_use_medium_high_event_queue,
   RelaxedAtomicBool, true
 )
 
+// IMPORTANT: Keep this condition in sync with all.js. The value of
+// MOZILLA_OFFICIAL is different between full and artifact builds, so without
+// it being specified there, dump is disabled in artifact builds.  See
+// bug 1490412.
+#ifdef MOZILLA_OFFICIAL
+# define PREF_VALUE false
+#else
+# define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "browser.dom.window.dump.enabled",
+   browser_dom_window_dump_enabled,
+  RelaxedAtomicBool, PREF_VALUE
+)
+#undef PREF_VALUE
+
 // Enable content type normalization of XHR uploads via MIME Sniffing standard
 VARCACHE_PREF(
   "dom.xhr.standard_content_type_normalization",
    dom_xhr_standard_content_type_normalization,
   RelaxedAtomicBool, true
 )
 
 // Block multiple external protocol URLs in iframes per single event.
--- a/testing/web-platform/meta/html/browsers/the-window-object/window-properties.https.html.ini
+++ b/testing/web-platform/meta/html/browsers/the-window-object/window-properties.https.html.ini
@@ -5,19 +5,16 @@
   [Window attribute: onmousewheel]
     expected: FAIL
 
   [Window method: print]
     expected:
       if (os == "android") and not e10s: FAIL
       if (os == "android") and e10s: FAIL
 
-  [Window method: queueMicrotask]
-    expected: FAIL
-
   [Window unforgeable attribute: window]
     expected:
       if nightly_build: FAIL
     bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1510437
 
   [Window unforgeable attribute: document]
     expected:
       if nightly_build: FAIL
--- a/testing/web-platform/meta/html/dom/interfaces.https.html.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.https.html.ini
@@ -367,26 +367,16 @@ prefs: [dom.security.featurePolicy.enabl
     expected: FAIL
 
   [Document interface: iframe.contentDocument must inherit property "onformdata" with the proper type]
     expected: FAIL
 
   [Document interface: attribute onformdata]
     expected: FAIL
 
-  [Window interface: window must inherit property "queueMicrotask(VoidFunction)" with the proper type]
-    expected: FAIL
-
-  [Window interface: calling queueMicrotask(VoidFunction) on window with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Window interface: operation queueMicrotask(VoidFunction)]
-    expected: FAIL
-
-
 [interfaces.https.html?include=HTML.*]
   [HTMLAllCollection must be primary interface of document.all]
     expected: FAIL
 
   [Stringification of document.all]
     expected: FAIL
 
   [HTMLAllCollection interface: document.all must inherit property "length" with the proper type]
--- a/testing/web-platform/meta/html/dom/interfaces.worker.js.ini
+++ b/testing/web-platform/meta/html/dom/interfaces.worker.js.ini
@@ -444,37 +444,28 @@
     expected: FAIL
 
   [DedicatedWorkerGlobalScope interface: calling requestAnimationFrame(FrameRequestCallback) on self with too few arguments must throw TypeError]
     expected: FAIL
 
   [TextMetrics interface: attribute ideographicBaseline]
     expected: FAIL
 
-  [WorkerGlobalScope interface: self must inherit property "queueMicrotask(VoidFunction)" with the proper type]
-    expected: FAIL
-
   [OffscreenCanvasRenderingContext2D interface: existence and properties of interface prototype object's @@unscopables property]
     expected: FAIL
 
-  [WorkerGlobalScope interface: operation queueMicrotask(VoidFunction)]
-    expected: FAIL
-
   [TextMetrics interface: attribute actualBoundingBoxAscent]
     expected: FAIL
 
   [CanvasPattern interface: existence and properties of interface prototype object's @@unscopables property]
     expected: FAIL
 
   [TextMetrics interface: existence and properties of interface prototype object's @@unscopables property]
     expected: FAIL
 
-  [WorkerGlobalScope interface: calling queueMicrotask(VoidFunction) on self with too few arguments must throw TypeError]
-    expected: FAIL
-
   [DedicatedWorkerGlobalScope interface: self must inherit property "cancelAnimationFrame(unsigned long)" with the proper type]
     expected: FAIL
 
   [SharedWorker interface: existence and properties of interface prototype object's @@unscopables property]
     expected: FAIL
 
   [TextMetrics interface: attribute emHeightDescent]
     expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/microtask-queuing/queue-microtask-exceptions.any.js.ini
+++ /dev/null
@@ -1,22 +0,0 @@
-[queue-microtask-exceptions.https.any.serviceworker.html]
-  expected: TIMEOUT
-
-[queue-microtask-exceptions.any.worker.html]
-  [It rethrows exceptions]
-    expected: FAIL
-
-
-[queue-microtask-exceptions.any.html]
-  [It rethrows exceptions]
-    expected: FAIL
-
-
-[queue-microtask-exceptions.any.sharedworker.html]
-  [It rethrows exceptions]
-    expected: FAIL
-
-
-[queue-microtask-exceptions.any.serviceworker.html]
-  [It rethrows exceptions]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/microtask-queuing/queue-microtask.any.js.ini
+++ /dev/null
@@ -1,72 +0,0 @@
-[queue-microtask.https.any.serviceworker.html]
-  expected: TIMEOUT
-
-[queue-microtask.any.sharedworker.html]
-  [It exists and is a function]
-    expected: FAIL
-
-  [It does not pass any arguments]
-    expected: FAIL
-
-  [It calls the callback asynchronously]
-    expected: FAIL
-
-  [It throws when given non-functions]
-    expected: FAIL
-
-  [It interleaves with promises as expected]
-    expected: FAIL
-
-
-[queue-microtask.any.html]
-  [It exists and is a function]
-    expected: FAIL
-
-  [It does not pass any arguments]
-    expected: FAIL
-
-  [It calls the callback asynchronously]
-    expected: FAIL
-
-  [It throws when given non-functions]
-    expected: FAIL
-
-  [It interleaves with promises as expected]
-    expected: FAIL
-
-
-[queue-microtask.any.worker.html]
-  [It exists and is a function]
-    expected: FAIL
-
-  [It does not pass any arguments]
-    expected: FAIL
-
-  [It calls the callback asynchronously]
-    expected: FAIL
-
-  [It throws when given non-functions]
-    expected: FAIL
-
-  [It interleaves with promises as expected]
-    expected: FAIL
-
-
-[queue-microtask.any.serviceworker.html]
-  disabled:
-    if os == "android" and not e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1499003
-  [It exists and is a function]
-    expected: FAIL
-
-  [It does not pass any arguments]
-    expected: FAIL
-
-  [It calls the callback asynchronously]
-    expected: FAIL
-
-  [It throws when given non-functions]
-    expected: FAIL
-
-  [It interleaves with promises as expected]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/webappapis/microtask-queuing/queue-microtask.window.js.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[queue-microtask.window.html]
-  [It interleaves with MutationObservers and promises together as expected]
-    expected: FAIL
-
-  [It interleaves with MutationObservers as expected]
-    expected: FAIL
-
--- a/toolkit/components/printingui/ipc/PPrintingTypes.ipdlh
+++ b/toolkit/components/printingui/ipc/PPrintingTypes.ipdlh
@@ -39,17 +39,16 @@ struct PrintData {
   nsString headerStrCenter;
   nsString headerStrRight;
   nsString footerStrLeft;
   nsString footerStrCenter;
   nsString footerStrRight;
 
   short  howToEnableFrameUI;
   bool isCancelled;
-  short printFrameTypeUsage;
   short  printFrameType;
   bool printSilent;
   bool shrinkToFit;
   bool showPrintProgress;
 
   nsString paperName;
   short paperData;
   double paperWidth;
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -472,37 +472,54 @@ WidgetWheelEvent PanGestureInput::ToWidg
   WidgetWheelEvent wheelEvent(true, eWheel, aWidget);
   wheelEvent.mModifiers = this->modifiers;
   wheelEvent.mTime = mTime;
   wheelEvent.mTimeStamp = mTimeStamp;
   wheelEvent.mRefPoint = RoundedToInt(ViewAs<LayoutDevicePixel>(
       mPanStartPoint,
       PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent));
   wheelEvent.mButtons = 0;
-  wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_PIXEL;
   wheelEvent.mMayHaveMomentum = true;  // pan inputs may have momentum
   wheelEvent.mIsMomentum = IsMomentum();
   wheelEvent.mLineOrPageDeltaX = mLineOrPageDeltaX;
   wheelEvent.mLineOrPageDeltaY = mLineOrPageDeltaY;
   wheelEvent.mDeltaX = mPanDisplacement.x;
   wheelEvent.mDeltaY = mPanDisplacement.y;
   wheelEvent.mFlags.mHandledByAPZ = mHandledByAPZ;
   wheelEvent.mFocusSequenceNumber = mFocusSequenceNumber;
+  if (mDeltaType == PanGestureInput::PANDELTA_PAGE) {
+    // Emulate legacy widget/gtk behavior
+    wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_LINE;
+    wheelEvent.mIsNoLineOrPageDelta = true;
+    wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;
+    wheelEvent.mDeltaX *= 3;
+    wheelEvent.mDeltaY *= 3;
+  } else {
+    wheelEvent.mDeltaMode = WheelEvent_Binding::DOM_DELTA_PIXEL;
+  }
   return wheelEvent;
 }
 
 bool PanGestureInput::TransformToLocal(
     const ScreenToParentLayerMatrix4x4& aTransform) {
   Maybe<ParentLayerPoint> panStartPoint =
       UntransformBy(aTransform, mPanStartPoint);
   if (!panStartPoint) {
     return false;
   }
   mLocalPanStartPoint = *panStartPoint;
 
+  if (mDeltaType == PanGestureInput::PANDELTA_PAGE) {
+    // Skip transforming the pan displacement because we want
+    // raw page proportion counts.
+    mLocalPanDisplacement.x = mPanDisplacement.x;
+    mLocalPanDisplacement.y = mPanDisplacement.y;
+    return true;
+  }
+
   Maybe<ParentLayerPoint> panDisplacement =
       UntransformVector(aTransform, mPanDisplacement, mPanStartPoint);
   if (!panDisplacement) {
     return false;
   }
   mLocalPanDisplacement = *panDisplacement;
   return true;
 }
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -327,16 +327,27 @@ class PanGestureInput : public InputData
       // MomentumPan: The actual momentum motion by mPanDisplacement.
       PANGESTURE_MOMENTUMPAN,
 
       // MomentumEnd: The momentum animation has ended, for example because the
       // momentum velocity has gone below the stopping threshold, or because the
       // user has stopped the animation by putting their fingers on a touchpad.
       PANGESTURE_MOMENTUMEND
   ));
+
+  MOZ_DEFINE_ENUM_AT_CLASS_SCOPE(
+    PanDeltaType, (
+      // There are three kinds of scroll delta modes in Gecko: "page", "line"
+      // and "pixel". Touchpad pan gestures only support "page" and "pixel".
+      //
+      // NOTE: PANDELTA_PAGE currently replicates Gtk behavior
+      // (see AsyncPanZoomController::OnPan).
+      PANDELTA_PAGE,
+      PANDELTA_PIXEL
+  ));
   // clang-format on
 
   PanGestureInput(PanGestureType aType, uint32_t aTime, TimeStamp aTimeStamp,
                   const ScreenPoint& aPanStartPoint,
                   const ScreenPoint& aPanDisplacement, Modifiers aModifiers);
 
   bool IsMomentum() const;
 
@@ -363,37 +374,56 @@ class PanGestureInput : public InputData
   // See lineOrPageDeltaX/Y on WidgetWheelEvent.
   int32_t mLineOrPageDeltaX;
   int32_t mLineOrPageDeltaY;
 
   // User-set delta multipliers.
   double mUserDeltaMultiplierX;
   double mUserDeltaMultiplierY;
 
-  bool mHandledByAPZ;
+  PanDeltaType mDeltaType = PANDELTA_PIXEL;
+
+  bool mHandledByAPZ : 1;
 
   // true if this is a PANGESTURE_END event that will be followed by a
   // PANGESTURE_MOMENTUMSTART event.
-  bool mFollowedByMomentum;
+  bool mFollowedByMomentum : 1;
 
   // If this is true, and this event started a new input block that couldn't
   // find a scrollable target which is scrollable in the horizontal component
   // of the scroll start direction, then this input block needs to be put on
   // hold until a content response has arrived, even if the block has a
   // confirmed target.
   // This is used by events that can result in a swipe instead of a scroll.
-  bool mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection;
+  bool mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection : 1;
 
   // This is used by APZ to communicate to the macOS widget code whether
   // the overscroll-behavior of the scroll frame handling this swipe allows
   // non-local overscroll behaviors in the horizontal direction (such as
   // swipe navigation).
-  bool mOverscrollBehaviorAllowsSwipe;
+  bool mOverscrollBehaviorAllowsSwipe : 1;
+
+  // true if APZ should do a fling animation after this pan ends, like
+  // it would with touchscreens. (For platforms that don't emit momentum
+  // events.)
+  bool mSimulateMomentum : 1;
 
-  // XXX: If adding any more bools, switch to using bitfields instead.
+  void SetHandledByAPZ(bool aHandled) { mHandledByAPZ = aHandled; }
+  void SetFollowedByMomentum(bool aFollowed) {
+    mFollowedByMomentum = aFollowed;
+  }
+  void SetRequiresContentResponseIfCannotScrollHorizontallyInStartDirection(
+      bool aRequires) {
+    mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection =
+        aRequires;
+  }
+  void SetOverscrollBehaviorAllowsSwipe(bool aAllows) {
+    mOverscrollBehaviorAllowsSwipe = aAllows;
+  }
+  void SetSimulateMomentum(bool aSimulate) { mSimulateMomentum = aSimulate; }
 };
 
 /**
  * Encapsulation class for pinch events. In general, these will be generated by
  * a gesture listener by looking at SingleTouchData/MultiTouchInput instances
  * and determining whether or not the user was trying to do a gesture.
  */
 class PinchGestureInput : public InputData {
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -114,16 +114,19 @@ using namespace mozilla::widget;
 #include "GLContextProvider.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/HelpersCairo.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/KnowsCompositor.h"
 
+#include "mozilla/layers/APZInputBridge.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+
 #ifdef MOZ_X11
 #  include "GLContextGLX.h"  // for GLContextGLX::FindVisual()
 #  include "GtkCompositorWidget.h"
 #  include "gfxXlibSurface.h"
 #  include "WindowSurfaceX11Image.h"
 #  include "WindowSurfaceX11SHM.h"
 #  include "WindowSurfaceXRender.h"
 #endif  // MOZ_X11
@@ -2995,38 +2998,70 @@ gboolean nsWindow::OnKeyReleaseEvent(Gdk
 void nsWindow::OnScrollEvent(GdkEventScroll* aEvent) {
   // check to see if we should rollup
   if (CheckForRollup(aEvent->x_root, aEvent->y_root, true, false)) return;
 #if GTK_CHECK_VERSION(3, 4, 0)
   // check for duplicate legacy scroll event, see GNOME bug 726878
   if (aEvent->direction != GDK_SCROLL_SMOOTH &&
       mLastScrollEventTime == aEvent->time)
     return;
+  mLastScrollEventTime = aEvent->time;
 #endif
   WidgetWheelEvent wheelEvent(true, eWheel, this);
   wheelEvent.mDeltaMode = dom::WheelEvent_Binding::DOM_DELTA_LINE;
   switch (aEvent->direction) {
 #if GTK_CHECK_VERSION(3, 4, 0)
     case GDK_SCROLL_SMOOTH: {
       // As of GTK 3.4, all directional scroll events are provided by
-      // the GDK_SCROLL_SMOOTH direction on XInput2 devices.
-      mLastScrollEventTime = aEvent->time;
+      // the GDK_SCROLL_SMOOTH direction on XInput2 and Wayland devices.
+
+      // Special handling for touchpads to support flings
+      // (also known as kinetic/inertial/momentum scrolling)
+      GdkDevice* device = gdk_event_get_source_device((GdkEvent*)aEvent);
+      GdkInputSource source = gdk_device_get_source(device);
+      if (source == GDK_SOURCE_TOUCHSCREEN || source == GDK_SOURCE_TOUCHPAD) {
+        if (gtk_check_version(3, 20, 0) == nullptr) {
+          static auto sGdkEventIsScrollStopEvent =
+              (gboolean(*)(const GdkEvent*))dlsym(
+                  RTLD_DEFAULT, "gdk_event_is_scroll_stop_event");
+
+          PanGestureInput::PanGestureType eventType =
+              PanGestureInput::PANGESTURE_PAN;
+          if (sGdkEventIsScrollStopEvent((GdkEvent*)aEvent)) {
+            eventType = PanGestureInput::PANGESTURE_END;
+            mPanInProgress = false;
+          } else if (!mPanInProgress) {
+            eventType = PanGestureInput::PANGESTURE_START;
+            mPanInProgress = true;
+          }
+
+          LayoutDeviceIntPoint touchPoint = GetRefPoint(this, aEvent);
+          PanGestureInput panEvent(
+              eventType, aEvent->time, GetEventTimeStamp(aEvent->time),
+              ScreenPoint(touchPoint.x, touchPoint.y),
+              ScreenPoint(aEvent->delta_x, aEvent->delta_y),
+              KeymapWrapper::ComputeKeyModifiers(aEvent->state));
+          panEvent.mDeltaType = PanGestureInput::PANDELTA_PAGE;
+          panEvent.mSimulateMomentum = true;
+
+          DispatchPanGestureInput(panEvent);
+
+          return;
+        }
+        // Older GTK doesn't support stop events, so we can't support fling
+        // there
+        wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;
+      }
+
       // TODO - use a more appropriate scrolling unit than lines.
       // Multiply event deltas by 3 to emulate legacy behaviour.
       wheelEvent.mDeltaX = aEvent->delta_x * 3;
       wheelEvent.mDeltaY = aEvent->delta_y * 3;
       wheelEvent.mIsNoLineOrPageDelta = true;
-      // This next step manually unsets smooth scrolling for touch devices
-      // that trigger GDK_SCROLL_SMOOTH. We use the slave device, which
-      // represents the actual input.
-      GdkDevice* device = gdk_event_get_source_device((GdkEvent*)aEvent);
-      GdkInputSource source = gdk_device_get_source(device);
-      if (source == GDK_SOURCE_TOUCHSCREEN || source == GDK_SOURCE_TOUCHPAD) {
-        wheelEvent.mScrollType = WidgetWheelEvent::SCROLL_ASYNCHRONOUSELY;
-      }
+
       break;
     }
 #endif
     case GDK_SCROLL_UP:
       wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = -3;
       break;
     case GDK_SCROLL_DOWN:
       wheelEvent.mDeltaY = wheelEvent.mLineOrPageDeltaY = 3;
@@ -4237,17 +4272,18 @@ LayoutDeviceIntSize nsWindow::GetSafeWin
   // reads it as CARD16.  Sizes of pixmaps, used for drawing, are (unsigned)
   // CARD16 in the protocol, but the server's ProcCreatePixmap returns
   // BadAlloc if dimensions cannot be represented by signed shorts.
   // Because we are creating Cairo surfaces to represent window buffers,
   // we also must ensure that the window can fit in a Cairo surface.
   LayoutDeviceIntSize result = aSize;
   int32_t maxSize = 32767;
   if (mLayerManager && mLayerManager->AsKnowsCompositor()) {
-    maxSize = std::min(maxSize, mLayerManager->AsKnowsCompositor()->GetMaxTextureSize());
+    maxSize = std::min(maxSize,
+                       mLayerManager->AsKnowsCompositor()->GetMaxTextureSize());
   }
   if (result.width > maxSize) {
     result.width = maxSize;
   }
   if (result.height > maxSize) {
     result.height = maxSize;
   }
   return result;
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -486,16 +486,18 @@ class nsWindow final : public nsBaseWidg
   nsSizeMode mSizeState;
 
   nsIntPoint mClientOffset;
 
 #if GTK_CHECK_VERSION(3, 4, 0)
   // This field omits duplicate scroll events caused by GNOME bug 726878.
   guint32 mLastScrollEventTime;
 
+  bool mPanInProgress = false;
+
   // for touch event handling
   nsRefPtrHashtable<nsPtrHashKey<GdkEventSequence>, mozilla::dom::Touch>
       mTouches;
 #endif
 
 #ifdef MOZ_X11
   Display* mXDisplay;
   Window mXWindow;
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1091,16 +1091,39 @@ void nsBaseWidget::DispatchTouchInput(Mu
   } else {
     WidgetTouchEvent event = aInput.ToWidgetTouchEvent(this);
 
     nsEventStatus status;
     DispatchEvent(&event, status);
   }
 }
 
+void nsBaseWidget::DispatchPanGestureInput(PanGestureInput& aInput) {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mAPZC) {
+    MOZ_ASSERT(APZThreadUtils::IsControllerThread());
+    uint64_t inputBlockId = 0;
+    ScrollableLayerGuid guid;
+
+    nsEventStatus result =
+        mAPZC->InputBridge()->ReceiveInputEvent(aInput, &guid, &inputBlockId);
+    if (result == nsEventStatus_eConsumeNoDefault) {
+      return;
+    }
+
+    WidgetWheelEvent event = aInput.ToWidgetWheelEvent(this);
+    ProcessUntransformedAPZEvent(&event, guid, inputBlockId, result);
+  } else {
+    WidgetWheelEvent event = aInput.ToWidgetWheelEvent(this);
+
+    nsEventStatus status;
+    DispatchEvent(&event, status);
+  }
+}
+
 nsEventStatus nsBaseWidget::DispatchInputEvent(WidgetInputEvent* aEvent) {
   MOZ_ASSERT(NS_IsMainThread());
   if (mAPZC) {
     if (APZThreadUtils::IsControllerThread()) {
       uint64_t inputBlockId = 0;
       ScrollableLayerGuid guid;
 
       nsEventStatus result = mAPZC->InputBridge()->ReceiveInputEvent(
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -613,16 +613,24 @@ class nsBaseWidget : public nsIWidget, p
   /**
    * Dispatch the given MultiTouchInput through APZ to Gecko (if APZ is enabled)
    * or directly to gecko (if APZ is not enabled). This function must only
    * be called from the main thread, and if APZ is enabled, that must also be
    * the APZ controller thread.
    */
   void DispatchTouchInput(mozilla::MultiTouchInput& aInput);
 
+  /**
+   * Dispatch the given PanGestureInput through APZ to Gecko (if APZ is enabled)
+   * or directly to gecko (if APZ is not enabled). This function must only
+   * be called from the main thread, and if APZ is enabled, that must also be
+   * the APZ controller thread.
+   */
+  void DispatchPanGestureInput(mozilla::PanGestureInput& aInput);
+
 #if defined(XP_WIN)
   void UpdateScrollCapture() override;
 
   /**
    * To be overridden by derived classes to return a snapshot that can be used
    * during scrolling. Returning null means we won't update the container.
    * @return an already AddRefed SourceSurface containing the snapshot
    */
--- a/widget/nsGUIEventIPC.h
+++ b/widget/nsGUIEventIPC.h
@@ -1206,58 +1206,74 @@ struct ParamTraits<mozilla::MouseInput> 
 template <>
 struct ParamTraits<mozilla::PanGestureInput::PanGestureType>
     : public ContiguousEnumSerializerInclusive<
           mozilla::PanGestureInput::PanGestureType,
           mozilla::PanGestureInput::PanGestureType::PANGESTURE_MAYSTART,
           mozilla::PanGestureInput::sHighestPanGestureType> {};
 
 template <>
-struct ParamTraits<mozilla::PanGestureInput> {
+struct ParamTraits<mozilla::PanGestureInput::PanDeltaType>
+    : public ContiguousEnumSerializerInclusive<
+          mozilla::PanGestureInput::PanDeltaType,
+          mozilla::PanGestureInput::PanDeltaType::PANDELTA_PAGE,
+          mozilla::PanGestureInput::sHighestPanDeltaType> {};
+
+template <>
+struct ParamTraits<mozilla::PanGestureInput>
+    : BitfieldHelper<mozilla::PanGestureInput> {
   typedef mozilla::PanGestureInput paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     WriteParam(aMsg, static_cast<const mozilla::InputData&>(aParam));
     WriteParam(aMsg, aParam.mType);
     WriteParam(aMsg, aParam.mPanStartPoint);
     WriteParam(aMsg, aParam.mPanDisplacement);
     WriteParam(aMsg, aParam.mLocalPanStartPoint);
     WriteParam(aMsg, aParam.mLocalPanDisplacement);
     WriteParam(aMsg, aParam.mLineOrPageDeltaX);
     WriteParam(aMsg, aParam.mLineOrPageDeltaY);
     WriteParam(aMsg, aParam.mUserDeltaMultiplierX);
     WriteParam(aMsg, aParam.mUserDeltaMultiplierY);
+    WriteParam(aMsg, aParam.mDeltaType);
     WriteParam(aMsg, aParam.mHandledByAPZ);
     WriteParam(aMsg, aParam.mFollowedByMomentum);
     WriteParam(
         aMsg,
         aParam
             .mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection);
     WriteParam(aMsg, aParam.mOverscrollBehaviorAllowsSwipe);
+    WriteParam(aMsg, aParam.mSimulateMomentum);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
     return ReadParam(aMsg, aIter, static_cast<mozilla::InputData*>(aResult)) &&
            ReadParam(aMsg, aIter, &aResult->mType) &&
            ReadParam(aMsg, aIter, &aResult->mPanStartPoint) &&
            ReadParam(aMsg, aIter, &aResult->mPanDisplacement) &&
            ReadParam(aMsg, aIter, &aResult->mLocalPanStartPoint) &&
            ReadParam(aMsg, aIter, &aResult->mLocalPanDisplacement) &&
            ReadParam(aMsg, aIter, &aResult->mLineOrPageDeltaX) &&
            ReadParam(aMsg, aIter, &aResult->mLineOrPageDeltaY) &&
            ReadParam(aMsg, aIter, &aResult->mUserDeltaMultiplierX) &&
            ReadParam(aMsg, aIter, &aResult->mUserDeltaMultiplierY) &&
-           ReadParam(aMsg, aIter, &aResult->mHandledByAPZ) &&
-           ReadParam(aMsg, aIter, &aResult->mFollowedByMomentum) &&
-           ReadParam(
-               aMsg, aIter,
-               &aResult
-                    ->mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection) &&
-           ReadParam(aMsg, aIter, &aResult->mOverscrollBehaviorAllowsSwipe);
+           ReadParam(aMsg, aIter, &aResult->mDeltaType) &&
+           ReadBoolForBitfield(aMsg, aIter, aResult,
+                               &paramType::SetHandledByAPZ) &&
+           ReadBoolForBitfield(aMsg, aIter, aResult,
+                               &paramType::SetFollowedByMomentum) &&
+           ReadBoolForBitfield(
+               aMsg, aIter, aResult,
+               &paramType::
+                   SetRequiresContentResponseIfCannotScrollHorizontallyInStartDirection) &&
+           ReadBoolForBitfield(aMsg, aIter, aResult,
+                               &paramType::SetOverscrollBehaviorAllowsSwipe) &&
+           ReadBoolForBitfield(aMsg, aIter, aResult,
+                               &paramType::SetSimulateMomentum);
   }
 };
 
 template <>
 struct ParamTraits<mozilla::PinchGestureInput::PinchGestureType>
     : public ContiguousEnumSerializerInclusive<
           mozilla::PinchGestureInput::PinchGestureType,
           mozilla::PinchGestureInput::PinchGestureType::PINCHGESTURE_START,
--- a/widget/nsIPrintSettings.idl
+++ b/widget/nsIPrintSettings.idl
@@ -65,30 +65,23 @@ interface nsIPrintSettings : nsISupports
   const long kPrintOddPages     = 0x00000001;
   const long kPrintEvenPages    = 0x00000002;
   const long kEnableSelectionRB = 0x00000004;
 
   /* Print Range Enums */
   const long kRangeAllPages           = 0;
   const long kRangeSpecifiedPageRange = 1;
   const long kRangeSelection          = 2;
-  const long kRangeFocusFrame         = 3;
 
   /* Justification Enums */
   const long kJustLeft   = 0;
   const long kJustCenter = 1;
   const long kJustRight  = 2;
 
   /**
-   * FrameSet Default Type Constants
-   */
-  const short kUseInternalDefault    = 0;
-  const short kUseSettingWhenPossible = 1;
-
-  /**
    * Page Size Type Constants
    */
   const short kPaperSizeNativeData  = 0;
   const short kPaperSizeDefined     = 1;
 
   /**
    * Page Size Unit Constants
    */
@@ -210,17 +203,16 @@ interface nsIPrintSettings : nsISupports
 
   attribute AString footerStrLeft;
   attribute AString footerStrCenter;
   attribute AString footerStrRight;
 
   attribute short   howToEnableFrameUI;  /* indicates how to enable the frameset UI            */
   attribute boolean isCancelled;         /* indicates whether the print job has been cancelled */
   readonly attribute boolean saveOnCancel;        /* indicates whether the print settings should be saved after a cancel */
-  attribute short   printFrameTypeUsage; /* indicates whether to use the interal value or not  */
   attribute short   printFrameType;
   attribute boolean printSilent;         /* print without putting up the dialog */
   attribute boolean shrinkToFit;         /* shrinks content to fit on page      */
   attribute boolean showPrintProgress;   /* indicates whether the progress dialog should be shown */
 
   /* Additional XP Related */
   attribute AString paperName;     /* name of paper */
   attribute short   paperData;     /* native data value */
--- a/widget/nsIPrintSettingsService.idl
+++ b/widget/nsIPrintSettingsService.idl
@@ -82,17 +82,17 @@ interface nsIPrintSettingsService : nsIS
    *
    * aPS - PrintSettings to have its settings read
    * aUsePrinterNamePrefix - indicates whether to use the printer name as a prefix
    * aFlags - indicates which prefs to read, see nsIPrintSettings.idl for the
    *          const values.
    *
    * Items not read:
    *   startPageRange, endPageRange, scaling, printRange, title
-   *   docURL, howToEnableFrameUI, isCancelled, printFrameTypeUsage
+   *   docURL, howToEnableFrameUI, isCancelled,
    *   printFrameType, printSilent, shrinkToFit, numCopies,
    *   printerName
    *
    */
   void initPrintSettingsFromPrefs(in nsIPrintSettings aPrintSettings, in boolean aUsePrinterNamePrefix, in unsigned long aFlags);
 
   /**
    * Writes PrintSettings values to Prefs,
@@ -103,17 +103,17 @@ interface nsIPrintSettingsService : nsIS
    * If a PrinterName is there, then it saves the items qualified for that Printer
    *
    * aPS - PrintSettings to have its settings saved
    * aUsePrinterNamePrefix - indicates whether to use the printer name as a prefix
    * aFlags - indicates which prefs to save, see nsIPrintSettings.idl for the const values.
    *
    * Items not written:
    *   startPageRange, endPageRange, scaling, printRange, title
-   *   docURL, howToEnableFrameUI, isCancelled, printFrameTypeUsage
+   *   docURL, howToEnableFrameUI, isCancelled,
    *   printFrameType, printSilent, shrinkToFit, numCopies
    *
    */
   void savePrintSettingsToPrefs(in nsIPrintSettings aPrintSettings, in boolean aUsePrinterNamePrefix, in unsigned long aFlags);
 
   /**
    * Given some nsIPrintSettings and (optionally) an nsIWebBrowserPrint,
    * populates a PrintData representing them which can be sent over IPC. Values
--- a/widget/nsPrintSettingsImpl.cpp
+++ b/widget/nsPrintSettingsImpl.cpp
@@ -15,17 +15,16 @@ NS_IMPL_ISUPPORTS(nsPrintSettings, nsIPr
 nsPrintSettings::nsPrintSettings()
     : mPrintOptions(0L),
       mPrintRange(kRangeAllPages),
       mStartPageNum(1),
       mEndPageNum(1),
       mScaling(1.0),
       mPrintBGColors(false),
       mPrintBGImages(false),
-      mPrintFrameTypeUsage(kUseInternalDefault),
       mPrintFrameType(kFramesAsIs),
       mHowToEnableFrameUI(kFrameEnableNone),
       mIsCancelled(false),
       mSaveOnCancel(true),
       mPrintSilent(false),
       mShrinkToFit(true),
       mShowPrintProgress(true),
       mPrintPageDelay(50),
@@ -532,28 +531,16 @@ NS_IMETHODIMP nsPrintSettings::GetFooter
   aTitle = mFooterStrs[2];
   return NS_OK;
 }
 NS_IMETHODIMP nsPrintSettings::SetFooterStrRight(const nsAString& aTitle) {
   mFooterStrs[2] = aTitle;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsPrintSettings::GetPrintFrameTypeUsage(
-    int16_t* aPrintFrameTypeUsage) {
-  NS_ENSURE_ARG_POINTER(aPrintFrameTypeUsage);
-  *aPrintFrameTypeUsage = mPrintFrameTypeUsage;
-  return NS_OK;
-}
-NS_IMETHODIMP nsPrintSettings::SetPrintFrameTypeUsage(
-    int16_t aPrintFrameTypeUsage) {
-  mPrintFrameTypeUsage = aPrintFrameTypeUsage;
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsPrintSettings::GetPrintFrameType(int16_t* aPrintFrameType) {
   NS_ENSURE_ARG_POINTER(aPrintFrameType);
   *aPrintFrameType = (int32_t)mPrintFrameType;
   return NS_OK;
 }
 NS_IMETHODIMP nsPrintSettings::SetPrintFrameType(int16_t aPrintFrameType) {
   mPrintFrameType = aPrintFrameType;
   return NS_OK;
@@ -794,17 +781,16 @@ nsPrintSettings& nsPrintSettings::operat
   mScaling = rhs.mScaling;
   mPrintBGColors = rhs.mPrintBGColors;
   mPrintBGImages = rhs.mPrintBGImages;
   mPrintRange = rhs.mPrintRange;
   mTitle = rhs.mTitle;
   mURL = rhs.mURL;
   mHowToEnableFrameUI = rhs.mHowToEnableFrameUI;
   mIsCancelled = rhs.mIsCancelled;
-  mPrintFrameTypeUsage = rhs.mPrintFrameTypeUsage;
   mPrintFrameType = rhs.mPrintFrameType;
   mPrintSilent = rhs.mPrintSilent;
   mShrinkToFit = rhs.mShrinkToFit;
   mShowPrintProgress = rhs.mShowPrintProgress;
   mPaperName = rhs.mPaperName;
   mPaperData = rhs.mPaperData;
   mPaperWidth = rhs.mPaperWidth;
   mPaperHeight = rhs.mPaperHeight;
--- a/widget/nsPrintSettingsImpl.h
+++ b/widget/nsPrintSettingsImpl.h
@@ -50,17 +50,16 @@ class nsPrintSettings : public nsIPrintS
   // scriptable data members
   int16_t mPrintRange;
   int32_t mStartPageNum;  // only used for ePrintRange_SpecifiedRange
   int32_t mEndPageNum;
   double mScaling;
   bool mPrintBGColors;  // print background colors
   bool mPrintBGImages;  // print background images
 
-  int16_t mPrintFrameTypeUsage;
   int16_t mPrintFrameType;
   int16_t mHowToEnableFrameUI;
   bool mIsCancelled;
   bool mSaveOnCancel;
   bool mPrintSilent;
   bool mShrinkToFit;
   bool mShowPrintProgress;
   int32_t mPrintPageDelay;
--- a/widget/nsPrintSettingsService.cpp
+++ b/widget/nsPrintSettingsService.cpp
@@ -130,17 +130,16 @@ nsPrintSettingsService::SerializeToPrint
   aSettings->GetHeaderStrRight(data->headerStrRight());
 
   aSettings->GetFooterStrLeft(data->footerStrLeft());
   aSettings->GetFooterStrCenter(data->footerStrCenter());
   aSettings->GetFooterStrRight(data->footerStrRight());
 
   aSettings->GetHowToEnableFrameUI(&data->howToEnableFrameUI());
   aSettings->GetIsCancelled(&data->isCancelled());
-  aSettings->GetPrintFrameTypeUsage(&data->printFrameTypeUsage());
   aSettings->GetPrintFrameType(&data->printFrameType());
   aSettings->GetPrintSilent(&data->printSilent());
   aSettings->GetShrinkToFit(&data->shrinkToFit());
   aSettings->GetShowPrintProgress(&data->showPrintProgress());
 
   aSettings->GetPaperName(data->paperName());
   aSettings->GetPaperData(&data->paperData());
   aSettings->GetPaperWidth(&data->paperWidth());
@@ -238,17 +237,16 @@ nsPrintSettingsService::DeserializeToPri
 
   // Footer strings...
   settings->SetFooterStrLeft(data.footerStrLeft());
   settings->SetFooterStrCenter(data.footerStrCenter());
   settings->SetFooterStrRight(data.footerStrRight());
 
   settings->SetHowToEnableFrameUI(data.howToEnableFrameUI());
   settings->SetIsCancelled(data.isCancelled());
-  settings->SetPrintFrameTypeUsage(data.printFrameTypeUsage());
   settings->SetPrintFrameType(data.printFrameType());
   settings->SetPrintSilent(data.printSilent());
   settings->SetShrinkToFit(data.shrinkToFit());
   settings->SetShowPrintProgress(data.showPrintProgress());
 
   settings->SetPaperName(data.paperName());
 
   settings->SetPaperData(data.paperData());