Merge autoland to mozilla-central. a=merge
authorMarian-Vasile Laza <mlaza@mozilla.com>
Sat, 26 Jun 2021 00:46:53 +0300
changeset 584438 b9a82200b994f1d8c24f4cc2881b01f245c82757
parent 584363 634121e78fb63a84a8cc721db053a4ca1135b2c9 (current diff)
parent 584437 13a8f52f46525ccc76e7f0861137ade422a8f8da (diff)
child 584478 a1125ca12e8090bf6e8b0752f40984bc3398e0e3
push id38565
push usermlaza@mozilla.com
push dateFri, 25 Jun 2021 21:51:52 +0000
treeherdermozilla-central@b9a82200b994 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone91.0a1
first release with
nightly linux32
b9a82200b994 / 91.0a1 / 20210625215152 / files
nightly linux64
b9a82200b994 / 91.0a1 / 20210625215152 / files
nightly mac
b9a82200b994 / 91.0a1 / 20210625215152 / files
nightly win32
b9a82200b994 / 91.0a1 / 20210625215152 / files
nightly win64
b9a82200b994 / 91.0a1 / 20210625215152 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -10,17 +10,17 @@ rev = "4af6c367603869a30fddb5ffb0aba2b94
 [source."https://github.com/msirringhaus/minidump_writer_linux.git"]
 git = "https://github.com/msirringhaus/minidump_writer_linux.git"
 replace-with = "vendored-sources"
 rev = "5cea1c9a3d8ed3ed2d7bdd5be3285e7821400b7f"
 
 [source."https://github.com/mozilla/neqo"]
 git = "https://github.com/mozilla/neqo"
 replace-with = "vendored-sources"
-tag = "v0.4.25"
+tag = "v0.4.26"
 
 [source."https://github.com/mozilla/mp4parse-rust"]
 git = "https://github.com/mozilla/mp4parse-rust"
 replace-with = "vendored-sources"
 rev = "1bb484e96ae724309e3346968e8ffd4c25e61616"
 
 [source."https://github.com/mozilla/cubeb-pulse-rs"]
 git = "https://github.com/mozilla/cubeb-pulse-rs"
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3202,72 +3202,72 @@ dependencies = [
  "petgraph",
  "rose_tree",
  "spirv_headers",
  "thiserror",
 ]
 
 [[package]]
 name = "neqo-common"
-version = "0.4.25"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
+version = "0.4.26"
+source = "git+https://github.com/mozilla/neqo?tag=v0.4.26#2ad6f5f694e5bf11c61d5fd38ced8685d127fbb7"
 dependencies = [
  "chrono",
  "env_logger",
  "lazy_static",
  "log",
  "qlog",
  "winapi",
 ]
 
 [[package]]
 name = "neqo-crypto"
-version = "0.4.25"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
+version = "0.4.26"
+source = "git+https://github.com/mozilla/neqo?tag=v0.4.26#2ad6f5f694e5bf11c61d5fd38ced8685d127fbb7"
 dependencies = [
  "bindgen",
  "log",
  "neqo-common",
  "serde",
  "serde_derive",
  "toml",
 ]
 
 [[package]]
 name = "neqo-http3"
-version = "0.4.25"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
+version = "0.4.26"
+source = "git+https://github.com/mozilla/neqo?tag=v0.4.26#2ad6f5f694e5bf11c61d5fd38ced8685d127fbb7"
 dependencies = [
  "log",
  "neqo-common",
  "neqo-crypto",
  "neqo-qpack",
  "neqo-transport",
  "qlog",
  "smallvec",
 ]
 
 [[package]]
 name = "neqo-qpack"
-version = "0.4.25"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
+version = "0.4.26"
+source = "git+https://github.com/mozilla/neqo?tag=v0.4.26#2ad6f5f694e5bf11c61d5fd38ced8685d127fbb7"
 dependencies = [
  "lazy_static",
  "log",
  "neqo-common",
  "neqo-crypto",
  "neqo-transport",
  "qlog",
  "static_assertions",
 ]
 
 [[package]]
 name = "neqo-transport"
-version = "0.4.25"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.25#13b5a13022419babba7183f768a974edfe2bd58b"
+version = "0.4.26"
+source = "git+https://github.com/mozilla/neqo?tag=v0.4.26#2ad6f5f694e5bf11c61d5fd38ced8685d127fbb7"
 dependencies = [
  "indexmap",
  "lazy_static",
  "log",
  "neqo-common",
  "neqo-crypto",
  "qlog",
  "smallvec",
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -71,16 +71,23 @@ if (AppConstants.MOZ_BACKGROUNDTASKS) {
   gExceptionPaths.push("resource://gre/modules/backgroundtasks/");
 }
 
 // Bug 1710546 https://bugzilla.mozilla.org/show_bug.cgi?id=1710546
 if (AppConstants.NIGHTLY_BUILD) {
   gExceptionPaths.push("resource://builtin-addons/translations/");
 }
 
+if (AppConstants.NIGHTLY_BUILD) {
+  // This is nightly-only debug tool.
+  gExceptionPaths.push(
+    "chrome://browser/content/places/interactionsViewer.html"
+  );
+}
+
 // Each whitelist entry should have a comment indicating which file is
 // referencing the whitelisted file in a way that the test can't detect, or a
 // bug number to remove or use the file if it is indeed currently unreferenced.
 var whitelist = [
   // pocket/content/panels/tmpl/loggedoutvariants/variant_a.handlebars
   { file: "chrome://pocket/content/panels/img/glyph.svg" },
 
   // toolkit/components/pdfjs/content/PdfStreamConverter.jsm
--- a/browser/components/doh/DoHController.jsm
+++ b/browser/components/doh/DoHController.jsm
@@ -119,17 +119,17 @@ const ROLLOUT_URI_PREF = "doh-rollout.ur
 
 const TRR_SELECT_DRY_RUN_RESULT_PREF =
   "doh-rollout.trr-selection.dry-run-result";
 
 const HEURISTICS_TELEMETRY_CATEGORY = "doh";
 const TRRSELECT_TELEMETRY_CATEGORY = "security.doh.trrPerformance";
 
 const kLinkStatusChangedTopic = "network:link-status-changed";
-const kConnectivityTopic = "network:captive-portal-connectivity";
+const kConnectivityTopic = "network:captive-portal-connectivity-changed";
 const kPrefChangedTopic = "nsPref:changed";
 
 // Helper function to hash the network ID concatenated with telemetry client ID.
 // This prevents us from being able to tell if 2 clients are on the same network.
 function getHashedNetworkID() {
   let currentNetworkID = gNetworkLinkService.networkID;
   if (!currentNetworkID) {
     return "";
--- a/browser/components/newtab/content-src/asrouter/docs/targeting-attributes.md
+++ b/browser/components/newtab/content-src/asrouter/docs/targeting-attributes.md
@@ -44,16 +44,17 @@ Please note that some targeting attribut
 * [isChinaRepack](#ischinarepack)
 * [userId](#userid)
 * [profileRestartCount](#profilerestartcount)
 * [homePageSettings](#homepagesettings)
 * [newtabSettings](#newtabsettings)
 * [isFissionExperimentEnabled](#isfissionexperimentenabled)
 * [activeNotifications](#activenotifications)
 * [isMajorUpgrade](#ismajorupgrade)
+* [hasActiveEnterprisePolicies](#hasactiveenterprisepolicies)
 
 ## Detailed usage
 
 ### `addonsInfo`
 Provides information about the add-ons the user has installed.
 
 Note that the `name`, `userDisabled`, and `installDate` is only available if `isFullData` is `true` (this is usually not the case right at start-up).
 
@@ -805,9 +806,13 @@ A boolean. `true` if we're running Fissi
 
 ### `activeNotifications`
 
 True when an infobar style message is displayed or when the awesomebar is
 expanded to show a message (for example onboarding tips).
 
 ### `isMajorUpgrade`
 
-A boolean. `true` is the browser just updated to a new major version.
+A boolean. `true` if the browser just updated to a new major version.
+
+### `hasActiveEnterprisePolicies`
+
+A boolean. `true` if any Enterprise Policies are active.
--- a/browser/components/newtab/lib/ASRouterTargeting.jsm
+++ b/browser/components/newtab/lib/ASRouterTargeting.jsm
@@ -625,16 +625,20 @@ const TargetingGetters = {
     }
 
     return false;
   },
 
   get isMajorUpgrade() {
     return BrowserHandler.majorUpgrade;
   },
+
+  get hasActiveEnterprisePolicies() {
+    return Services.policies.status === Services.policies.ACTIVE;
+  },
 };
 
 this.ASRouterTargeting = {
   Environment: TargetingGetters,
 
   isTriggerMatch(trigger = {}, candidateMessageTrigger = {}) {
     if (trigger.id !== candidateMessageTrigger.id) {
       return false;
--- a/browser/components/newtab/test/xpcshell/test_ASRouterTargeting_attribution.js
+++ b/browser/components/newtab/test/xpcshell/test_ASRouterTargeting_attribution.js
@@ -8,16 +8,19 @@ const { AttributionCode } = ChromeUtils.
   "resource:///modules/AttributionCode.jsm"
 );
 const { ASRouterTargeting } = ChromeUtils.import(
   "resource://activity-stream/lib/ASRouterTargeting.jsm"
 );
 const { MacAttribution } = ChromeUtils.import(
   "resource:///modules/MacAttribution.jsm"
 );
+const { EnterprisePolicyTesting } = ChromeUtils.import(
+  "resource://testing-common/EnterprisePolicyTesting.jsm"
+);
 
 add_task(async function check_attribution_data() {
   // Some setup to fake the correct attribution data
   const appPath = MacAttribution.applicationPath;
   const attributionSvc = Cc["@mozilla.org/mac-attribution;1"].getService(
     Ci.nsIMacAttributionService
   );
   const campaign = "non-fx-button";
@@ -57,8 +60,41 @@ add_task(async function check_attributio
 
   equal(
     await ASRouterTargeting.findMatchingMessage({ messages }),
     messages[1],
     "should select the message with the correct campaign and source"
   );
   AttributionCode._clearCache();
 });
+
+add_task(async function check_enterprise_targeting() {
+  const messages = [
+    {
+      id: "foo1",
+      targeting: "hasActiveEnterprisePolicies",
+    },
+    {
+      id: "foo2",
+      targeting: "!hasActiveEnterprisePolicies",
+    },
+  ];
+
+  equal(
+    await ASRouterTargeting.findMatchingMessage({ messages }),
+    messages[1],
+    "should select the message for policies turned off"
+  );
+
+  await EnterprisePolicyTesting.setupPolicyEngineWithJson({
+    policies: {
+      DisableFirefoxStudies: {
+        Value: true,
+      },
+    },
+  });
+
+  equal(
+    await ASRouterTargeting.findMatchingMessage({ messages }),
+    messages[0],
+    "should select the message for policies turned on"
+  );
+});
--- a/browser/components/places/jar.mn
+++ b/browser/components/places/jar.mn
@@ -16,8 +16,13 @@ browser.jar:
     content/browser/places/controller.js                 (content/controller.js)
     content/browser/places/treeView.js                   (content/treeView.js)
     content/browser/places/browserPlacesViews.js         (content/browserPlacesViews.js)
 *   content/browser/places/historySidebar.xhtml          (content/historySidebar.xhtml)
     content/browser/places/historySidebar.js             (content/historySidebar.js)
 *   content/browser/places/bookmarksSidebar.xhtml        (content/bookmarksSidebar.xhtml)
     content/browser/places/bookmarksSidebar.js           (content/bookmarksSidebar.js)
     content/browser/places/editBookmark.js               (content/editBookmark.js)
+#ifdef NIGHTLY_BUILD
+    content/browser/places/interactionsViewer.css        (metadataViewer/interactionsViewer.css)
+    content/browser/places/interactionsViewer.html       (metadataViewer/interactionsViewer.html)
+    content/browser/places/interactionsViewer.js         (metadataViewer/interactionsViewer.js)
+#endif
new file mode 100644
--- /dev/null
+++ b/browser/components/places/metadataViewer/interactionsViewer.css
@@ -0,0 +1,47 @@
+/* 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/. */
+
+body {
+  padding: .5em 2em;
+}
+
+.hidden {
+  display: none;
+}
+
+.message-bar-icon {
+  vertical-align: middle;
+}
+
+#metadataLimit {
+  padding-bottom: 1em;
+}
+
+#metadataViewer {
+  display: grid;
+  grid-template-columns: max-content fit-content(100%) repeat(4, max-content);
+}
+
+#metadataViewer > div {
+  padding: .3em 1em;
+  overflow-x: hidden;
+	text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* Sets the first row of elements to bold. The number is the number of columns */
+#metadataViewer > div:nth-child(-n+6) {
+  font-weight: bold;
+}
+
+/* Highlights every other row to make visual scanning of the table easier.
+   The numbers need to be adapted if the number of columns changes. */
+#metadataViewer > div:nth-child(6n+7):nth-child(12n+7),
+#metadataViewer > div:nth-child(6n+8):nth-child(12n+8),
+#metadataViewer > div:nth-child(6n+9):nth-child(12n+9),
+#metadataViewer > div:nth-child(6n+10):nth-child(12n+10),
+#metadataViewer > div:nth-child(6n+11):nth-child(12n+11),
+#metadataViewer > div:nth-child(6n+12):nth-child(12n+12) {
+  background: var(--in-content-box-background-odd);
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/places/metadataViewer/interactionsViewer.html
@@ -0,0 +1,28 @@
+<!--
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>Interactions Debug Viewer</title>
+    <script type="module" src="chrome://browser/content/places/interactionsViewer.js"></script>
+    <link rel="stylesheet" type="text/css" href="chrome://global/skin/in-content/common.css">
+    <link rel="stylesheet" type="text/css" href="chrome://browser/content/places/interactionsViewer.css">
+  </head>
+  <body>
+    <div id="enabledWarning" class="message-bar message-bar-warning" hidden>
+      <img class="message-bar-icon" src="chrome://browser/skin/warning.svg">
+      <descripton class="message-bar-description">
+        You need to have <code>browser.places.interactions.enabled</code>
+        set to true (and restart) for metadata recording to be enabled.
+      </descripton>
+    </div>
+    <h1>Page Metadata</h1>
+    <div id="metadataLimit"></div>
+    <div id="metadataViewer">
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/places/metadataViewer/interactionsViewer.js
@@ -0,0 +1,146 @@
+/* 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/. */
+
+/* eslint-env module */
+
+const { Interactions } = ChromeUtils.import(
+  "resource:///modules/Interactions.jsm"
+);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { PlacesUtils } = ChromeUtils.import(
+  "resource://gre/modules/PlacesUtils.jsm"
+);
+
+const metadataHandler = new (class {
+  /**
+   * Maximum number of rows to display by default.
+   *
+   * @typedef {number}
+   */
+  #maxRows = 100;
+
+  /**
+   * A reference to the database connection.
+   *
+   * @typedef {mozIStorageConnection}
+   */
+  #db = null;
+
+  /**
+   * A map of columns that are displayed by default.
+   * @note If you change the number of columns, then you also need to change
+   *       the css to account for the new number.
+   *
+   * - The key is the column name in the database.
+   * - The header is the column header on the table.
+   * - The modifier is a function to modify the returned value from the database
+   *   for display.
+   * - includeTitle determines if the title attribute should be set on that
+   *   column, for tooltips, e.g. if an element is likely to overflow.
+   *
+   * @typedef {Map<string, object>}
+   */
+  #columnMap = new Map([
+    ["id", { header: "ID" }],
+    ["url", { header: "URL", includeTitle: true }],
+    [
+      "updated_at",
+      {
+        header: "Updated",
+        modifier: updatedAt => new Date(updatedAt).toLocaleString(),
+      },
+    ],
+    [
+      "total_view_time",
+      {
+        header: "View Time (s)",
+        modifier: totalViewTime => (totalViewTime / 1000).toFixed(2),
+      },
+    ],
+    [
+      "typing_time",
+      {
+        header: "Typing Time (s)",
+        modifier: typingTime => (typingTime / 1000).toFixed(2),
+      },
+    ],
+    ["key_presses", { header: "Key Presses" }],
+  ]);
+
+  async start() {
+    this.#setupUI();
+    this.#db = await PlacesUtils.promiseDBConnection();
+    await this.#updateDisplay();
+    setInterval(this.#updateDisplay.bind(this), 10000);
+  }
+
+  /**
+   * Creates the initial table layout to the correct size.
+   */
+  #setupUI() {
+    let tableBody = document.createDocumentFragment();
+    let header = document.createDocumentFragment();
+    for (let details of this.#columnMap.values()) {
+      let columnDiv = document.createElement("div");
+      columnDiv.textContent = details.header;
+      header.appendChild(columnDiv);
+    }
+    tableBody.appendChild(header);
+
+    for (let i = 0; i < this.#maxRows; i++) {
+      let row = document.createDocumentFragment();
+      for (let j = 0; j < this.#columnMap.size; j++) {
+        row.appendChild(document.createElement("div"));
+      }
+      tableBody.appendChild(row);
+    }
+    let viewer = document.getElementById("metadataViewer");
+    viewer.appendChild(tableBody);
+
+    let limit = document.getElementById("metadataLimit");
+    limit.textContent = `Maximum rows displayed: ${this.#maxRows}.`;
+  }
+
+  /**
+   * Loads the current metadata from the database and updates the display.
+   */
+  async #updateDisplay() {
+    let rows = await this.#db.executeCached(
+      `SELECT m.id AS id, h.url AS url, updated_at, total_view_time,
+              typing_time, key_presses FROM moz_places_metadata m
+       JOIN moz_places h ON h.id = m.place_id
+       ORDER BY updated_at DESC
+       LIMIT ${this.#maxRows}`
+    );
+    let viewer = document.getElementById("metadataViewer");
+    let index = this.#columnMap.size;
+    for (let row of rows) {
+      for (let [column, details] of this.#columnMap.entries()) {
+        let value = row.getResultByName(column);
+
+        if (details.includeTitle) {
+          viewer.children[index].setAttribute("title", value);
+        }
+
+        viewer.children[index].textContent = details.modifier
+          ? details.modifier(value)
+          : value;
+
+        index++;
+      }
+    }
+  }
+})();
+
+function checkPrefs() {
+  if (
+    !Services.prefs.getBoolPref("browser.places.interactions.enabled", false)
+  ) {
+    let warning = document.getElementById("enabledWarning");
+    warning.hidden = false;
+  }
+}
+
+checkPrefs();
+metadataHandler.start().catch(console.error);
--- a/browser/components/preferences/tests/browser.ini
+++ b/browser/components/preferences/tests/browser.ini
@@ -74,16 +74,18 @@ skip-if = tsan || ccov && (os == 'linux'
 [browser_languages_subdialog.js]
 [browser_browser_languages_subdialog.js]
 skip-if = tsan || (!debug && os == 'win') # Bug 1518370
 [browser_layersacceleration.js]
 [browser_localSearchShortcuts.js]
 [browser_masterpassword.js]
 [browser_newtab_menu.js]
 [browser_notifications_do_not_disturb.js]
+[browser_open_download_preferences.js]
+support-files = empty_pdf_file.pdf
 [browser_password_management.js]
 [browser_performance.js]
 [browser_performance_content_process_limit.js]
 skip-if = !e10s
 [browser_performance_e10srollout.js]
 skip-if = !e10s
 [browser_performance_non_e10s.js]
 skip-if = e10s
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/tests/browser_open_download_preferences.js
@@ -0,0 +1,279 @@
+const { HandlerServiceTestUtils } = ChromeUtils.import(
+  "resource://testing-common/HandlerServiceTestUtils.jsm"
+);
+
+const { DownloadIntegration } = ChromeUtils.import(
+  "resource://gre/modules/DownloadIntegration.jsm"
+);
+
+const TEST_PATH = getRootDirectory(gTestPath).replace(
+  "chrome://mochitests/content",
+  "https://example.com"
+);
+
+async function selectPdfCategoryItem() {
+  await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true });
+  info("Preferences page opened on the general pane.");
+
+  await gBrowser.selectedBrowser.contentWindow.promiseLoadHandlersList;
+  info("Apps list loaded.");
+
+  let win = gBrowser.selectedBrowser.contentWindow;
+  let container = win.document.getElementById("handlersView");
+  let pdfCategory = container.querySelector(
+    "richlistitem[type='application/pdf']"
+  );
+
+  pdfCategory.closest("richlistbox").selectItem(pdfCategory);
+  Assert.ok(pdfCategory.selected, "Should be able to select our item.");
+
+  return pdfCategory;
+}
+
+async function selectItemInPopup(item, list) {
+  let popup = list.menupopup;
+  let popupShown = BrowserTestUtils.waitForEvent(popup, "popupshown");
+  // Synthesizing the mouse on the .actionsMenu menulist somehow just selects
+  // the top row. Probably something to do with the multiple layers of anon
+  // content - workaround by using the `.open` setter instead.
+  list.open = true;
+  await popupShown;
+  let popupHidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
+
+  item.click();
+  popup.hidePopup();
+  await popupHidden;
+  return item;
+}
+
+function downloadHadFinished(publicList) {
+  return new Promise(resolve => {
+    publicList.addView({
+      onDownloadChanged(download) {
+        if (download.succeeded || download.error) {
+          publicList.removeView(this);
+          resolve(download);
+        }
+      },
+    });
+  });
+}
+
+async function removeTheFile(download) {
+  Assert.ok(
+    await OS.File.exists(download.target.path),
+    "The file should have been downloaded."
+  );
+
+  try {
+    info("removing " + download.target.path);
+    if (Services.appinfo.OS === "WINNT") {
+      // We need to make the file writable to delete it on Windows.
+      await IOUtils.setPermissions(download.target.path, 0o600);
+    }
+    await IOUtils.remove(download.target.path);
+  } catch (ex) {
+    info("The file " + download.target.path + " is not removed, " + ex);
+  }
+}
+
+add_task(async function alwaysAskPreferenceWorks() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.download.improvements_to_download_panel", true],
+      ["browser.download.useDownloadDir", true],
+    ],
+  });
+
+  let pdfCategory = await selectPdfCategoryItem();
+  let list = pdfCategory.querySelector(".actionsMenu");
+
+  let alwaysAskItem = list.querySelector(
+    `menuitem[action='${Ci.nsIHandlerInfo.alwaysAsk}']`
+  );
+
+  await selectItemInPopup(alwaysAskItem, list);
+  Assert.equal(
+    list.selectedItem,
+    alwaysAskItem,
+    "Should have selected 'always ask' for pdf"
+  );
+  let alwaysAskBeforeHandling = HandlerServiceTestUtils.getHandlerInfo(
+    pdfCategory.getAttribute("type")
+  ).alwaysAskBeforeHandling;
+  Assert.ok(
+    alwaysAskBeforeHandling,
+    "Should have turned on 'always asking before handling'"
+  );
+
+  let domWindowPromise = BrowserTestUtils.domWindowOpenedAndLoaded();
+  let loadingTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    TEST_PATH + "empty_pdf_file.pdf"
+  );
+
+  let domWindow = await domWindowPromise;
+  let dialog = domWindow.document.querySelector("#unknownContentType");
+  let button = dialog.getButton("cancel");
+
+  await TestUtils.waitForCondition(
+    () => !button.disabled,
+    "Wait for Cancel button to get enabled"
+  );
+  Assert.ok(dialog, "Dialog should be shown");
+  dialog.cancelDialog();
+  BrowserTestUtils.removeTab(loadingTab);
+
+  gBrowser.removeCurrentTab();
+});
+
+add_task(async function handleInternallyPreferenceWorks() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.download.improvements_to_download_panel", true],
+      ["browser.download.useDownloadDir", true],
+    ],
+  });
+
+  let pdfCategory = await selectPdfCategoryItem();
+  let list = pdfCategory.querySelector(".actionsMenu");
+
+  let handleInternallyItem = list.querySelector(
+    `menuitem[action='${Ci.nsIHandlerInfo.handleInternally}']`
+  );
+
+  await selectItemInPopup(handleInternallyItem, list);
+  Assert.equal(
+    list.selectedItem,
+    handleInternallyItem,
+    "Should have selected 'handle internally' for pdf"
+  );
+
+  let loadingTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    TEST_PATH + "empty_pdf_file.pdf"
+  );
+
+  await ContentTask.spawn(loadingTab.linkedBrowser, null, async () => {
+    await ContentTaskUtils.waitForCondition(
+      () => content.document.readyState == "complete"
+    );
+    Assert.ok(
+      content.document.querySelector("div#viewer"),
+      "document content has viewer UI"
+    );
+  });
+
+  BrowserTestUtils.removeTab(loadingTab);
+
+  gBrowser.removeCurrentTab();
+});
+
+add_task(async function saveToDiskPreferenceWorks() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.download.improvements_to_download_panel", true],
+      ["browser.download.useDownloadDir", true],
+    ],
+  });
+
+  let pdfCategory = await selectPdfCategoryItem();
+  let list = pdfCategory.querySelector(".actionsMenu");
+
+  let saveToDiskItem = list.querySelector(
+    `menuitem[action='${Ci.nsIHandlerInfo.saveToDisk}']`
+  );
+
+  await selectItemInPopup(saveToDiskItem, list);
+  Assert.equal(
+    list.selectedItem,
+    saveToDiskItem,
+    "Should have selected 'save to disk' for pdf"
+  );
+
+  let publicList = await Downloads.getList(Downloads.PUBLIC);
+  registerCleanupFunction(async () => {
+    await publicList.removeFinished();
+  });
+
+  let downloadFinishedPromise = downloadHadFinished(publicList);
+
+  let loadingTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    TEST_PATH + "empty_pdf_file.pdf"
+  );
+
+  let download = await downloadFinishedPromise;
+  BrowserTestUtils.removeTab(loadingTab);
+
+  await removeTheFile(download);
+
+  gBrowser.removeCurrentTab();
+});
+
+add_task(async function useSystemDefaultPreferenceWorks() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.download.improvements_to_download_panel", true],
+      ["browser.download.useDownloadDir", true],
+    ],
+  });
+
+  let pdfCategory = await selectPdfCategoryItem();
+  let list = pdfCategory.querySelector(".actionsMenu");
+
+  let useSystemDefaultItem = list.querySelector(
+    `menuitem[action='${Ci.nsIHandlerInfo.useSystemDefault}']`
+  );
+
+  // Whether there's a "use default" item depends on the OS, there might not be a system default viewer.
+  if (!useSystemDefaultItem) {
+    info(
+      "No 'Use default' item, so no testing for setting 'use system default' preference"
+    );
+    gBrowser.removeCurrentTab();
+    return;
+  }
+
+  await selectItemInPopup(useSystemDefaultItem, list);
+  Assert.equal(
+    list.selectedItem,
+    useSystemDefaultItem,
+    "Should have selected 'use system default' for pdf"
+  );
+
+  let oldLaunchFile = DownloadIntegration.launchFile;
+
+  let waitForLaunchFileCalled = new Promise(resolve => {
+    DownloadIntegration.launchFile = () => {
+      ok(true, "The file should be launched with an external application");
+      resolve();
+    };
+  });
+
+  let publicList = await Downloads.getList(Downloads.PUBLIC);
+  registerCleanupFunction(async () => {
+    await publicList.removeFinished();
+  });
+
+  let downloadFinishedPromise = downloadHadFinished(publicList);
+
+  let loadingTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    TEST_PATH + "empty_pdf_file.pdf"
+  );
+
+  info("Downloading had finished");
+  let download = await downloadFinishedPromise;
+
+  info("Waiting until DownloadIntegration.launchFile is called");
+  await waitForLaunchFileCalled;
+
+  DownloadIntegration.launchFile = oldLaunchFile;
+
+  await removeTheFile(download);
+
+  BrowserTestUtils.removeTab(loadingTab);
+
+  gBrowser.removeCurrentTab();
+});
new file mode 100644
--- a/browser/components/search/test/browser/browser_searchbar_focus_timing.js
+++ b/browser/components/search/test/browser/browser_searchbar_focus_timing.js
@@ -15,36 +15,29 @@ add_task(async function setup() {
 
   registerCleanupFunction(async function() {
     Services.search.defaultEngine = defaultEngine;
     gCUITestUtils.removeSearchBar();
   });
 });
 
 add_task(async function() {
-  info("Open a page");
-  const tab = await BrowserTestUtils.openNewForegroundTab(
-    gBrowser,
-    "http://example.com"
-  );
-
-  const onLoad = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
   const onPageHide = SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
     return new Promise(resolve => {
-      content.window.addEventListener("pagehide", () => {
+      content.addEventListener("pagehide", () => {
         resolve();
       });
     });
   });
   const onResult = SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
     return new Promise(resolve => {
-      content.window.addEventListener("keyup", () => {
+      content.addEventListener("keyup", () => {
         resolve("keyup");
       });
-      content.window.addEventListener("unload", () => {
+      content.addEventListener("unload", () => {
         resolve("unload");
       });
     });
   });
 
   info("Focus on the search bar");
   const searchBarTextBox = BrowserSearch.searchBar.textbox;
   EventUtils.synthesizeMouseAtCenter(searchBarTextBox, {});
@@ -71,13 +64,9 @@ add_task(async function() {
     () => ownerDocument.activeElement === gBrowser.selectedBrowser,
     "Wait for focus to be moved to the browser"
   );
   info("The focus is moved to the browser");
 
   // Check whether keyup event is not captured before unload event happens.
   const result = await onResult;
   is(result, "unload", "Keyup event is not captured");
-
-  // Cleanup.
-  await onLoad;
-  BrowserTestUtils.removeTab(tab);
 });
--- a/browser/themes/shared/preferences/containers.css
+++ b/browser/themes/shared/preferences/containers.css
@@ -14,32 +14,36 @@
 }
 
 [data-identity-icon] {
   margin: 0;
   margin-inline-end: 16px;
 }
 
 #containersView {
-  border: 0 none;
+  border: 0;
   background: transparent;
+  margin-block-end: 8px;
 }
 
 #containersView richlistitem {
   padding-block: 4px;
   border-block-end: 1px solid var(--in-content-border-color);
 }
 
+#containersView richlistitem > .container-buttons {
+  margin-inline-end: 4px;
+}
+
 /* Crop the label at the end using CSS. This isn't using XUL crop
  * and a value attribute because those labels cannot be highlighted
  * by the prefs' find-in-page feature.
  */
 .userContext-label-inprefs {
   display: -moz-box;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
 
 #containersView richlistitem:last-of-type {
-  border-block-end: 0 none;
-  margin-block-end: 8px;
+  border-block-end: 0;
 }
--- a/browser/themes/shared/preferences/preferences.inc.css
+++ b/browser/themes/shared/preferences/preferences.inc.css
@@ -400,18 +400,19 @@ checkbox {
 .actionsMenu > menupopup > menuitem {
   padding-inline-start: 10px !important;
 }
 
 .actionsMenu > menupopup > menuitem > .menu-iconic-left {
   margin-inline-end: 8px !important;
 }
 
-.actionsMenu:focus-visible {
-  outline-offset: -2px;
+/* Increase the specifity to override common.inc.css */
+richlistitem[selected] .actionsMenu:focus-visible {
+  outline-offset: -3px;
 }
 
 /* Home Pane */
 
 #homepageGroup menulist,
 #homepageGroup html|input {
   margin: 5px 0;
 }
new file mode 100644
--- /dev/null
+++ b/build/debian-packages/valgrind-buster.diff
@@ -0,0 +1,65 @@
+diff -Nru valgrind-3.16.1/debian/changelog valgrind-3.16.1/debian/changelog
+--- valgrind-3.16.1/debian/changelog	2020-06-29 02:49:18.000000000 +0900
++++ valgrind-3.16.1/debian/changelog	2021-02-25 08:11:31.000000000 +0900
+@@ -1,3 +1,12 @@
++valgrind (1:3.16.1-1.deb10moz1) buster; urgency=medium
++
++  * Mozilla backport for buster.
++  * debian/control, debian/compat: Drop debhelper compat back to 12.
++    (reverting https://salsa.debian.org/debian/valgrind/-/commit/11f49dcb8f3ded2878b22781f845e44e1b22f494)
++  * coregrind/m_syswrap/syswrap-linux.c: Apply patch from https://bugs.kde.org/show_bug.cgi?id=433641
++
++ -- Mike Hommey <glandium@mozilla.com>  Thu, 25 Feb 2021 08:11:31 +0900
++
+ valgrind (1:3.16.1-1) unstable; urgency=medium
+ 
+   * New upstream release
+diff -Nru valgrind-3.16.1/debian/compat valgrind-3.16.1/debian/compat
+--- valgrind-3.16.1/debian/compat	1970-01-01 09:00:00.000000000 +0900
++++ valgrind-3.16.1/debian/compat	2021-02-25 08:11:31.000000000 +0900
+@@ -0,0 +1 @@
++12
+diff -Nru valgrind-3.16.1/debian/control valgrind-3.16.1/debian/control
+--- valgrind-3.16.1/debian/control	2020-06-29 02:49:18.000000000 +0900
++++ valgrind-3.16.1/debian/control	2021-02-25 08:11:31.000000000 +0900
+@@ -2,7 +2,7 @@
+ Section: devel
+ Priority: optional
+ Maintainer: Alessandro Ghedini <ghedo@debian.org>
+-Build-Depends: debhelper-compat (= 13),
++Build-Depends: debhelper (>= 12),
+  gdb,
+  gcc-multilib [amd64],
+  libc6-dev-i386 [amd64],
+diff -Nru valgrind-3.16.1/debian/patches/15_fstatat_workaround.patch valgrind-3.16.1/debian/patches/15_fstatat_workaround.patch
+--- valgrind-3.16.1/debian/patches/15_fstatat_workaround.patch	1970-01-01 09:00:00.000000000 +0900
++++ valgrind-3.16.1/debian/patches/15_fstatat_workaround.patch	2021-02-25 08:11:31.000000000 +0900
+@@ -0,0 +1,20 @@
++Index: valgrind-3.16.1/coregrind/m_syswrap/syswrap-linux.c
++===================================================================
++--- valgrind-3.16.1.orig/coregrind/m_syswrap/syswrap-linux.c
+++++ valgrind-3.16.1/coregrind/m_syswrap/syswrap-linux.c
++@@ -5815,8 +5815,13 @@ PRE(sys_newfstatat)
++          SARG1, ARG2, (HChar*)(Addr)ARG2, ARG3);
++    PRE_REG_READ3(long, "fstatat",
++                  int, dfd, char *, file_name, struct stat *, buf);
++-   PRE_MEM_RASCIIZ( "fstatat(file_name)", ARG2 );
++-   PRE_MEM_WRITE( "fstatat(buf)", ARG3, sizeof(struct vki_stat) );
+++   // See the comment about Rust in PRE(sys_statx). When glibc does support statx
+++   // rust uses that instead of the system call, but glibc's statx is implemented
+++   // in terms of fstatat, so the filename being NULL is transferred here.
+++   if (ARG2 != 0) {
+++      PRE_MEM_RASCIIZ( "fstatat(file_name)", ARG2 );
+++      PRE_MEM_WRITE( "fstatat(buf)", ARG3, sizeof(struct vki_stat) );
+++   }
++ }
++ 
++ POST(sys_newfstatat)
+diff -Nru valgrind-3.16.1/debian/patches/series valgrind-3.16.1/debian/patches/series
+--- valgrind-3.16.1/debian/patches/series	2020-06-29 02:49:18.000000000 +0900
++++ valgrind-3.16.1/debian/patches/series	2021-02-25 08:11:31.000000000 +0900
+@@ -8,3 +8,4 @@
+ 12_drop-MPI-1-support.patch
+ 13_fix-path-to-vgdb.patch
+ 14_fix-debuginfo-section-duplicates-a-section-in-the-main-ELF-file.patch
++15_fstatat_workaround.patch
deleted file mode 100644
--- a/build/debian-packages/valgrind-jessie.diff
+++ /dev/null
@@ -1,61 +0,0 @@
-diff -Nru valgrind-3.16.1/debian/changelog valgrind-3.16.1/debian/changelog
---- valgrind-3.16.1/debian/changelog	2020-06-29 02:49:18.000000000 +0900
-+++ valgrind-3.16.1/debian/changelog	2020-12-09 09:17:53.000000000 +0900
-@@ -1,3 +1,16 @@
-+valgrind (1:3.16.1-1.deb8moz1) jessie; urgency=medium
-+
-+  * Mozilla backport for jessie.
-+  * debian/control, debian/compat: Drop debhelper compat back to 9, which
-+    requires adding back an explicit dependency on dh-autoreconf.
-+  * debian/rules:
-+    - Debhelper only defaulted to --parallel in compat >= 10, so add
-+      --parallel back.
-+    - Add an explicit --libexecdir to match that of debhelper compat level >=
-+      12.
-+
-+ -- Mike Hommey <glandium@mozilla.com>  Wed, 9 Dec 2020 09:17:53 +0900
-+
- valgrind (1:3.16.1-1) unstable; urgency=medium
- 
-   * New upstream release
-diff -Nru valgrind-3.16.1/debian/compat valgrind-3.16.1/debian/compat
---- valgrind-3.16.1/debian/compat	1970-01-01 09:00:00.000000000 +0900
-+++ valgrind-3.16.1/debian/compat	2020-12-09 09:15:49.000000000 +0900
-@@ -0,0 +1 @@
-+9
-diff -Nru valgrind-3.16.1/debian/control valgrind-3.16.1/debian/control
---- valgrind-3.16.1/debian/control	2020-06-29 02:49:18.000000000 +0900
-+++ valgrind-3.16.1/debian/control	2020-12-09 09:17:53.000000000 +0900
-@@ -2,7 +2,8 @@
- Section: devel
- Priority: optional
- Maintainer: Alessandro Ghedini <ghedo@debian.org>
--Build-Depends: debhelper-compat (= 13),
-+Build-Depends: debhelper (>= 9),
-+ dh-autoreconf,
-  gdb,
-  gcc-multilib [amd64],
-  libc6-dev-i386 [amd64],
-diff -Nru valgrind-3.16.1/debian/rules valgrind-3.16.1/debian/rules
---- valgrind-3.16.1/debian/rules	2020-06-29 02:49:18.000000000 +0900
-+++ valgrind-3.16.1/debian/rules	2020-12-09 09:17:53.000000000 +0900
-@@ -11,16 +11,16 @@
- LDFLAGS  = $(shell dpkg-buildflags --get LDFLAGS)
- 
- %:
--	dh $@ --with=autoreconf
-+	dh $@ --parallel --with=autoreconf
- 
- override_dh_auto_configure:
--	dh_auto_configure -- --enable-tls CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
-+	dh_auto_configure -- --libexecdir=/usr/libexec --enable-tls CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
- 
- override_dh_auto_test:
- 	: # do nothing for now
- 
- override_dh_auto_build:
--	dh_auto_build
-+	dh_auto_build --parallel
- 	$(MAKE) -C docs FAQ.txt
- 	$(MAKE) -C docs html-docs
- 	$(MAKE) -C docs man-pages
--- a/build/unix/mozconfig.stdcxx
+++ b/build/unix/mozconfig.stdcxx
@@ -1,17 +1,2 @@
 # Avoid dependency on libstdc++ 4.7
 export MOZ_STDCXX_COMPAT=1
-
-# Depending whether GCC was built on a RedHat-based or a Debian-based system,
-# the directory containing 32-bits libraries can be either (respectively)
-# lib or lib32. The directory for 64-bits libraries is always lib64.
-if [ -f "$MOZ_FETCHES_DIR/gcc/lib64/libstdc++.so.6" ]; then
-  # We put both 32-bits and 64-bits library path in LD_LIBRARY_PATH: ld.so
-  # will prefer the files in the 32-bits path when loading 32-bits executables,
-  # and the files in the 64-bits path when loading 64-bits executables.
-  # We also put both possible 32-bits library paths.
-  LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$MOZ_FETCHES_DIR/gcc/lib64:$MOZ_FETCHES_DIR/gcc/lib32:$MOZ_FETCHES_DIR/gcc/lib
-elif [ -f "$MOZ_FETCHES_DIR/clang/lib/libstdc++.so.6" ]; then
-  LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$MOZ_FETCHES_DIR/clang/lib:$MOZ_FETCHES_DIR/clang/lib32
-fi
-
-mk_add_options "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
--- a/build/valgrind/x86_64-pc-linux-gnu.sup
+++ b/build/valgrind/x86_64-pc-linux-gnu.sup
@@ -210,34 +210,72 @@
    fun:_g_io_module_get_default
 }
 {
    dlopen leak triggered by bug 1492121
    Memcheck:Leak
    match-leak-kinds: definite
    fun:malloc
    fun:dl_open_worker
-   fun:_dl_catch_error
+   fun:_dl_catch_exception
    fun:_dl_open
    fun:dlopen_doit
+   fun:_dl_catch_exception
    fun:_dl_catch_error
    fun:_dlerror_run
    fun:dlopen@@GLIBC_2.2.5
    ...
 }
 {
     gtk leak triggered by bug 1707957. Relatively minor, and seems fixed in newer GTK versions.
     Memcheck:Leak
     match-leak-kinds: definite
     ...
     fun:gtk_css_keyframes_new
     ...
     fun:settings_update_theme
     ...
 }
+{
+    gtk leak, likely related to https://gitlab.gnome.org/GNOME/gtk/-/issues/3113
+    Memcheck:Leak
+    match-leak-kinds: definite
+    fun:malloc
+    fun:g_malloc
+    fun:g_slice_alloc
+    fun:g_slice_alloc0
+    fun:g_type_create_instance
+    fun:g_object_new_internal
+    fun:g_object_new_valist
+    fun:g_object_new
+    fun:create_core_pointer
+    fun:gdk_x11_device_manager_core_constructed
+    fun:g_object_new_internal
+    fun:g_object_new_valist
+    fun:g_object_new
+    fun:_gdk_x11_device_manager_new
+    fun:_gdk_x11_display_open
+    fun:gdk_display_manager_open_display
+}
+{
+    gtk leak, likely related to https://gitlab.gnome.org/GNOME/gtk/-/issues/3113
+    Memcheck:Leak
+    match-leak-kinds: definite
+    fun:malloc
+    fun:g_malloc
+    fun:g_slice_alloc
+    fun:g_slice_alloc0
+    fun:g_type_create_instance
+    fun:g_object_new_internal
+    fun:g_object_new_with_properties
+    fun:g_object_new
+    fun:gdk_window_new
+    fun:_gdk_x11_display_open
+    fun:gdk_display_manager_open_display
+}
 
 ###################################
 #  Leaks in short lived processes #
 ###################################
 
 {
    Bug 984196
    Memcheck:Leak
--- a/devtools/client/inspector/fonts/fonts.js
+++ b/devtools/client/inspector/fonts/fonts.js
@@ -1010,17 +1010,20 @@ class FontInspector {
     // Augment each font object with a dataURI for an image with a sample of the font.
     for (const font of [...allFonts]) {
       font.previewUrl = await font.preview.data.string();
     }
 
     // Dispatch to the store if it hasn't been destroyed in the meantime.
     this.store && this.store.dispatch(updateFonts(allFonts));
     // Emit on the inspector if it hasn't been destroyed in the meantime.
-    this.inspector && this.inspector.emit("fontinspector-updated");
+    // Pass the current node in the payload so that tests can check the update
+    // corresponds to the expected node.
+    this.inspector &&
+      this.inspector.emitForTests("fontinspector-updated", this.node);
   }
 
   /**
    * Update the "font-variation-settings" CSS property with the state of all touched
    * font variation axes which shouldn't be written to other CSS font properties.
    */
   updateFontVariationSettings() {
     const fontEditor = this.store.getState().fontEditor;
--- a/devtools/client/inspector/fonts/test/browser_fontinspector_input-element-used-font.js
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_input-element-used-font.js
@@ -6,21 +6,17 @@ const TEST_URI = URL_ROOT + "doc_browser
 
 // Verify that a styled input field element is showing proper font information
 // in its font tab.
 // Non-regression test for https://bugzilla.mozilla.org/show_bug.cgi?id=1435469
 add_task(async function() {
   const { inspector, view } = await openFontInspectorForURL(TEST_URI);
   const viewDoc = view.document;
 
-  const onInspectorUpdated = inspector.once("fontinspector-updated");
   await selectNode(".input-field", inspector);
 
-  info("Waiting for font editor to render");
-  await onInspectorUpdated;
-
   const fontEls = getUsedFontsEls(viewDoc);
   ok(fontEls.length == 1, `Used fonts found for styled input element`);
   ok(
     fontEls[0].textContent == "Ostrich Sans Medium",
     `Proper font found: 'Ostrich Sans Medium' for styled input.`
   );
 });
--- a/devtools/client/inspector/fonts/test/head.js
+++ b/devtools/client/inspector/fonts/test/head.js
@@ -12,30 +12,55 @@ Services.scriptloader.loadSubScript(
   this
 );
 
 Services.prefs.setCharPref("devtools.inspector.activeSidebar", "fontinspector");
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
 });
 
+var nodeConstants = require("devtools/shared/dom-node-constants");
+
 /**
  * The font-inspector doesn't participate in the inspector's update mechanism
  * (i.e. it doesn't call inspector.updating() when updating), so simply calling
  * the default selectNode isn't enough to guaranty that the panel has finished
  * updating. We also need to wait for the fontinspector-updated event.
+ *
+ * @param {String|NodeFront} selector
+ * @param {InspectorPanel} inspector
+ *        The instance of InspectorPanel currently loaded in the toolbox.
+ * @param {String} reason
+ *        Defaults to "test" which instructs the inspector not to highlight the
+ *        node upon selection.
  */
 var _selectNode = selectNode;
 selectNode = async function(node, inspector, reason) {
-  const onInspectorUpdated = inspector.once("fontinspector-updated");
+  // Ensure node is a NodeFront and not a selector (which is also accepted as
+  // an argument to selectNode).
+  node = await getNodeFront(node, inspector);
+
+  // The FontInspector will fallback to the parent node when a text node is
+  // selected.
+  const isTextNode = node.nodeType == nodeConstants.TEXT_NODE;
+  const expectedNode = isTextNode ? node.parentNode() : node;
+
   const onEditorUpdated = inspector.once("fonteditor-updated");
+  const onFontInspectorUpdated = new Promise(resolve => {
+    inspector.on("fontinspector-updated", function onUpdated(eventNode) {
+      if (eventNode === expectedNode) {
+        inspector.off("fontinspector-updated", onUpdated);
+        resolve();
+      }
+    });
+  });
   await _selectNode(node, inspector, reason);
 
   // Wait for both the font inspector and font editor before proceeding.
-  await Promise.all([onInspectorUpdated, onEditorUpdated]);
+  await Promise.all([onFontInspectorUpdated, onEditorUpdated]);
 };
 
 /**
  * Adds a new tab with the given URL, opens the inspector and selects the
  * font-inspector tab.
  * @return {Promise} resolves to a {tab, toolbox, inspector, view} object
  */
 var openFontInspectorForURL = async function(url) {
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -1554,17 +1554,17 @@ Inspector.prototype = {
     }
   },
 
   /**
    * Stops listening for reflows.
    */
   untrackReflowsInSelection() {
     this.commands.resourceCommand.unwatchResources(
-      [this.commands.resourceCommand.REFLOW],
+      [this.commands.resourceCommand.TYPES.REFLOW],
       {
         onAvailable: this.onReflowInSelection,
       }
     );
   },
 
   onReflowInSelection() {
     // This event will be fired whenever a reflow is detected in the target front of the
--- a/devtools/server/actors/descriptors/process.js
+++ b/devtools/server/actors/descriptors/process.js
@@ -158,20 +158,36 @@ const ProcessDescriptorActor = ActorClas
   form() {
     return {
       actor: this.actorID,
       id: this.id,
       isParent: this.isParent,
       traits: {
         // Supports the Watcher actor. Can be removed as part of Bug 1680280.
         watcher: true,
+        // ParentProcessTargetActor can be reloaded.
+        supportsReloadBrowsingContext: this.isParent,
       },
     };
   },
 
+  async reloadBrowsingContext({ bypassCache }) {
+    if (!this.isParent) {
+      throw new Error(
+        "reloadBrowsingContext is not available for content process descriptors"
+      );
+    }
+
+    this._browsingContextTargetActor.browsingContext.reload(
+      bypassCache
+        ? Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE
+        : Ci.nsIWebNavigation.LOAD_FLAGS_NONE
+    );
+  },
+
   destroy() {
     this.emit("descriptor-destroyed");
 
     this._browsingContextTargetActor = null;
     Actor.prototype.destroy.call(this);
   },
 });
 
--- a/devtools/server/actors/descriptors/tab.js
+++ b/devtools/server/actors/descriptors/tab.js
@@ -7,16 +7,17 @@
 /*
  * Descriptor Actor that represents a Tab in the parent process. It
  * launches a FrameTargetActor in the content process to do the real work and tunnels the
  * data.
  *
  * See devtools/docs/backend/actor-hierarchy.md for more details.
  */
 
+const { Ci } = require("chrome");
 const Services = require("Services");
 const {
   connectToFrame,
 } = require("devtools/server/connectors/frame-connector");
 loader.lazyImporter(
   this,
   "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm"
@@ -57,16 +58,17 @@ const TabDescriptorActor = ActorClassWit
           : null,
       isZombieTab: this._isZombieTab(),
       outerWindowID: this._getOuterWindowId(),
       selected: this.selected,
       title: this._getTitle(),
       traits: {
         // Supports the Watcher actor. Can be removed as part of Bug 1680280.
         watcher: true,
+        supportsReloadBrowsingContext: true,
       },
       url: this._getUrl(),
     };
 
     return form;
   },
 
   _getTitle() {
@@ -209,16 +211,28 @@ const TabDescriptorActor = ActorClassWit
 
   _isZombieTab() {
     // Note: GeckoView doesn't support zombie tabs
     const tabbrowser = this._tabbrowser;
     const tab = tabbrowser ? tabbrowser.getTabForBrowser(this._browser) : null;
     return tab?.hasAttribute && tab.hasAttribute("pending");
   },
 
+  reloadBrowsingContext({ bypassCache }) {
+    if (!this._browser || !this._browser.browsingContext) {
+      return;
+    }
+
+    this._browser.browsingContext.reload(
+      bypassCache
+        ? Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE
+        : Ci.nsIWebNavigation.LOAD_FLAGS_NONE
+    );
+  },
+
   destroy() {
     this.emit("descriptor-destroyed");
     this._browser = null;
 
     Actor.prototype.destroy.call(this);
   },
 });
 
--- a/devtools/server/actors/descriptors/webextension.js
+++ b/devtools/server/actors/descriptors/webextension.js
@@ -7,16 +7,17 @@
 /*
  * Represents a WebExtension add-on in the parent process. This gives some metadata about
  * the add-on and watches for uninstall events. This uses a proxy to access the
  * WebExtension in the WebExtension process via the message manager.
  *
  * See devtools/docs/backend/actor-hierarchy.md for more details.
  */
 
+const { Ci } = require("chrome");
 const protocol = require("devtools/shared/protocol");
 const {
   webExtensionDescriptorSpec,
 } = require("devtools/shared/specs/descriptors/webextension");
 const {
   connectToFrame,
 } = require("devtools/server/connectors/frame-connector");
 
@@ -77,17 +78,19 @@ const WebExtensionDescriptorActor = prot
         iconDataURL: this._iconDataURL,
         iconURL: this.addon.iconURL,
         id: this.addonId,
         isSystem: this.addon.isSystem,
         isWebExtension: this.addon.isWebExtension,
         manifestURL: policy && policy.getURL("manifest.json"),
         name: this.addon.name,
         temporarilyInstalled: this.addon.temporarilyInstalled,
-        traits: {},
+        traits: {
+          supportsReloadBrowsingContext: true,
+        },
         url: this.addon.sourceURI ? this.addon.sourceURI.spec : undefined,
         warnings: ExtensionParent.DebugUtils.getExtensionManifestWarnings(
           this.addonId
         ),
       };
     },
 
     async getTarget() {
@@ -136,16 +139,28 @@ const WebExtensionDescriptorActor = prot
       this._childActorID = this._form.actor;
 
       // Exit the proxy child actor if the child actor has been destroyed.
       this._mm.addMessageListener("debug:webext_child_exit", this._onChildExit);
 
       return this._form;
     },
 
+    async reloadBrowsingContext({ bypassCache }) {
+      if (!this._browser || !this._browser.browsingContext) {
+        return;
+      }
+
+      this._browser.browsingContext.reload(
+        bypassCache
+          ? Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE
+          : Ci.nsIWebNavigation.LOAD_FLAGS_NONE
+      );
+    },
+
     /** WebExtension Actor Methods **/
     async reload() {
       await this.addon.reload();
       return {};
     },
 
     // This function will be called from RootActor in case that the devtools client
     // retrieves list of addons with `iconDataURL` option.
--- a/devtools/server/actors/targets/browsing-context.js
+++ b/devtools/server/actors/targets/browsing-context.js
@@ -1150,16 +1150,20 @@ const browsingContextTargetPrototype = {
       }, "BrowsingContextTargetActor.prototype.goBack's delayed body")
     );
 
     return {};
   },
 
   /**
    * Reload the page in this browsing context.
+   *
+   * @backward-compat { legacy }
+   *                  reload is preserved for third party tools. See Bug 1717837.
+   *                  DevTools should use Descriptor::reloadBrowsingContext instead.
    */
   reload(request) {
     const force = request?.options?.force;
     // Wait a tick so that the response packet can be dispatched before the
     // subsequent navigation event packet.
     Services.tm.dispatchToMainThread(
       DevToolsUtils.makeInfallible(() => {
         // This won't work while the browser is shutting down and we don't really
@@ -1286,17 +1290,17 @@ const browsingContextTargetPrototype = {
     ) {
       this._setPaintFlashingEnabled(options.paintFlashing);
     }
     if (typeof options.restoreFocus == "boolean") {
       this._restoreFocus = options.restoreFocus;
     }
 
     if (reload) {
-      this.reload();
+      this.webNavigation.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
     }
   },
 
   get touchSimulator() {
     if (!this._touchSimulator) {
       this._touchSimulator = new TouchSimulator(this.chromeEventHandler);
     }
 
--- a/devtools/shared/commands/resource/resource-command.js
+++ b/devtools/shared/commands/resource/resource-command.js
@@ -123,16 +123,24 @@ class ResourceCommand {
     } = options;
 
     if (typeof onAvailable !== "function") {
       throw new Error(
         "ResourceCommand.watchResources expects an onAvailable function as argument"
       );
     }
 
+    for (const type of resources) {
+      if (!this._isValidResourceType(type)) {
+        throw new Error(
+          `ResourceCommand.watchResources invoked with an unknown type: "${type}"`
+        );
+      }
+    }
+
     // Pending watchers are used in unwatchResources to remove watchers which
     // are not fully registered yet. Store `onAvailable` which is the unique key
     // for a watcher, as well as the resources array, so that unwatchResources
     // can update the array if we stop watching a specific resource.
     const pendingWatcher = {
       resources,
       onAvailable,
     };
@@ -214,16 +222,24 @@ class ResourceCommand {
     const { onAvailable } = options;
 
     if (typeof onAvailable !== "function") {
       throw new Error(
         "ResourceCommand.unwatchResources expects an onAvailable function as argument"
       );
     }
 
+    for (const type of resources) {
+      if (!this._isValidResourceType(type)) {
+        throw new Error(
+          `ResourceCommand.unwatchResources invoked with an unknown type: "${type}"`
+        );
+      }
+    }
+
     // Unregister the callbacks from the watchers registries.
     // Check _watchers for the fully initialized watchers, as well as
     // `_pendingWatchers` for new watchers still being created by `watchResources`
     const allWatchers = [...this._watchers, ...this._pendingWatchers];
     for (const watcherEntry of allWatchers) {
       // onAvailable is the only mandatory argument which ends up being used to match
       // the right watcher entry.
       if (watcherEntry.onAvailable == onAvailable) {
@@ -832,16 +848,20 @@ class ResourceCommand {
     // If it doesn't, no resource type can be listened via the Watcher actor for this target.
     if (!this.targetCommand.hasTargetWatcherSupport(targetFront.targetType)) {
       return false;
     }
 
     return this.hasResourceCommandSupport(resourceType);
   }
 
+  _isValidResourceType(type) {
+    return this.ALL_TYPES.includes(type);
+  }
+
   /**
    * Start listening for a given type of resource.
    * For backward compatibility code, we register the legacy listeners on
    * each individual target
    *
    * @param {String} resourceType
    *        One string of ResourceCommand.TYPES, which designates the types of resources
    *        to be listened.
@@ -1074,16 +1094,19 @@ ResourceCommand.TYPES = ResourceCommand.
   EXTENSION_STORAGE: "extension-storage",
   INDEXED_DB: "indexed-db",
   NETWORK_EVENT_STACKTRACE: "network-event-stacktrace",
   REFLOW: "reflow",
   SOURCE: "source",
   THREAD_STATE: "thread-state",
   SERVER_SENT_EVENT: "server-sent-event",
 };
+ResourceCommand.ALL_TYPES = ResourceCommand.prototype.ALL_TYPES = Object.values(
+  ResourceCommand.TYPES
+);
 module.exports = ResourceCommand;
 
 // Backward compat code for each type of resource.
 // Each section added here should eventually be removed once the equivalent server
 // code is implement in Firefox, in its release channel.
 const LegacyListeners = {
   [ResourceCommand.TYPES
     .CONSOLE_MESSAGE]: require("devtools/shared/commands/resource/legacy-listeners/console-messages"),
--- a/devtools/shared/commands/resource/tests/browser.ini
+++ b/devtools/shared/commands/resource/tests/browser.ini
@@ -35,16 +35,17 @@ support-files =
 [browser_resources_console_messages.js]
 [browser_resources_console_messages_navigation.js]
 [browser_resources_console_messages_workers.js]
 [browser_resources_css_changes.js]
 [browser_resources_css_messages.js]
 [browser_resources_document_events.js]
 [browser_resources_error_messages.js]
 [browser_resources_getAllResources.js]
+[browser_resources_invalid_api_usage.js]
 [browser_resources_network_event_stacktraces.js]
 [browser_resources_network_events.js]
 [browser_resources_platform_messages.js]
 [browser_resources_reflows.js]
 [browser_resources_root_node.js]
 [browser_resources_server_sent_events.js]
 [browser_resources_several_resources.js]
 [browser_resources_sources.js]
new file mode 100644
--- /dev/null
+++ b/devtools/shared/commands/resource/tests/browser_resources_invalid_api_usage.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test watch/unwatchResources throw when provided with invalid types.
+
+const TEST_URI = "data:text/html;charset=utf-8,invalid api usage test";
+
+add_task(async function() {
+  const tab = await addTab(TEST_URI);
+
+  const { client, resourceCommand, targetCommand } = await initResourceCommand(
+    tab
+  );
+
+  const onAvailable = function() {};
+
+  await Assert.rejects(
+    resourceCommand.watchResources([null], { onAvailable }),
+    /ResourceCommand\.watchResources invoked with an unknown type/,
+    "watchResources should throw for null type"
+  );
+
+  await Assert.rejects(
+    resourceCommand.watchResources([undefined], { onAvailable }),
+    /ResourceCommand\.watchResources invoked with an unknown type/,
+    "watchResources should throw for undefined type"
+  );
+
+  await Assert.rejects(
+    resourceCommand.watchResources(["NOT_A_RESOURCE"], { onAvailable }),
+    /ResourceCommand\.watchResources invoked with an unknown type/,
+    "watchResources should throw for unknown type"
+  );
+
+  await Assert.rejects(
+    resourceCommand.watchResources(
+      [resourceCommand.TYPES.CONSOLE_MESSAGE, "NOT_A_RESOURCE"],
+      { onAvailable }
+    ),
+    /ResourceCommand\.watchResources invoked with an unknown type/,
+    "watchResources should throw for unknown type mixed with a correct type"
+  );
+
+  await Assert.throws(
+    () => resourceCommand.unwatchResources([null], { onAvailable }),
+    /ResourceCommand\.unwatchResources invoked with an unknown type/,
+    "unwatchResources should throw for null type"
+  );
+
+  await Assert.throws(
+    () => resourceCommand.unwatchResources([undefined], { onAvailable }),
+    /ResourceCommand\.unwatchResources invoked with an unknown type/,
+    "unwatchResources should throw for undefined type"
+  );
+
+  await Assert.throws(
+    () => resourceCommand.unwatchResources(["NOT_A_RESOURCE"], { onAvailable }),
+    /ResourceCommand\.unwatchResources invoked with an unknown type/,
+    "unwatchResources should throw for unknown type"
+  );
+
+  await Assert.throws(
+    () =>
+      resourceCommand.unwatchResources(
+        [resourceCommand.TYPES.CONSOLE_MESSAGE, "NOT_A_RESOURCE"],
+        { onAvailable }
+      ),
+    /ResourceCommand\.unwatchResources invoked with an unknown type/,
+    "unwatchResources should throw for unknown type mixed with a correct type"
+  );
+
+  targetCommand.destroy();
+  await client.close();
+});
--- a/devtools/shared/commands/target/target-command.js
+++ b/devtools/shared/commands/target/target-command.js
@@ -572,16 +572,24 @@ class TargetCommand extends EventEmitter
    */
   async watchTargets(types, onAvailable, onDestroy) {
     if (typeof onAvailable != "function") {
       throw new Error(
         "TargetCommand.watchTargets expects a function as second argument"
       );
     }
 
+    for (const type of types) {
+      if (!this._isValidTargetType(type)) {
+        throw new Error(
+          `TargetCommand.watchTargets invoked with an unknown type: "${type}"`
+        );
+      }
+    }
+
     // Notify about already existing target of these types
     const targetFronts = [...this._targets].filter(targetFront =>
       types.includes(targetFront.targetType)
     );
     this._pendingWatchTargetInitialization.set(
       onAvailable,
       new Set(targetFronts)
     );
@@ -645,16 +653,22 @@ class TargetCommand extends EventEmitter
   unwatchTargets(types, onAvailable, onDestroy) {
     if (typeof onAvailable != "function") {
       throw new Error(
         "TargetCommand.unwatchTargets expects a function as second argument"
       );
     }
 
     for (const type of types) {
+      if (!this._isValidTargetType(type)) {
+        throw new Error(
+          `TargetCommand.unwatchTargets invoked with an unknown type: "${type}"`
+        );
+      }
+
       this._createListeners.off(type, onAvailable);
       if (onDestroy) {
         this._destroyListeners.off(type, onDestroy);
       }
     }
     this._pendingWatchTargetInitialization.delete(onAvailable);
   }
 
@@ -739,19 +753,24 @@ class TargetCommand extends EventEmitter
   /**
    * Reload the current top level target.
    * This only works for targets inheriting from BrowsingContextTarget.
    *
    * @param {Boolean} bypassCache
    *        If true, the reload will be forced to bypass any cache.
    */
   async reloadTopLevelTarget(bypassCache = false) {
+    // @backward-compat { version 91 }
+    //                  BrowsingContextTargetActor.reload was moved to descriptors.
+    //                  After release 91 is on the release channel, we can check
+    //                  this.descriptorFront.traits.supportsReloadBrowsingContext
+    //                  instead.
     if (!this.targetFront.isBrowsingContext) {
       throw new Error(
-        "The top level target isn't a BrowsingContext and don't support being reloaded"
+        "The top level target isn't a BrowsingContext and doesn't support being reloaded"
       );
     }
 
     // Wait for the next DOCUMENT_EVENT's dom-complete event
     // Wait for waitForNextResource completion before reloading, otherwise we might miss the dom-complete event.
     // This can happen if `ResourceCommand.watchResources` made by `waitForNextResource` is still pending
     // while the reload already started and finished loading the document early.
     const {
@@ -761,31 +780,41 @@ class TargetCommand extends EventEmitter
       {
         ignoreExistingResources: true,
         predicate(resource) {
           return resource.name == "dom-complete";
         },
       }
     );
 
+    // @backward-compat { version 91 }
+    //                  BrowsingContextTargetActor.reload was moved to descriptors.
+    if (this.descriptorFront.traits.supportsReloadBrowsingContext) {
+      await this.descriptorFront.reloadBrowsingContext({ bypassCache });
+    } else {
+      await this._legacyTargetActorReload(bypassCache);
+    }
+
+    await onReloaded;
+  }
+
+  async _legacyTargetActorReload(force) {
     const { targetFront } = this;
     try {
       // Arguments of reload are a bit convoluted.
       // We expect an dictionary object, which only support one attribute
       // called "force" which force bypassing the caches.
-      await targetFront.reload({ options: { force: bypassCache } });
+      await targetFront.reload({ options: { force } });
     } catch (e) {
       // If the target follows the window global lifecycle, the reload request
       // will fail, and we should swallow the error. Re-throw it otherwise.
       if (!targetFront.targetForm.followWindowGlobalLifeCycle) {
         throw e;
       }
     }
-
-    await onReloaded;
   }
 
   /**
    * Called when the top level target is replaced by a new one.
    * Typically when we navigate to another domain which requires to be loaded in a distinct process.
    *
    * @param {TargetFront} newTarget
    *        The new top level target to debug.
@@ -806,16 +835,20 @@ class TargetCommand extends EventEmitter
 
   isServerTargetSwitchingEnabled() {
     if (this.descriptorFront.isServerTargetSwitchingEnabled) {
       return this.descriptorFront.isServerTargetSwitchingEnabled();
     }
     return false;
   }
 
+  _isValidTargetType(type) {
+    return this.ALL_TYPES.includes(type);
+  }
+
   destroy() {
     this.stopListening();
     this._createListeners.off();
     this._destroyListeners.off();
 
     this._isDestroyed = true;
   }
 }
--- a/devtools/shared/commands/target/tests/browser.ini
+++ b/devtools/shared/commands/target/tests/browser.ini
@@ -10,21 +10,22 @@ support-files =
   incremental-js-value-script.sjs
   fission_document.html
   fission_iframe.html
   test_service_worker.js
   test_sw_page.html
   test_sw_page_worker.js
   test_worker.js
 
+[browser_target_command_reload.js]
+[browser_target_invalid_api_usage.js]
 [browser_target_list_bfcache.js]
 [browser_target_list_browser_workers.js]
 [browser_target_list_frames.js]
 [browser_target_list_getAllTargets.js]
-[browser_target_command_reload.js]
 [browser_target_list_preffedoff.js]
 [browser_target_list_processes.js]
 [browser_target_list_service_workers.js]
 [browser_target_list_service_workers_navigation.js]
 skip-if = fission
 # There are several issues to test Targets navigation scenarios with fission.
 # Without a toolbox linked to the target-list, the target list cannot switch
 # targets. The legacy worker watchers are also not designed to support target
--- a/devtools/shared/commands/target/tests/browser_target_command_reload.js
+++ b/devtools/shared/commands/target/tests/browser_target_command_reload.js
@@ -76,17 +76,17 @@ add_task(async function() {
   await commands.targetCommand.startListening();
 
   try {
     await commands.targetCommand.reloadTopLevelTarget();
     ok(false, "reloadToLevelTarget() should have thrown for the main process");
   } catch (e) {
     is(
       e.message,
-      "The top level target isn't a BrowsingContext and don't support being reloaded"
+      "The top level target isn't a BrowsingContext and doesn't support being reloaded"
     );
   }
   await commands.destroy();
 });
 
 function getContentVariable() {
   return SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
     return content.wrappedJSObject.jsValue;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/commands/target/tests/browser_target_invalid_api_usage.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test watch/unwatchTargets throw when provided with invalid types.
+
+const TEST_URL = "data:text/html;charset=utf-8,invalid api usage test";
+
+add_task(async function() {
+  info("Setup the test page with workers of all types");
+  const tab = await addTab(TEST_URL);
+
+  info("Create a target list for a tab target");
+  const commands = await CommandsFactory.forTab(tab);
+  const targetCommand = commands.targetCommand;
+
+  const onAvailable = function() {};
+
+  await Assert.rejects(
+    targetCommand.watchTargets([null], onAvailable),
+    /TargetCommand.watchTargets invoked with an unknown type/,
+    "watchTargets should throw for null type"
+  );
+
+  await Assert.rejects(
+    targetCommand.watchTargets([undefined], onAvailable),
+    /TargetCommand.watchTargets invoked with an unknown type/,
+    "watchTargets should throw for undefined type"
+  );
+
+  await Assert.rejects(
+    targetCommand.watchTargets(["NOT_A_TARGET"], onAvailable),
+    /TargetCommand.watchTargets invoked with an unknown type/,
+    "watchTargets should throw for unknown type"
+  );
+
+  await Assert.rejects(
+    targetCommand.watchTargets(
+      [targetCommand.TYPES.FRAME, "NOT_A_TARGET"],
+      onAvailable
+    ),
+    /TargetCommand.watchTargets invoked with an unknown type/,
+    "watchTargets should throw for unknown type mixed with a correct type"
+  );
+
+  Assert.throws(
+    () => targetCommand.unwatchTargets([null], onAvailable),
+    /TargetCommand.unwatchTargets invoked with an unknown type/,
+    "unwatchTargets should throw for null type"
+  );
+
+  Assert.throws(
+    () => targetCommand.unwatchTargets([undefined], onAvailable),
+    /TargetCommand.unwatchTargets invoked with an unknown type/,
+    "unwatchTargets should throw for undefined type"
+  );
+
+  Assert.throws(
+    () => targetCommand.unwatchTargets(["NOT_A_TARGET"], onAvailable),
+    /TargetCommand.unwatchTargets invoked with an unknown type/,
+    "unwatchTargets should throw for unknown type"
+  );
+
+  Assert.throws(
+    () =>
+      targetCommand.unwatchTargets(
+        [targetCommand.TYPES.CONSOLE_MESSAGE, "NOT_A_TARGET"],
+        onAvailable
+      ),
+    /TargetCommand.unwatchTargets invoked with an unknown type/,
+    "unwatchTargets should throw for unknown type mixed with a correct type"
+  );
+
+  BrowserTestUtils.removeTab(tab);
+  await commands.destroy();
+});
--- a/devtools/shared/specs/descriptors/process.js
+++ b/devtools/shared/specs/descriptors/process.js
@@ -1,29 +1,39 @@
 /* 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 { generateActorSpec, RetVal } = require("devtools/shared/protocol");
+const {
+  generateActorSpec,
+  RetVal,
+  Option,
+} = require("devtools/shared/protocol");
 
 const processDescriptorSpec = generateActorSpec({
   typeName: "processDescriptor",
 
   methods: {
     getTarget: {
       request: {},
       response: {
         process: RetVal("json"),
       },
     },
     getWatcher: {
       request: {},
       response: RetVal("watcher"),
     },
+    reloadBrowsingContext: {
+      request: {
+        bypassCache: Option(0, "boolean"),
+      },
+      response: {},
+    },
   },
 
   events: {
     "descriptor-destroyed": {
       type: "descriptor-destroyed",
     },
   },
 });
--- a/devtools/shared/specs/descriptors/tab.js
+++ b/devtools/shared/specs/descriptors/tab.js
@@ -1,14 +1,18 @@
 /* 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 { generateActorSpec, RetVal } = require("devtools/shared/protocol");
+const {
+  generateActorSpec,
+  Option,
+  RetVal,
+} = require("devtools/shared/protocol");
 
 const tabDescriptorSpec = generateActorSpec({
   typeName: "tabDescriptor",
 
   methods: {
     getTarget: {
       request: {},
       response: {
@@ -20,16 +24,22 @@ const tabDescriptorSpec = generateActorS
       response: {
         favicon: RetVal("string"),
       },
     },
     getWatcher: {
       request: {},
       response: RetVal("watcher"),
     },
+    reloadBrowsingContext: {
+      request: {
+        bypassCache: Option(0, "boolean"),
+      },
+      response: {},
+    },
   },
 
   events: {
     "descriptor-destroyed": {
       type: "descriptor-destroyed",
     },
   },
 });
--- a/devtools/shared/specs/descriptors/webextension.js
+++ b/devtools/shared/specs/descriptors/webextension.js
@@ -1,14 +1,18 @@
 /* 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 { RetVal, generateActorSpec } = require("devtools/shared/protocol");
+const {
+  RetVal,
+  generateActorSpec,
+  Option,
+} = require("devtools/shared/protocol");
 
 const webExtensionDescriptorSpec = generateActorSpec({
   typeName: "webExtensionDescriptor",
 
   methods: {
     reload: {
       request: {},
       response: { addon: RetVal("json") },
@@ -19,16 +23,23 @@ const webExtensionDescriptorSpec = gener
       request: {},
       response: { form: RetVal("json") },
     },
 
     getTarget: {
       request: {},
       response: { form: RetVal("json") },
     },
+
+    reloadBrowsingContext: {
+      request: {
+        bypassCache: Option(0, "boolean"),
+      },
+      response: {},
+    },
   },
 
   events: {
     "descriptor-destroyed": {
       type: "descriptor-destroyed",
     },
   },
 });
--- a/devtools/shared/specs/targets/browsing-context.js
+++ b/devtools/shared/specs/targets/browsing-context.js
@@ -32,16 +32,22 @@ types.addDictType("browsingContextTarget
   title: "nullable:string", // should be present if not destroying
   destroy: "nullable:boolean", // not present if not destroying
 });
 
 types.addDictType("browsingContextTarget.workers", {
   workers: "array:workerDescriptor",
 });
 
+// @backward-compat { version 91 }
+//                  BrowsingContextTarget reload should no longer be used within
+//                  DevTools. Remove this comment when version 91 hits release.
+// @backward-compat { legacy }
+//                  reload is preserved for third party tools. See Bug 1717837.
+//                  DevTools should use Descriptor::reloadBrowsingContext instead.
 types.addDictType("browsingContextTarget.reload", {
   force: "boolean",
 });
 
 // @backward-compat { version 87 } See backward-compat note for `reconfigure`.
 types.addDictType("browsingContextTarget.reconfigure", {
   cacheDisabled: "nullable:boolean",
   colorSchemeSimulation: "nullable:string",
@@ -74,16 +80,23 @@ const browsingContextTargetSpecPrototype
     goForward: {
       request: {},
       response: {},
     },
     goBack: {
       request: {},
       response: {},
     },
+    // @backward-compat { version 91 }
+    //                  BrowsingContextTarget reload should no longer be used within
+    //                  DevTools. Remove this comment when version 91 hits release.
+    // @backward-compat { legacy }
+    //                  reload is preserved for third party tools. See Bug 1717837.
+    //                  DevTools should use Descriptor::reloadBrowsingContext instead.
+
     reload: {
       request: {
         options: Option(0, "browsingContextTarget.reload"),
       },
       response: {},
     },
     navigateTo: {
       request: {
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -702,16 +702,29 @@ void ChromeUtils::CreateOriginAttributes
   if (!attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(aOrigin), suffix)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
   aAttrs = attrs;
 }
 
 /* static */
+void ChromeUtils::CreateOriginAttributesFromOriginSuffix(
+    dom::GlobalObject& aGlobal, const nsAString& aSuffix,
+    dom::OriginAttributesDictionary& aAttrs, ErrorResult& aRv) {
+  OriginAttributes attrs;
+  nsAutoCString suffix;
+  if (!attrs.PopulateFromSuffix(NS_ConvertUTF16toUTF8(aSuffix))) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+  aAttrs = attrs;
+}
+
+/* static */
 void ChromeUtils::FillNonDefaultOriginAttributes(
     dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
     dom::OriginAttributesDictionary& aNewAttrs) {
   aNewAttrs = aAttrs;
 }
 
 /* static */
 bool ChromeUtils::IsOriginAttributesEqual(
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -94,16 +94,20 @@ class ChromeUtils {
   static bool OriginAttributesMatchPattern(
       dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
       const dom::OriginAttributesPatternDictionary& aPattern);
 
   static void CreateOriginAttributesFromOrigin(
       dom::GlobalObject& aGlobal, const nsAString& aOrigin,
       dom::OriginAttributesDictionary& aAttrs, ErrorResult& aRv);
 
+  static void CreateOriginAttributesFromOriginSuffix(
+      dom::GlobalObject& aGlobal, const nsAString& aSuffix,
+      dom::OriginAttributesDictionary& aAttrs, ErrorResult& aRv);
+
   static void FillNonDefaultOriginAttributes(
       dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aAttrs,
       dom::OriginAttributesDictionary& aNewAttrs);
 
   static bool IsOriginAttributesEqual(
       dom::GlobalObject& aGlobal, const dom::OriginAttributesDictionary& aA,
       const dom::OriginAttributesDictionary& aB);
 
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -266,16 +266,29 @@ partial namespace ChromeUtils {
    *                          the origin suffix and unspecified attributes
    *                          added and assigned default values.
    */
   [Throws]
   OriginAttributesDictionary
   createOriginAttributesFromOrigin(DOMString origin);
 
   /**
+   * Returns an OriginAttributesDictionary with values from the origin |suffix|
+   * and unspecified attributes added and assigned default values.
+   *
+   * @param suffix            The origin suffix to create from.
+   * @returns                 An OriginAttributesDictionary with values from
+   *                          the origin suffix and unspecified attributes
+   *                          added and assigned default values.
+   */
+  [Throws]
+  OriginAttributesDictionary
+  CreateOriginAttributesFromOriginSuffix(DOMString suffix);
+
+  /**
    * Returns an OriginAttributesDictionary that is a copy of |originAttrs| with
    * unspecified attributes added and assigned default values.
    *
    * @param originAttrs       The origin attributes to copy.
    * @returns                 An OriginAttributesDictionary copy of |originAttrs|
    *                          with unspecified attributes added and assigned
    *                          default values.
    */
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -1001,17 +1001,16 @@ nsresult EventDispatcher::Dispatch(nsISu
 
         clearTargets = ShouldClearTargets(aEvent);
 
         // Handle the chain.
         EventChainPostVisitor postVisitor(preVisitor);
         MOZ_RELEASE_ASSERT(!aEvent->mPath);
         aEvent->mPath = &chain;
 
-#ifdef MOZ_GECKO_PROFILER
         if (profiler_is_active()) {
           // Add a profiler label and a profiler marker for the actual
           // dispatch of the event.
           // This is a very hot code path, so we need to make sure not to
           // do this extra work when we're not profiling.
           if (!postVisitor.mDOMEvent) {
             // This is tiny bit slow, but happens only once per event.
             // Similar code also in EventListenerManager.
@@ -1074,19 +1073,17 @@ nsresult EventDispatcher::Dispatch(nsISu
 
           EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
                                                        aCallback, cd);
 
           profiler_add_marker(
               "DOMEvent", geckoprofiler::category::DOM,
               {MarkerTiming::IntervalEnd(), std::move(innerWindowId)},
               DOMEventMarker{}, typeStr, startTime, aEvent->mTimeStamp);
-        } else
-#endif
-        {
+        } else {
           EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
                                                        aCallback, cd);
         }
         aEvent->mPath = nullptr;
 
         if (aPresContext && aPresContext->GetRootPresContext() &&
             aEvent->IsTrusted()) {
           nsRefreshDriver* driver =
--- a/dom/indexedDB/ActorsParentCommon.cpp
+++ b/dom/indexedDB/ActorsParentCommon.cpp
@@ -36,16 +36,17 @@
 #include "mozilla/ProfilerLabels.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/ResultExtensions.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TelemetryScalarEnums.h"
 #include "mozilla/dom/quota/DecryptingInputStream_impl.h"
 #include "mozilla/dom/quota/QuotaCommon.h"
+#include "mozilla/dom/quota/ScopedLogExtraInfo.h"
 #include "mozilla/fallible.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/mozalloc.h"
 #include "nsCOMPtr.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -306,17 +306,16 @@ using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::intl;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::net;
 using namespace mozilla::widget;
 using mozilla::loader::PScriptCacheChild;
 
-#ifdef MOZ_GECKO_PROFILER
 namespace geckoprofiler::markers {
 struct ProcessPriorityChange {
   static constexpr Span<const char> MarkerTypeName() {
     return MakeStringSpan("ProcessPriorityChange");
   }
   static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
                                    const ProfilerString8View& aPreviousPriority,
                                    const ProfilerString8View& aNewPriority) {
@@ -354,17 +353,16 @@ struct ProcessPriority {
     MS schema{MS::Location::markerChart, MS::Location::markerTable};
     schema.AddKeyFormat("Priority", MS::Format::string);
     schema.AddKeyFormat("Marker cause", MS::Format::string);
     schema.SetAllLabels("priority: {marker.data.Priority}");
     return schema;
   }
 };
 }  // namespace geckoprofiler::markers
-#endif  // MOZ_GECKO_PROFILER
 
 namespace mozilla {
 
 namespace dom {
 
 // IPC sender for remote GC/CC logging.
 class CycleCollectWithLogsChild final : public PCycleCollectWithLogsChild {
  public:
@@ -619,34 +617,32 @@ ContentChild::ContentChild()
       ,
       mIsForBrowser(false),
       mIsAlive(true),
       mShuttingDown(false) {
   // This process is a content process, so it's clearly running in
   // multiprocess mode!
   nsDebugImpl::SetMultiprocessMode("Child");
 
-#ifdef MOZ_GECKO_PROFILER
   // Our static analysis doesn't allow capturing ref-counted pointers in
   // lambdas, so we need to hide it in a uintptr_t. This is safe because this
   // lambda will be destroyed in ~ContentChild().
   uintptr_t self = reinterpret_cast<uintptr_t>(this);
   profiler_add_state_change_callback(
       AllProfilingStates(),
       [self](ProfilingState aProfilingState) {
         const ContentChild* selfPtr =
             reinterpret_cast<const ContentChild*>(self);
         PROFILER_MARKER("Process Priority", OTHER,
                         mozilla::MarkerThreadId::MainThread(), ProcessPriority,
                         ProfilerString8View::WrapNullTerminatedString(
                             ProcessPriorityToString(selfPtr->mProcessPriority)),
                         aProfilingState);
       },
       self);
-#endif  // MOZ_GECKO_PROFILER
 
   // When ContentChild is created, the observer service does not even exist.
   // When ContentChild::RecvSetXPCOMProcessAttributes is called (the first
   // IPDL call made on this object), shutdown may have already happened. Thus
   // we create a canary here that relies upon getting cleared if shutdown
   // happens without requiring the observer service at this time.
   if (!sShutdownCanary) {
     sShutdownCanary = new ShutdownCanary();
@@ -657,19 +653,17 @@ ContentChild::ContentChild()
 #ifdef _MSC_VER
 #  pragma warning(push)
 #  pragma warning(                                                  \
       disable : 4722) /* Silence "destructor never returns" warning \
                        */
 #endif
 
 ContentChild::~ContentChild() {
-#ifdef MOZ_GECKO_PROFILER
   profiler_remove_state_change_callback(reinterpret_cast<uintptr_t>(this));
-#endif  // MOZ_GECKO_PROFILER
 
 #ifndef NS_FREE_PERMANENT_DATA
   MOZ_CRASH("Content Child shouldn't be destroyed.");
 #endif
 }
 
 #ifdef _MSC_VER
 #  pragma warning(pop)
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -558,17 +558,16 @@ ParticularProcessPriorityManager::Partic
       mPriority(PROCESS_PRIORITY_UNKNOWN),
       mHoldsCPUWakeLock(false),
       mHoldsHighPriorityWakeLock(false),
       mHoldsPlayingAudioWakeLock(false),
       mHoldsPlayingVideoWakeLock(false) {
   MOZ_ASSERT(XRE_IsParentProcess());
   MOZ_RELEASE_ASSERT(!aContentParent->IsDead());
   LOGP("Creating ParticularProcessPriorityManager.");
-#ifdef MOZ_GECKO_PROFILER
   // Our static analysis doesn't allow capturing ref-counted pointers in
   // lambdas, so we need to hide it in a uintptr_t. This is safe because this
   // lambda will be destroyed in ~ParticularProcessPriorityManager().
   uintptr_t self = reinterpret_cast<uintptr_t>(this);
   profiler_add_state_change_callback(
       AllProfilingStates(),
       [self](ProfilingState aProfilingState) {
         const ParticularProcessPriorityManager* selfPtr =
@@ -576,17 +575,16 @@ ParticularProcessPriorityManager::Partic
         PROFILER_MARKER("Subprocess Priority", OTHER,
                         MarkerThreadId::MainThread(), SubProcessPriority,
                         selfPtr->Pid(),
                         ProfilerString8View::WrapNullTerminatedString(
                             ProcessPriorityToString(selfPtr->mPriority)),
                         aProfilingState);
       },
       self);
-#endif  // MOZ_GECKO_PROFILER
 }
 
 void ParticularProcessPriorityManager::Init() {
   RegisterWakeLockObserver(this);
 
   // This process may already hold the CPU lock; for example, our parent may
   // have acquired it on our behalf.
   mHoldsCPUWakeLock = IsHoldingWakeLock(u"cpu"_ns);
@@ -607,19 +605,17 @@ bool ParticularProcessPriorityManager::I
   WakeLockInformation info;
   GetWakeLockInfo(aTopic, &info);
   return info.lockingProcesses().Contains(ChildID());
 }
 
 ParticularProcessPriorityManager::~ParticularProcessPriorityManager() {
   LOGP("Destroying ParticularProcessPriorityManager.");
 
-#ifdef MOZ_GECKO_PROFILER
   profiler_remove_state_change_callback(reinterpret_cast<uintptr_t>(this));
-#endif  // MOZ_GECKO_PROFILER
 
   ShutDown();
 }
 
 /* virtual */
 void ParticularProcessPriorityManager::Notify(
     const WakeLockInformation& aInfo) {
   if (!mContentParent) {
--- a/dom/media/AsyncLogger.h
+++ b/dom/media/AsyncLogger.h
@@ -115,27 +115,25 @@ class AsyncLogger {
             "{\"name\": \"%s\", \"cat\": \"%s\", \"ph\": \"%c\","
             "\"ts\": %" PRIu64
             ", \"pid\": %d, \"tid\":"
             " %zu, \"args\": { \"comment\": \"%s\"}},",
             aName, aCategory, TRACING_PHASE_STRINGS[static_cast<int>(aPhase)],
             NowInUs(), getpid(),
             std::hash<std::thread::id>{}(std::this_thread::get_id()), aComment);
       } else {
-#ifdef MOZ_GECKO_PROFILER
         auto* msg = new MPSCQueue<TracePayload>::Message();
         msg->data.mTID = profiler_current_thread_id();
         msg->data.mPhase = aPhase;
         msg->data.mTimestamp = TimeStamp::NowUnfuzzed();
         msg->data.mDurationUs = 0;  // unused, duration is end - begin
         size_t len = std::min(strlen(aName), ArrayLength(msg->data.mName));
         memcpy(msg->data.mName, aName, len);
         msg->data.mName[len] = 0;
         mMessageQueueProfiler.Push(msg);
-#endif
       }
     }
   }
 
   // Log something that has a beginning and a duration
   void LogDuration(const char* aName, const char* aCategory, uint64_t aDuration,
                    uint64_t aFrames, uint64_t aSampleRate) {
     if (Enabled()) {
@@ -145,62 +143,56 @@ class AsyncLogger {
             "\"ts\": %" PRIu64 ", \"dur\": %" PRIu64
             ", \"pid\": %d,"
             "\"tid\": %zu, \"args\": { \"comment\": \"%" PRIu64 "/%" PRIu64
             "\"}},",
             aName, aCategory, NowInUs(), aDuration, getpid(),
             std::hash<std::thread::id>{}(std::this_thread::get_id()), aFrames,
             aSampleRate);
       } else {
-#ifdef MOZ_GECKO_PROFILER
         auto* msg = new MPSCQueue<TracePayload>::Message();
         msg->data.mTID = profiler_current_thread_id();
         msg->data.mPhase = TracingPhase::COMPLETE;
         msg->data.mTimestamp = TimeStamp::NowUnfuzzed();
         msg->data.mDurationUs =
             (static_cast<double>(aFrames) / aSampleRate) * 1e6;
         size_t len = std::min(strlen(aName), ArrayLength(msg->data.mName));
         memcpy(msg->data.mName, aName, len);
         msg->data.mName[len] = 0;
         mMessageQueueProfiler.Push(msg);
-#endif
       }
     }
   }
   void LogMozLog(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3) {
     auto* msg = new MPSCQueue<TextPayload>::Message();
     va_list args;
     va_start(args, format);
     VsprintfLiteral(msg->data.mPayload, format, args);
     va_end(args);
     mMessageQueueLog.Push(msg);
   }
 
   bool Enabled() {
     return (mMode == AsyncLoggerOutputMode::MOZLOG &&
-            MOZ_LOG_TEST(mLogModule, mozilla::LogLevel::Verbose))
-#ifdef MOZ_GECKO_PROFILER
-           || (mMode == AsyncLoggerOutputMode::PROFILER && profiler_is_active())
-#endif
-        ;
+            MOZ_LOG_TEST(mLogModule, mozilla::LogLevel::Verbose)) ||
+           (mMode == AsyncLoggerOutputMode::PROFILER && profiler_is_active());
   }
 
  private:
   void Run() {
     mThread.reset(new std::thread([this]() {
       PROFILER_REGISTER_THREAD("AsyncLogger");
       while (mRunning) {
         {
           TextPayload message;
           while (mMessageQueueLog.Pop(&message) && mRunning) {
             MOZ_LOG(mLogModule, mozilla::LogLevel::Verbose,
                     ("%s", message.mPayload));
           }
         }
-#ifdef MOZ_GECKO_PROFILER
         {
           struct BudgetMarker {
             static constexpr Span<const char> MarkerTypeName() {
               return MakeStringSpan("Budget");
             }
             static void StreamJSONMarkerData(
                 baseprofiler::SpliceableJSONWriter& aWriter) {}
             static MarkerSchema MarkerTypeDisplay() {
@@ -230,17 +222,16 @@ class AsyncLogger {
                    MarkerTiming::Interval(
                        message.mTimestamp,
                        message.mTimestamp + TimeDuration::FromMicroseconds(
                                                 message.mDurationUs))},
                   BudgetMarker{});
             }
           }
         }
-#endif
         Sleep();
       }
       PROFILER_UNREGISTER_THREAD();
     }));
     // cleanup is done via mRunning
     mThread->detach();
   }
 
@@ -249,19 +240,17 @@ class AsyncLogger {
     return (TimeStamp::NowUnfuzzed() - base).ToMicroseconds();
   }
 
   void Sleep() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
 
   std::unique_ptr<std::thread> mThread;
   mozilla::LazyLogModule mLogModule;
   MPSCQueue<TextPayload> mMessageQueueLog;
-#ifdef MOZ_GECKO_PROFILER
   MPSCQueue<TracePayload> mMessageQueueProfiler;
-#endif
   std::atomic<bool> mRunning;
   std::atomic<AsyncLoggerOutputMode> mMode;
 };
 
 }  // end namespace mozilla
 
 #if defined(_WIN32)
 #  undef getpid
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -231,19 +231,16 @@ class MediaManager final : public nsIMed
   RefPtr<StreamPromise> GetUserMedia(
       nsPIDOMWindowInner* aWindow,
       const dom::MediaStreamConstraints& aConstraints,
       dom::CallerType aCallerType);
 
   RefPtr<DeviceSetPromise> EnumerateDevices(nsPIDOMWindowInner* aWindow,
                                             dom::CallerType aCallerType);
 
-  nsresult EnumerateDevices(nsPIDOMWindowInner* aWindow,
-                            dom::Promise& aPromise);
-
   RefPtr<DevicePromise> SelectAudioOutput(
       nsPIDOMWindowInner* aWindow, const dom::AudioOutputOptions& aOptions,
       dom::CallerType aCallerType);
   // Get the sink that corresponds to the given device id.
   // It is resposible to check if an application is
   // authorized to play audio through the requested device.
   // The returned promise will be resolved with the device
   // information if the device id matches one and operation is
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp
@@ -37,26 +37,19 @@
 
 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 extern const GUID CLSID_WebmMfVpxDec;
 
 namespace mozilla {
 
 // Helper function to add a profile marker and log at the same time.
-static void MOZ_FORMAT_PRINTF(2, 3) WmfDecoderModuleMarkerAndLog(
-#ifdef MOZ_GECKO_PROFILER
-    const ProfilerString8View&
-#else
-    // ProfilerString8View is not defined in non-MOZ_GECKO_PROFILER builds, but
-    // we still need to accept the given marker tag, though it won't be used.
-    const char*
-#endif
-        aMarkerTag,
-    const char* aFormat, ...) {
+static void MOZ_FORMAT_PRINTF(2, 3)
+    WmfDecoderModuleMarkerAndLog(const ProfilerString8View& aMarkerTag,
+                                 const char* aFormat, ...) {
   va_list ap;
   va_start(ap, aFormat);
   const nsVprintfCString markerString(aFormat, ap);
   va_end(ap);
   PROFILER_MARKER_TEXT(aMarkerTag, MEDIA_PLAYBACK, {}, markerString);
   LOG("%s", markerString.get());
 }
 
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -219,17 +219,16 @@ void Performance::ClearUserEntries(const
   mUserEntries.RemoveElementsBy([name, entryType](const auto& entry) {
     return (!name || entry->GetName() == name) &&
            (entry->GetEntryType() == entryType);
   });
 }
 
 void Performance::ClearResourceTimings() { mResourceEntries.Clear(); }
 
-#ifdef MOZ_GECKO_PROFILER
 struct UserTimingMarker {
   static constexpr Span<const char> MarkerTypeName() {
     return MakeStringSpan("UserTiming");
   }
   static void StreamJSONMarkerData(
       baseprofiler::SpliceableJSONWriter& aWriter,
       const ProfilerString16View& aName, bool aIsMeasure,
       const Maybe<ProfilerString16View>& aStartMark,
@@ -264,44 +263,41 @@ struct UserTimingMarker {
     schema.AddKeyLabelFormat("startMark", "Start Mark", MS::Format::string);
     schema.AddKeyLabelFormat("endMark", "End Mark", MS::Format::string);
     schema.AddStaticLabelValue("Description",
                                "UserTimingMeasure is created using the DOM API "
                                "performance.measure().");
     return schema;
   }
 };
-#endif
 
 void Performance::Mark(const nsAString& aName, ErrorResult& aRv) {
   // We add nothing when 'privacy.resistFingerprinting' is on.
   if (nsContentUtils::ShouldResistFingerprinting()) {
     return;
   }
 
   if (IsPerformanceTimingAttribute(aName)) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   RefPtr<PerformanceMark> performanceMark =
       new PerformanceMark(GetParentObject(), aName, Now());
   InsertUserEntry(performanceMark);
 
-#ifdef MOZ_GECKO_PROFILER
   if (profiler_can_accept_markers()) {
     Maybe<uint64_t> innerWindowId;
     if (GetOwner()) {
       innerWindowId = Some(GetOwner()->WindowID());
     }
     profiler_add_marker("UserTiming", geckoprofiler::category::DOM,
                         MarkerInnerWindowId(innerWindowId), UserTimingMarker{},
                         aName, /* aIsMeasure */ false, Nothing{}, Nothing{});
   }
-#endif
 }
 
 void Performance::ClearMarks(const Optional<nsAString>& aName) {
   ClearUserEntries(aName, u"mark"_ns);
 }
 
 DOMHighResTimeStamp Performance::ResolveTimestampFromName(
     const nsAString& aName, ErrorResult& aRv) {
@@ -361,17 +357,16 @@ void Performance::Measure(const nsAStrin
   } else {
     endTime = Now();
   }
 
   RefPtr<PerformanceMeasure> performanceMeasure =
       new PerformanceMeasure(GetParentObject(), aName, startTime, endTime);
   InsertUserEntry(performanceMeasure);
 
-#ifdef MOZ_GECKO_PROFILER
   if (profiler_can_accept_markers()) {
     TimeStamp startTimeStamp =
         CreationTimeStamp() + TimeDuration::FromMilliseconds(startTime);
     TimeStamp endTimeStamp =
         CreationTimeStamp() + TimeDuration::FromMilliseconds(endTime);
 
     // Convert to Maybe values so that Optional types do not need to be used in
     // the profiler.
@@ -389,17 +384,16 @@ void Performance::Measure(const nsAStrin
       innerWindowId = Some(GetOwner()->WindowID());
     }
     profiler_add_marker("UserTiming", geckoprofiler::category::DOM,
                         {MarkerTiming::Interval(startTimeStamp, endTimeStamp),
                          MarkerInnerWindowId(innerWindowId)},
                         UserTimingMarker{}, aName, /* aIsMeasure */ true,
                         startMark, endMark);
   }
-#endif
 }
 
 void Performance::ClearMeasures(const Optional<nsAString>& aName) {
   ClearUserEntries(aName, u"measure"_ns);
 }
 
 void Performance::LogEntry(PerformanceEntry* aEntry,
                            const nsACString& aOwner) const {
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -83,16 +83,17 @@
 #include "mozilla/dom/quota/DirectoryLock.h"
 #include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/dom/quota/PQuota.h"
 #include "mozilla/dom/quota/PQuotaParent.h"
 #include "mozilla/dom/quota/PQuotaRequest.h"
 #include "mozilla/dom/quota/PQuotaRequestParent.h"
 #include "mozilla/dom/quota/PQuotaUsageRequest.h"
 #include "mozilla/dom/quota/PQuotaUsageRequestParent.h"
+#include "mozilla/dom/quota/ScopedLogExtraInfo.h"
 #include "mozilla/dom/simpledb/ActorsParent.h"
 #include "mozilla/fallible.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/net/MozURL.h"
@@ -10639,19 +10640,22 @@ nsresult CreateOrUpgradeDirectoryMetadat
     QM_TRY(MaybeUpgradeOriginDirectory(aOriginProps.mDirectory.get()));
 
     aOriginProps.mTimestamp = GetOriginLastModifiedTime(aOriginProps);
   } else {
     int64_t timestamp;
     nsCString group;
     nsCString origin;
     Nullable<bool> isApp;
-    nsresult rv = GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
-                                       group, origin, isApp);
-    if (NS_FAILED(rv)) {
+
+    QM_WARNONLY_TRY_UNWRAP(
+        const auto maybeDirectoryMetadata,
+        ToResult(GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
+                                      group, origin, isApp)));
+    if (!maybeDirectoryMetadata) {
       aOriginProps.mTimestamp = GetOriginLastModifiedTime(aOriginProps);
       aOriginProps.mNeedsRestore = true;
     } else if (!isApp.IsNull()) {
       aOriginProps.mIgnore = true;
     }
   }
 
   *aRemoved = false;
@@ -10743,19 +10747,22 @@ nsresult UpgradeStorageFrom0_0To1_0Helpe
     OriginProps& aOriginProps, bool* aRemoved) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aRemoved);
 
   int64_t timestamp;
   nsCString group;
   nsCString origin;
   Nullable<bool> isApp;
-  nsresult rv = GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
-                                     group, origin, isApp);
-  if (NS_FAILED(rv) || isApp.IsNull()) {
+
+  QM_WARNONLY_TRY_UNWRAP(
+      const auto maybeDirectoryMetadata,
+      ToResult(GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
+                                    group, origin, isApp)));
+  if (!maybeDirectoryMetadata || isApp.IsNull()) {
     aOriginProps.mTimestamp = GetOriginLastModifiedTime(aOriginProps);
     aOriginProps.mNeedsRestore = true;
   } else {
     aOriginProps.mTimestamp = timestamp;
   }
 
   *aRemoved = false;
   return NS_OK;
@@ -10860,26 +10867,30 @@ nsresult UpgradeStorageFrom1_0To2_0Helpe
     *aRemoved = true;
     return NS_OK;
   }
 
   int64_t timestamp;
   nsCString group;
   nsCString origin;
   Nullable<bool> isApp;
-  nsresult rv = GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
-                                     group, origin, isApp);
-  if (NS_FAILED(rv) || isApp.IsNull()) {
+  QM_WARNONLY_TRY_UNWRAP(
+      const auto maybeDirectoryMetadata,
+      ToResult(GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
+                                    group, origin, isApp)));
+  if (!maybeDirectoryMetadata || isApp.IsNull()) {
     aOriginProps.mNeedsRestore = true;
   }
 
   nsCString suffix;
-  rv = GetDirectoryMetadata2(aOriginProps.mDirectory.get(), timestamp, suffix,
-                             group, origin, isApp.SetValue());
-  if (NS_FAILED(rv)) {
+  QM_WARNONLY_TRY_UNWRAP(
+      const auto maybeDirectoryMetadata2,
+      ToResult(GetDirectoryMetadata2(aOriginProps.mDirectory.get(), timestamp,
+                                     suffix, group, origin, isApp.SetValue())));
+  if (!maybeDirectoryMetadata2) {
     aOriginProps.mTimestamp = GetOriginLastModifiedTime(aOriginProps);
     aOriginProps.mNeedsRestore2 = true;
   } else {
     aOriginProps.mTimestamp = timestamp;
   }
 
   *aRemoved = false;
   return NS_OK;
@@ -10918,26 +10929,30 @@ nsresult UpgradeStorageFrom2_0To2_1Helpe
 
   QM_TRY(
       MaybeUpgradeClients(aOriginProps, &Client::UpgradeStorageFrom2_0To2_1));
 
   int64_t timestamp;
   nsCString group;
   nsCString origin;
   Nullable<bool> isApp;
-  nsresult rv = GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
-                                     group, origin, isApp);
-  if (NS_FAILED(rv) || isApp.IsNull()) {
+  QM_WARNONLY_TRY_UNWRAP(
+      const auto maybeDirectoryMetadata,
+      ToResult(GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
+                                    group, origin, isApp)));
+  if (!maybeDirectoryMetadata || isApp.IsNull()) {
     aOriginProps.mNeedsRestore = true;
   }
 
   nsCString suffix;
-  rv = GetDirectoryMetadata2(aOriginProps.mDirectory.get(), timestamp, suffix,
-                             group, origin, isApp.SetValue());
-  if (NS_FAILED(rv)) {
+  QM_WARNONLY_TRY_UNWRAP(
+      const auto maybeDirectoryMetadata2,
+      ToResult(GetDirectoryMetadata2(aOriginProps.mDirectory.get(), timestamp,
+                                     suffix, group, origin, isApp.SetValue())));
+  if (!maybeDirectoryMetadata2) {
     aOriginProps.mTimestamp = GetOriginLastModifiedTime(aOriginProps);
     aOriginProps.mNeedsRestore2 = true;
   } else {
     aOriginProps.mTimestamp = timestamp;
   }
 
   *aRemoved = false;
   return NS_OK;
@@ -10969,26 +10984,30 @@ nsresult UpgradeStorageFrom2_1To2_2Helpe
 
   QM_TRY(
       MaybeUpgradeClients(aOriginProps, &Client::UpgradeStorageFrom2_1To2_2));
 
   int64_t timestamp;
   nsCString group;
   nsCString origin;
   Nullable<bool> isApp;
-  nsresult rv = GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
-                                     group, origin, isApp);
-  if (NS_FAILED(rv) || isApp.IsNull()) {
+  QM_WARNONLY_TRY_UNWRAP(
+      const auto maybeDirectoryMetadata,
+      ToResult(GetDirectoryMetadata(aOriginProps.mDirectory.get(), timestamp,
+                                    group, origin, isApp)));
+  if (!maybeDirectoryMetadata || isApp.IsNull()) {
     aOriginProps.mNeedsRestore = true;
   }
 
   nsCString suffix;
-  rv = GetDirectoryMetadata2(aOriginProps.mDirectory.get(), timestamp, suffix,
-                             group, origin, isApp.SetValue());
-  if (NS_FAILED(rv)) {
+  QM_WARNONLY_TRY_UNWRAP(
+      const auto maybeDirectoryMetadata2,
+      ToResult(GetDirectoryMetadata2(aOriginProps.mDirectory.get(), timestamp,
+                                     suffix, group, origin, isApp.SetValue())));
+  if (!maybeDirectoryMetadata2) {
     aOriginProps.mTimestamp = GetOriginLastModifiedTime(aOriginProps);
     aOriginProps.mNeedsRestore2 = true;
   } else {
     aOriginProps.mTimestamp = timestamp;
   }
 
   *aRemoved = false;
   return NS_OK;
--- a/dom/quota/CachingDatabaseConnection.h
+++ b/dom/quota/CachingDatabaseConnection.h
@@ -15,16 +15,17 @@
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 #include "nsString.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/InitializedOnce.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/dom/quota/QuotaCommon.h"
+#include "mozilla/dom/quota/ScopedLogExtraInfo.h"
 
 namespace mozilla::dom::quota {
 
 class CachingDatabaseConnection {
  public:
   class CachedStatement;
 
   // A stack-only RAII wrapper that resets its borrowed statement when the
--- a/dom/quota/QuotaCommon.cpp
+++ b/dom/quota/QuotaCommon.cpp
@@ -12,16 +12,17 @@
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
 #include "mozilla/ErrorNames.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TelemetryComms.h"
 #include "mozilla/TelemetryEventEnums.h"
 #include "mozilla/TextUtils.h"
+#include "mozilla/dom/quota/ScopedLogExtraInfo.h"
 #include "nsIConsoleService.h"
 #include "nsIFile.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringFlags.h"
 #include "nsTStringRepr.h"
 #include "nsUnicharUtils.h"
 #include "nsXPCOM.h"
 #include "nsXULAppAPI.h"
@@ -236,85 +237,16 @@ template Result<SingleStepSuccessType<Si
 CreateAndExecuteSingleStepStatement<SingleStepResult::AssertHasResult>(
     mozIStorageConnection& aConnection, const nsACString& aStatementString);
 
 template Result<SingleStepSuccessType<SingleStepResult::ReturnNullIfNoResult>,
                 nsresult>
 CreateAndExecuteSingleStepStatement<SingleStepResult::ReturnNullIfNoResult>(
     mozIStorageConnection& aConnection, const nsACString& aStatementString);
 
-#ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
-MOZ_THREAD_LOCAL(const nsACString*) ScopedLogExtraInfo::sQueryValue;
-MOZ_THREAD_LOCAL(const nsACString*) ScopedLogExtraInfo::sContextValue;
-
-/* static */
-auto ScopedLogExtraInfo::FindSlot(const char* aTag) {
-  // XXX For now, don't use a real map but just allow the known tag values.
-
-  if (aTag == kTagQuery) {
-    return &sQueryValue;
-  }
-
-  if (aTag == kTagContext) {
-    return &sContextValue;
-  }
-
-  MOZ_CRASH("Unknown tag!");
-}
-
-ScopedLogExtraInfo::~ScopedLogExtraInfo() {
-  if (mTag) {
-    MOZ_ASSERT(&mCurrentValue == FindSlot(mTag)->get(),
-               "Bad scoping of ScopedLogExtraInfo, must not be interleaved!");
-
-    FindSlot(mTag)->set(mPreviousValue);
-  }
-}
-
-ScopedLogExtraInfo::ScopedLogExtraInfo(ScopedLogExtraInfo&& aOther)
-    : mTag(aOther.mTag),
-      mPreviousValue(aOther.mPreviousValue),
-      mCurrentValue(std::move(aOther.mCurrentValue)) {
-  aOther.mTag = nullptr;
-  FindSlot(mTag)->set(&mCurrentValue);
-}
-
-/* static */ ScopedLogExtraInfo::ScopedLogExtraInfoMap
-ScopedLogExtraInfo::GetExtraInfoMap() {
-  // This could be done in a cheaper way, but this is never called on a hot
-  // path, so we anticipate using a real map inside here to make use simpler for
-  // the caller(s).
-
-  ScopedLogExtraInfoMap map;
-  if (XRE_IsParentProcess()) {
-    if (sQueryValue.get()) {
-      map.emplace(kTagQuery, sQueryValue.get());
-    }
-
-    if (sContextValue.get()) {
-      map.emplace(kTagContext, sContextValue.get());
-    }
-  }
-  return map;
-}
-
-/* static */ void ScopedLogExtraInfo::Initialize() {
-  MOZ_ALWAYS_TRUE(sQueryValue.init());
-  MOZ_ALWAYS_TRUE(sContextValue.init());
-}
-
-void ScopedLogExtraInfo::AddInfo() {
-  auto* slot = FindSlot(mTag);
-  MOZ_ASSERT(slot);
-  mPreviousValue = slot->get();
-
-  slot->set(&mCurrentValue);
-}
-#endif
-
 namespace detail {
 
 // Given aPath of /foo/bar/baz and aRelativePath of /bar/baz, returns the
 // absolute portion of aPath /foo by removing the common suffix from aPath.
 nsDependentCSubstring GetTreeBase(const nsLiteralCString& aPath,
                                   const nsLiteralCString& aRelativePath) {
   MOZ_ASSERT(StringEndsWith(aPath, aRelativePath));
   return Substring(aPath, 0, aPath.Length() - aRelativePath.Length());
--- a/dom/quota/QuotaCommon.h
+++ b/dom/quota/QuotaCommon.h
@@ -7,29 +7,27 @@
 #ifndef mozilla_dom_quota_quotacommon_h__
 #define mozilla_dom_quota_quotacommon_h__
 
 #include "mozilla/dom/quota/Config.h"
 
 #include <algorithm>
 #include <cstddef>
 #include <cstdint>
-#include <map>
 #include <type_traits>
 #include <utility>
 #include "mozIStorageStatement.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MacroArgs.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Result.h"
 #include "mozilla/ResultExtensions.h"
-#include "mozilla/ThreadLocal.h"
 #if defined(QM_LOG_ERROR_ENABLED) && defined(QM_ERROR_STACKS_ENABLED)
 #  include "mozilla/Variant.h"
 #endif
 #include "mozilla/dom/QMResult.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
@@ -37,17 +35,16 @@
 #include "nsIEventTarget.h"
 #include "nsIFile.h"
 #include "nsLiteralString.h"
 #include "nsPrintfCString.h"
 #include "nsReadableUtils.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsTLiteralString.h"
-#include "nsXULAppAPI.h"
 
 namespace mozilla {
 template <typename T>
 class NotNull;
 }
 
 // Proper use of unique variable names can be tricky (especially if nesting of
 // the final macro is required).
@@ -1336,68 +1333,16 @@ void LogError(const nsACString& aExpr, M
 #endif
 
 #ifdef DEBUG
 Result<bool, nsresult> WarnIfFileIsUnknown(nsIFile& aFile,
                                            const char* aSourceFilePath,
                                            int32_t aSourceFileLine);
 #endif
 
-struct MOZ_STACK_CLASS ScopedLogExtraInfo {
-  static constexpr const char kTagQuery[] = "query";
-  static constexpr const char kTagContext[] = "context";
-
-#ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
- private:
-  static auto FindSlot(const char* aTag);
-
- public:
-  template <size_t N>
-  ScopedLogExtraInfo(const char (&aTag)[N], const nsACString& aExtraInfo)
-      : mTag{aTag}, mCurrentValue{aExtraInfo} {
-    // Initialize is currently only called in the parent process, we could call
-    // it directly from nsLayoutStatics::Initialize in the content process to
-    // allow use of ScopedLogExtraInfo in that too. The check in GetExtraInfoMap
-    // must be removed then.
-    MOZ_ASSERT(XRE_IsParentProcess());
-
-    AddInfo();
-  }
-
-  ~ScopedLogExtraInfo();
-
-  ScopedLogExtraInfo(ScopedLogExtraInfo&& aOther);
-  ScopedLogExtraInfo& operator=(ScopedLogExtraInfo&& aOther) = delete;
-
-  ScopedLogExtraInfo(const ScopedLogExtraInfo&) = delete;
-  ScopedLogExtraInfo& operator=(const ScopedLogExtraInfo&) = delete;
-
-  using ScopedLogExtraInfoMap = std::map<const char*, const nsACString*>;
-  static ScopedLogExtraInfoMap GetExtraInfoMap();
-
-  static void Initialize();
-
- private:
-  const char* mTag;
-  const nsACString* mPreviousValue;
-  nsCString mCurrentValue;
-
-  static MOZ_THREAD_LOCAL(const nsACString*) sQueryValue;
-  static MOZ_THREAD_LOCAL(const nsACString*) sContextValue;
-
-  void AddInfo();
-#else
-  template <size_t N>
-  ScopedLogExtraInfo(const char (&aTag)[N], const nsACString& aExtraInfo) {}
-
-  // user-defined to silence unused variable warnings
-  ~ScopedLogExtraInfo() {}
-#endif
-};
-
 // As HandleError is a function that will only be called in error cases, it is
 // marked with MOZ_COLD to avoid bloating the code of calling functions, if it's
 // not empty.
 //
 // For the same reason, the string-ish parameters are of type const char* rather
 // than any ns*String type, to minimize the code at each call site. This
 // deliberately de-optimizes runtime performance, which is uncritical during
 // error handling.
copy from dom/quota/QuotaCommon.cpp
copy to dom/quota/ScopedLogExtraInfo.cpp
--- a/dom/quota/QuotaCommon.cpp
+++ b/dom/quota/ScopedLogExtraInfo.cpp
@@ -1,251 +1,18 @@
 /* -*- 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 "QuotaCommon.h"
-
-#ifdef QM_ERROR_STACKS_ENABLED
-#  include "base/process_util.h"
-#endif
-#include "mozIStorageConnection.h"
-#include "mozIStorageStatement.h"
-#include "mozilla/ErrorNames.h"
-#include "mozilla/Logging.h"
-#include "mozilla/Telemetry.h"
-#include "mozilla/TelemetryComms.h"
-#include "mozilla/TelemetryEventEnums.h"
-#include "mozilla/TextUtils.h"
-#include "nsIConsoleService.h"
-#include "nsIFile.h"
-#include "nsServiceManagerUtils.h"
-#include "nsStringFlags.h"
-#include "nsTStringRepr.h"
-#include "nsUnicharUtils.h"
-#include "nsXPCOM.h"
-#include "nsXULAppAPI.h"
-
-#ifdef XP_WIN
-#  include "mozilla/Atomics.h"
-#  include "mozilla/ipc/BackgroundParent.h"
-#  include "mozilla/StaticPrefs_dom.h"
-#  include "nsILocalFileWin.h"
-#endif
+#include "ScopedLogExtraInfo.h"
 
 namespace mozilla::dom::quota {
 
-using namespace mozilla::Telemetry;
-
-namespace {
-
-#ifdef DEBUG
-constexpr auto kDSStoreFileName = u".DS_Store"_ns;
-constexpr auto kDesktopFileName = u".desktop"_ns;
-constexpr auto kDesktopIniFileName = u"desktop.ini"_ns;
-constexpr auto kThumbsDbFileName = u"thumbs.db"_ns;
-#endif
-
-#ifdef XP_WIN
-Atomic<int32_t> gUseDOSDevicePathSyntax(-1);
-#endif
-
-LazyLogModule gLogger("QuotaManager");
-
-void AnonymizeCString(nsACString& aCString, uint32_t aStart) {
-  MOZ_ASSERT(!aCString.IsEmpty());
-  MOZ_ASSERT(aStart < aCString.Length());
-
-  char* iter = aCString.BeginWriting() + aStart;
-  char* end = aCString.EndWriting();
-
-  while (iter != end) {
-    char c = *iter;
-
-    if (IsAsciiAlpha(c)) {
-      *iter = 'a';
-    } else if (IsAsciiDigit(c)) {
-      *iter = 'D';
-    }
-
-    ++iter;
-  }
-}
-
-}  // namespace
-
-const char kQuotaGenericDelimiter = '|';
-
-#ifdef NIGHTLY_BUILD
-const nsLiteralCString kQuotaInternalError = "internal"_ns;
-const nsLiteralCString kQuotaExternalError = "external"_ns;
-#endif
-
-LogModule* GetQuotaManagerLogger() { return gLogger; }
-
-void AnonymizeCString(nsACString& aCString) {
-  if (aCString.IsEmpty()) {
-    return;
-  }
-  AnonymizeCString(aCString, /* aStart */ 0);
-}
-
-void AnonymizeOriginString(nsACString& aOriginString) {
-  if (aOriginString.IsEmpty()) {
-    return;
-  }
-
-  int32_t start = aOriginString.FindChar(':');
-  if (start < 0) {
-    start = 0;
-  }
-
-  AnonymizeCString(aOriginString, start);
-}
-
-#ifdef XP_WIN
-void CacheUseDOSDevicePathSyntaxPrefValue() {
-  MOZ_ASSERT(XRE_IsParentProcess());
-  AssertIsOnBackgroundThread();
-
-  if (gUseDOSDevicePathSyntax == -1) {
-    bool useDOSDevicePathSyntax =
-        StaticPrefs::dom_quotaManager_useDOSDevicePathSyntax_DoNotUseDirectly();
-    gUseDOSDevicePathSyntax = useDOSDevicePathSyntax ? 1 : 0;
-  }
-}
-#endif
-
-Result<nsCOMPtr<nsIFile>, nsresult> QM_NewLocalFile(const nsAString& aPath) {
-  QM_TRY_UNWRAP(auto file,
-                ToResultInvoke<nsCOMPtr<nsIFile>>(NS_NewLocalFile, aPath,
-                                                  /* aFollowLinks */ false),
-                QM_PROPAGATE, [&aPath](const nsresult rv) {
-                  QM_WARNING("Failed to construct a file for path (%s)",
-                             NS_ConvertUTF16toUTF8(aPath).get());
-                });
-
-#ifdef XP_WIN
-  MOZ_ASSERT(gUseDOSDevicePathSyntax != -1);
-
-  if (gUseDOSDevicePathSyntax) {
-    QM_TRY_INSPECT(const auto& winFile,
-                   ToResultGet<nsCOMPtr<nsILocalFileWin>>(
-                       MOZ_SELECT_OVERLOAD(do_QueryInterface), file));
-
-    MOZ_ASSERT(winFile);
-    winFile->SetUseDOSDevicePathSyntax(true);
-  }
-#endif
-
-  return file;
-}
-
-nsDependentCSubstring GetLeafName(const nsACString& aPath) {
-  nsACString::const_iterator start, end;
-  aPath.BeginReading(start);
-  aPath.EndReading(end);
-
-  bool found = RFindInReadable("/"_ns, start, end);
-  if (found) {
-    start = end;
-  }
-
-  aPath.EndReading(end);
-
-  return nsDependentCSubstring(start.get(), end.get());
-}
-
-Result<nsCOMPtr<nsIFile>, nsresult> CloneFileAndAppend(
-    nsIFile& aDirectory, const nsAString& aPathElement) {
-  QM_TRY_UNWRAP(auto resultFile, MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIFile>,
-                                                            aDirectory, Clone));
-
-  QM_TRY(resultFile->Append(aPathElement));
-
-  return resultFile;
-}
-
-Result<nsIFileKind, nsresult> GetDirEntryKind(nsIFile& aFile) {
-  // Callers call this function without checking if the directory already
-  // exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we
-  // just want to log NS_ERROR_FILE_NOT_FOUND,
-  // NS_ERROR_FILE_TARGET_DOES_NOT_EXIST and NS_ERROR_FILE_FS_CORRUPTED results
-  // and not spam the reports.
-  QM_TRY_RETURN(QM_OR_ELSE_LOG_VERBOSE_IF(
-      MOZ_TO_RESULT_INVOKE(aFile, IsDirectory).map([](const bool isDirectory) {
-        return isDirectory ? nsIFileKind::ExistsAsDirectory
-                           : nsIFileKind::ExistsAsFile;
-      }),
-      ([](const nsresult rv) {
-        return rv == NS_ERROR_FILE_NOT_FOUND ||
-               rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST ||
-               // We treat NS_ERROR_FILE_FS_CORRUPTED as if the file did not
-               // exist at all.
-               rv == NS_ERROR_FILE_FS_CORRUPTED;
-      }),
-      ErrToOk<nsIFileKind::DoesNotExist>));
-}
-
-Result<nsCOMPtr<mozIStorageStatement>, nsresult> CreateStatement(
-    mozIStorageConnection& aConnection, const nsACString& aStatementString) {
-  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageStatement>,
-                                           aConnection, CreateStatement,
-                                           aStatementString));
-}
-
-template <SingleStepResult ResultHandling>
-Result<SingleStepSuccessType<ResultHandling>, nsresult> ExecuteSingleStep(
-    nsCOMPtr<mozIStorageStatement>&& aStatement) {
-  QM_TRY_INSPECT(const bool& hasResult,
-                 MOZ_TO_RESULT_INVOKE(aStatement, ExecuteStep));
-
-  if constexpr (ResultHandling == SingleStepResult::AssertHasResult) {
-    MOZ_ASSERT(hasResult);
-    (void)hasResult;
-
-    return WrapNotNullUnchecked(std::move(aStatement));
-  } else {
-    return hasResult ? std::move(aStatement) : nullptr;
-  }
-}
-
-template Result<SingleStepSuccessType<SingleStepResult::AssertHasResult>,
-                nsresult>
-ExecuteSingleStep<SingleStepResult::AssertHasResult>(
-    nsCOMPtr<mozIStorageStatement>&&);
-
-template Result<SingleStepSuccessType<SingleStepResult::ReturnNullIfNoResult>,
-                nsresult>
-ExecuteSingleStep<SingleStepResult::ReturnNullIfNoResult>(
-    nsCOMPtr<mozIStorageStatement>&&);
-
-template <SingleStepResult ResultHandling>
-Result<SingleStepSuccessType<ResultHandling>, nsresult>
-CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection,
-                                    const nsACString& aStatementString) {
-  QM_TRY_UNWRAP(auto stmt, MOZ_TO_RESULT_INVOKE_TYPED(
-                               nsCOMPtr<mozIStorageStatement>, aConnection,
-                               CreateStatement, aStatementString));
-
-  return ExecuteSingleStep<ResultHandling>(std::move(stmt));
-}
-
-template Result<SingleStepSuccessType<SingleStepResult::AssertHasResult>,
-                nsresult>
-CreateAndExecuteSingleStepStatement<SingleStepResult::AssertHasResult>(
-    mozIStorageConnection& aConnection, const nsACString& aStatementString);
-
-template Result<SingleStepSuccessType<SingleStepResult::ReturnNullIfNoResult>,
-                nsresult>
-CreateAndExecuteSingleStepStatement<SingleStepResult::ReturnNullIfNoResult>(
-    mozIStorageConnection& aConnection, const nsACString& aStatementString);
-
 #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
 MOZ_THREAD_LOCAL(const nsACString*) ScopedLogExtraInfo::sQueryValue;
 MOZ_THREAD_LOCAL(const nsACString*) ScopedLogExtraInfo::sContextValue;
 
 /* static */
 auto ScopedLogExtraInfo::FindSlot(const char* aTag) {
   // XXX For now, don't use a real map but just allow the known tag values.
 
@@ -305,370 +72,9 @@ void ScopedLogExtraInfo::AddInfo() {
   auto* slot = FindSlot(mTag);
   MOZ_ASSERT(slot);
   mPreviousValue = slot->get();
 
   slot->set(&mCurrentValue);
 }
 #endif
 
-namespace detail {
-
-// Given aPath of /foo/bar/baz and aRelativePath of /bar/baz, returns the
-// absolute portion of aPath /foo by removing the common suffix from aPath.
-nsDependentCSubstring GetTreeBase(const nsLiteralCString& aPath,
-                                  const nsLiteralCString& aRelativePath) {
-  MOZ_ASSERT(StringEndsWith(aPath, aRelativePath));
-  return Substring(aPath, 0, aPath.Length() - aRelativePath.Length());
-}
-
-nsDependentCSubstring GetSourceTreeBase() {
-  static constexpr auto thisSourceFileRelativePath =
-      "/dom/quota/QuotaCommon.cpp"_ns;
-
-  return GetTreeBase(nsLiteralCString(__FILE__), thisSourceFileRelativePath);
-}
-
-nsDependentCSubstring GetObjdirDistIncludeTreeBase(
-    const nsLiteralCString& aQuotaCommonHPath) {
-  static constexpr auto quotaCommonHSourceFileRelativePath =
-      "/mozilla/dom/quota/QuotaCommon.h"_ns;
-
-  return GetTreeBase(aQuotaCommonHPath, quotaCommonHSourceFileRelativePath);
-}
-
-static constexpr auto kSourceFileRelativePathMap =
-    std::array<std::pair<nsLiteralCString, nsLiteralCString>, 1>{
-        {{"mozilla/dom/LocalStorageCommon.h"_ns,
-          "dom/localstorage/LocalStorageCommon.h"_ns}}};
-
-nsDependentCSubstring MakeSourceFileRelativePath(
-    const nsACString& aSourceFilePath) {
-  static constexpr auto error = "ERROR"_ns;
-  static constexpr auto mozillaRelativeBase = "mozilla/"_ns;
-
-  static const auto sourceTreeBase = GetSourceTreeBase();
-
-  if (MOZ_LIKELY(StringBeginsWith(aSourceFilePath, sourceTreeBase))) {
-    return Substring(aSourceFilePath, sourceTreeBase.Length() + 1);
-  }
-
-  // The source file could have been exported to the OBJDIR/dist/include
-  // directory, so we need to check that case as well.
-  static const auto objdirDistIncludeTreeBase = GetObjdirDistIncludeTreeBase();
-
-  if (MOZ_LIKELY(
-          StringBeginsWith(aSourceFilePath, objdirDistIncludeTreeBase))) {
-    const auto sourceFileRelativePath =
-        Substring(aSourceFilePath, objdirDistIncludeTreeBase.Length() + 1);
-
-    // Exported source files don't have to use the same directory structure as
-    // original source files. Check if we have a mapping for the exported
-    // source file.
-    const auto foundIt = std::find_if(
-        kSourceFileRelativePathMap.cbegin(), kSourceFileRelativePathMap.cend(),
-        [&sourceFileRelativePath](const auto& entry) {
-          return entry.first == sourceFileRelativePath;
-        });
-
-    if (MOZ_UNLIKELY(foundIt != kSourceFileRelativePathMap.cend())) {
-      return Substring(foundIt->second, 0);
-    }
-
-    // If we don't have a mapping for it, just remove the mozilla/ prefix
-    // (if there's any).
-    if (MOZ_LIKELY(
-            StringBeginsWith(sourceFileRelativePath, mozillaRelativeBase))) {
-      return Substring(sourceFileRelativePath, mozillaRelativeBase.Length());
-    }
-
-    // At this point, we don't know how to transform the relative path of the
-    // exported source file back to the relative path of the original source
-    // file. This can happen when QM_TRY is used in an exported nsIFoo.h file.
-    // If you really need to use QM_TRY there, consider adding a new mapping
-    // for the exported source file.
-    return sourceFileRelativePath;
-  }
-
-  nsCString::const_iterator begin, end;
-  if (RFindInReadable("/"_ns, aSourceFilePath.BeginReading(begin),
-                      aSourceFilePath.EndReading(end))) {
-    // Use the basename as a fallback, to avoid exposing any user parts of the
-    // path.
-    ++begin;
-    return Substring(begin, aSourceFilePath.EndReading(end));
-  }
-
-  return nsDependentCSubstring{static_cast<mozilla::Span<const char>>(
-      static_cast<const nsCString&>(error))};
-}
-
-}  // namespace detail
-
-#ifdef QM_LOG_ERROR_ENABLED
-#  ifdef QM_ERROR_STACKS_ENABLED
-void LogError(const nsACString& aExpr, const ResultType& aResult,
-              const nsACString& aSourceFilePath, const int32_t aSourceFileLine,
-              const Severity aSeverity)
-#  else
-void LogError(const nsACString& aExpr, const Maybe<nsresult> aMaybeRv,
-              const nsACString& aSourceFilePath, const int32_t aSourceFileLine,
-              const Severity aSeverity)
-#  endif
-{
-  // TODO: Add MOZ_LOG support, bug 1711661.
-
-  // We have to ignore failures with the Verbose severity until we have support
-  // for MOZ_LOG.
-  if (aSeverity == Severity::Verbose) {
-    return;
-  }
-
-  nsAutoCString context;
-
-#  ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
-  const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
-
-  if (const auto contextIt = extraInfoMap.find(ScopedLogExtraInfo::kTagContext);
-      contextIt != extraInfoMap.cend()) {
-    context = *contextIt->second;
-  }
-#  endif
-
-  const auto severityString = [&aSeverity]() -> nsLiteralCString {
-    switch (aSeverity) {
-      case Severity::Error:
-        return "ERROR"_ns;
-      case Severity::Warning:
-        return "WARNING"_ns;
-      case Severity::Note:
-        return "NOTE"_ns;
-      case Severity::Verbose:
-        return "VERBOSE"_ns;
-    }
-    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad severity value!");
-  }();
-
-  Maybe<nsresult> maybeRv;
-
-#  ifdef QM_ERROR_STACKS_ENABLED
-  if (aResult.is<QMResult>()) {
-    maybeRv = Some(aResult.as<QMResult>().NSResult());
-  } else if (aResult.is<nsresult>()) {
-    maybeRv = Some(aResult.as<nsresult>());
-  }
-#  else
-  maybeRv = aMaybeRv;
-#  endif
-
-  nsAutoCString rvCode;
-  nsAutoCString rvName;
-
-  if (maybeRv) {
-    nsresult rv = *maybeRv;
-
-    rvCode = nsPrintfCString("0x%" PRIX32, static_cast<uint32_t>(rv));
-
-    // XXX NS_ERROR_MODULE_WIN32 should be handled in GetErrorName directly.
-    if (NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_WIN32) {
-      // XXX We could also try to get the Win32 error name here.
-      rvName = nsPrintfCString(
-          "NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_WIN32, 0x%" PRIX16 ")",
-          NS_ERROR_GET_CODE(rv));
-    } else {
-      mozilla::GetErrorName(rv, rvName);
-    }
-  }
-
-#  ifdef QM_ERROR_STACKS_ENABLED
-  nsAutoCString frameIdString;
-  nsAutoCString stackIdString;
-  nsAutoCString processIdString;
-
-  if (aResult.is<QMResult>()) {
-    const QMResult& result = aResult.as<QMResult>();
-    frameIdString = IntToCString(result.FrameId());
-    stackIdString = IntToCString(result.StackId());
-    processIdString =
-        IntToCString(static_cast<uint32_t>(base::GetCurrentProcId()));
-  }
-#  endif
-
-  nsAutoCString extraInfosString;
-
-  if (!rvCode.IsEmpty()) {
-    extraInfosString.Append(" failed with resultCode "_ns + rvCode);
-  }
-
-  if (!rvName.IsEmpty()) {
-    extraInfosString.Append(", resultName "_ns + rvName);
-  }
-
-#  ifdef QM_ERROR_STACKS_ENABLED
-  if (!frameIdString.IsEmpty()) {
-    extraInfosString.Append(", frameId "_ns + frameIdString);
-  }
-
-  if (!stackIdString.IsEmpty()) {
-    extraInfosString.Append(", stackId "_ns + stackIdString);
-  }
-
-  if (!processIdString.IsEmpty()) {
-    extraInfosString.Append(", processId "_ns + processIdString);
-  }
-#  endif
-
-#  ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
-  for (const auto& item : extraInfoMap) {
-    extraInfosString.Append(", "_ns + nsDependentCString(item.first) + " "_ns +
-                            *item.second);
-  }
-#  endif
-
-  const auto sourceFileRelativePath =
-      detail::MakeSourceFileRelativePath(aSourceFilePath);
-
-#  ifdef QM_LOG_ERROR_TO_CONSOLE_ENABLED
-  NS_DebugBreak(
-      NS_DEBUG_WARNING,
-      nsAutoCString("QM_TRY failure ("_ns + severityString + ")"_ns).get(),
-      (extraInfosString.IsEmpty() ? nsPromiseFlatCString(aExpr)
-                                  : static_cast<const nsCString&>(nsAutoCString(
-                                        aExpr + extraInfosString)))
-          .get(),
-      nsPromiseFlatCString(sourceFileRelativePath).get(), aSourceFileLine);
-#  endif
-
-#  ifdef QM_LOG_ERROR_TO_BROWSER_CONSOLE_ENABLED
-  // XXX We might want to allow reporting to the browsing console even when
-  // there's no context in future once we are sure that it can't spam the
-  // browser console or when we have special about:quotamanager for the
-  // reporting (instead of the browsing console).
-  // Another option is to keep the current check and rely on MOZ_LOG reporting
-  // in future once that's available.
-  if (!context.IsEmpty()) {
-    nsCOMPtr<nsIConsoleService> console =
-        do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-    if (console) {
-      NS_ConvertUTF8toUTF16 message(
-          "QM_TRY failure ("_ns + severityString + ")"_ns + ": '"_ns + aExpr +
-          extraInfosString + "', file "_ns + sourceFileRelativePath + ":"_ns +
-          IntToCString(aSourceFileLine));
-
-      // The concatenation above results in a message like:
-      // QM_TRY failure (ERROR): 'MaybeRemoveLocalStorageArchiveTmpFile() failed
-      // with resultCode 0x80004005, resultName NS_ERROR_FAILURE, frameId 1,
-      // stackId 1, processId 53978, context Initialization::Storage', file
-      // dom/quota/ActorsParent.cpp:6029
-
-      console->LogStringMessage(message.get());
-    }
-  }
-#  endif
-
-#  ifdef QM_LOG_ERROR_TO_TELEMETRY_ENABLED
-  if (!context.IsEmpty()) {
-    // For now, we don't include aExpr in the telemetry event. It might help to
-    // match locations across versions, but they might be large.
-    auto extra = Some([&] {
-      auto res = CopyableTArray<EventExtraEntry>{};
-      res.SetCapacity(9);
-
-      res.AppendElement(EventExtraEntry{"context"_ns, nsCString{context}});
-
-#    ifdef QM_ERROR_STACKS_ENABLED
-      if (!frameIdString.IsEmpty()) {
-        res.AppendElement(
-            EventExtraEntry{"frame_id"_ns, nsCString{frameIdString}});
-      }
-
-      if (!processIdString.IsEmpty()) {
-        res.AppendElement(
-            EventExtraEntry{"process_id"_ns, nsCString{processIdString}});
-      }
-#    endif
-
-      if (!rvName.IsEmpty()) {
-        res.AppendElement(EventExtraEntry{"result"_ns, nsCString{rvName}});
-      }
-
-      // The sequence number is currently per-process, and we don't record the
-      // thread id. This is ok as long as we only record during storage
-      // initialization, and that happens (mostly) from a single thread. It's
-      // safe even if errors from multiple threads are interleaved, but the data
-      // will be hard to analyze then. In that case, we should record a pair of
-      // thread id and thread-local sequence number.
-      static Atomic<int32_t> sSequenceNumber{0};
-
-      res.AppendElement(
-          EventExtraEntry{"seq"_ns, IntToCString(++sSequenceNumber)});
-
-      res.AppendElement(EventExtraEntry{"severity"_ns, severityString});
-
-      res.AppendElement(
-          EventExtraEntry{"source_file"_ns, nsCString(sourceFileRelativePath)});
-
-      res.AppendElement(
-          EventExtraEntry{"source_line"_ns, IntToCString(aSourceFileLine)});
-
-#    ifdef QM_ERROR_STACKS_ENABLED
-      if (!stackIdString.IsEmpty()) {
-        res.AppendElement(
-            EventExtraEntry{"stack_id"_ns, nsCString{stackIdString}});
-      }
-#    endif
-
-      return res;
-    }());
-
-    Telemetry::RecordEvent(Telemetry::EventID::DomQuotaTry_Error_Step,
-                           Nothing(), extra);
-  }
-#  endif
-}
-#endif
-
-#ifdef DEBUG
-Result<bool, nsresult> WarnIfFileIsUnknown(nsIFile& aFile,
-                                           const char* aSourceFilePath,
-                                           const int32_t aSourceFileLine) {
-  nsString leafName;
-  nsresult rv = aFile.GetLeafName(leafName);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return Err(rv);
-  }
-
-  bool isDirectory;
-  rv = aFile.IsDirectory(&isDirectory);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return Err(rv);
-  }
-
-  if (!isDirectory) {
-    // Don't warn about OS metadata files. These files are only used in
-    // different platforms, but the profile can be shared across different
-    // operating systems, so we check it on all platforms.
-    if (leafName.Equals(kDSStoreFileName) ||
-        leafName.Equals(kDesktopFileName) ||
-        leafName.Equals(kDesktopIniFileName,
-                        nsCaseInsensitiveStringComparator) ||
-        leafName.Equals(kThumbsDbFileName, nsCaseInsensitiveStringComparator)) {
-      return false;
-    }
-
-    // Don't warn about files starting with ".".
-    if (leafName.First() == char16_t('.')) {
-      return false;
-    }
-  }
-
-  NS_DebugBreak(
-      NS_DEBUG_WARNING,
-      nsPrintfCString("Something (%s) in the directory that doesn't belong!",
-                      NS_ConvertUTF16toUTF8(leafName).get())
-          .get(),
-      nullptr, aSourceFilePath, aSourceFileLine);
-
-  return true;
-}
-#endif
-
 }  // namespace mozilla::dom::quota
copy from dom/quota/QuotaCommon.h
copy to dom/quota/ScopedLogExtraInfo.h
--- a/dom/quota/QuotaCommon.h
+++ b/dom/quota/ScopedLogExtraInfo.h
@@ -1,1351 +1,28 @@
 /* -*- 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/. */
 
-#ifndef mozilla_dom_quota_quotacommon_h__
-#define mozilla_dom_quota_quotacommon_h__
+#ifndef DOM_QUOTA_SCOPEDLOGEXTRAINFO_H_
+#define DOM_QUOTA_SCOPEDLOGEXTRAINFO_H_
 
 #include "mozilla/dom/quota/Config.h"
 
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
 #include <map>
-#include <type_traits>
-#include <utility>
-#include "mozIStorageStatement.h"
 #include "mozilla/Assertions.h"
-#include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/Likely.h"
-#include "mozilla/MacroArgs.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/Result.h"
-#include "mozilla/ResultExtensions.h"
 #include "mozilla/ThreadLocal.h"
-#if defined(QM_LOG_ERROR_ENABLED) && defined(QM_ERROR_STACKS_ENABLED)
-#  include "mozilla/Variant.h"
-#endif
-#include "mozilla/dom/QMResult.h"
-#include "mozilla/ipc/ProtocolUtils.h"
-#include "nsCOMPtr.h"
-#include "nsDebug.h"
-#include "nsError.h"
-#include "nsIDirectoryEnumerator.h"
-#include "nsIEventTarget.h"
-#include "nsIFile.h"
-#include "nsLiteralString.h"
-#include "nsPrintfCString.h"
-#include "nsReadableUtils.h"
 #include "nsString.h"
-#include "nsTArray.h"
-#include "nsTLiteralString.h"
 #include "nsXULAppAPI.h"
 
-namespace mozilla {
-template <typename T>
-class NotNull;
-}
-
-// Proper use of unique variable names can be tricky (especially if nesting of
-// the final macro is required).
-// See https://lifecs.likai.org/2016/07/c-preprocessor-hygienic-macros.html
-#define MOZ_UNIQUE_VAR(base) MOZ_CONCAT(base, __COUNTER__)
-
-// See
-// https://stackoverflow.com/questions/24481810/how-to-remove-the-enclosing-parentheses-with-macro
-#define MOZ_REMOVE_PAREN(X) MOZ_REMOVE_PAREN_HELPER2(MOZ_REMOVE_PAREN_HELPER X)
-#define MOZ_REMOVE_PAREN_HELPER(...) MOZ_REMOVE_PAREN_HELPER __VA_ARGS__
-#define MOZ_REMOVE_PAREN_HELPER2(...) MOZ_REMOVE_PAREN_HELPER3(__VA_ARGS__)
-#define MOZ_REMOVE_PAREN_HELPER3(...) MOZ_REMOVE_PAREN_HELPER4_##__VA_ARGS__
-#define MOZ_REMOVE_PAREN_HELPER4_MOZ_REMOVE_PAREN_HELPER
-
-// See https://florianjw.de/en/passing_overloaded_functions.html
-// TODO: Add a test for this macro.
-#define MOZ_SELECT_OVERLOAD(func)                         \
-  [](auto&&... aArgs) -> decltype(auto) {                 \
-    return func(std::forward<decltype(aArgs)>(aArgs)...); \
-  }
-
-#define DSSTORE_FILE_NAME ".DS_Store"
-#define DESKTOP_FILE_NAME ".desktop"
-#define DESKTOP_INI_FILE_NAME "desktop.ini"
-#define THUMBS_DB_FILE_NAME "thumbs.db"
-
-#define QM_WARNING(...)                                                      \
-  do {                                                                       \
-    nsPrintfCString str(__VA_ARGS__);                                        \
-    mozilla::dom::quota::ReportInternalError(__FILE__, __LINE__, str.get()); \
-    NS_WARNING(str.get());                                                   \
-  } while (0)
-
-#define QM_LOG_TEST() MOZ_LOG_TEST(GetQuotaManagerLogger(), LogLevel::Info)
-#define QM_LOG(_args) MOZ_LOG(GetQuotaManagerLogger(), LogLevel::Info, _args)
-
-#define UNKNOWN_FILE_WARNING(_leafName)                                       \
-  NS_WARNING(                                                                 \
-      nsPrintfCString("Something (%s) in the directory that doesn't belong!", \
-                      NS_ConvertUTF16toUTF8(_leafName).get())                 \
-          .get())
-
-// This macro should be used in directory traversals for files or directories
-// that are unknown for given directory traversal. It should only be called
-// after all known (directory traversal specific) files or directories have
-// been checked and handled.
-#ifdef DEBUG
-#  define WARN_IF_FILE_IS_UNKNOWN(_file) \
-    mozilla::dom::quota::WarnIfFileIsUnknown(_file, __FILE__, __LINE__)
-#else
-#  define WARN_IF_FILE_IS_UNKNOWN(_file) Result<bool, nsresult>(false)
-#endif
-
-/**
- * There are multiple ways to handle unrecoverable conditions (note that the
- * patterns are put in reverse chronological order and only the first pattern
- * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL should be used in
- * new code):
- *
- * 1. Using QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL macros
- *    (Quota manager specific, defined below)
- *
- * Typical use cases:
- *
- * nsresult MyFunc1(nsIFile& aFile) {
- *   bool exists;
- *   QM_TRY(aFile.Exists(&exists));
- *   QM_TRY(OkIf(exists), NS_ERROR_FAILURE);
- *
- *   // The file exists, and data could be read from it here.
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc2(nsIFile& aFile) {
- *   bool exists;
- *   QM_TRY(aFile.Exists(&exists), NS_ERROR_UNEXPECTED);
- *   QM_TRY(OkIf(exists), NS_ERROR_UNEXPECTED);
- *
- *   // The file exists, and data could be read from it here.
- *
- *   return NS_OK;
- * }
- *
- * void MyFunc3(nsIFile& aFile) {
- *   bool exists;
- *   QM_TRY(aFile.Exists(&exists), QM_VOID);
- *   QM_TRY(OkIf(exists), QM_VOID);
- *
- *   // The file exists, and data could be read from it here.
- * }
- *
- * nsresult MyFunc4(nsIFile& aFile) {
- *   bool exists;
- *   QM_TRY(storageFile->Exists(&exists), QM_PROPAGATE,
- *          []() { NS_WARNING("The Exists call failed!"); });
- *   QM_TRY(OkIf(exists), NS_ERROR_FAILURE,
- *          []() { NS_WARNING("The file doesn't exist!"); });
- *
- *   // The file exists, and data could be read from it here.
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc5(nsIFile& aFile) {
- *   bool exists;
- *   QM_TRY(aFile.Exists(&exists));
- *   if (exists) {
- *     // The file exists, and data could be read from it here.
- *   } else {
- *     QM_FAIL(NS_ERROR_FAILURE);
- *   }
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc6(nsIFile& aFile) {
- *   bool exists;
- *   QM_TRY(aFile.Exists(&exists));
- *   if (exists) {
- *     // The file exists, and data could be read from it here.
- *   } else {
- *     QM_FAIL(NS_ERROR_FAILURE,
- *             []() { NS_WARNING("The file doesn't exist!"); });
- *   }
- *
- *   return NS_OK;
- * }
- *
- * 2. Using MOZ_TRY/MOZ_TRY_VAR macros
- *
- * Typical use cases:
- *
- * nsresult MyFunc1(nsIFile& aFile) {
- *   // MOZ_TRY can't return a custom return value
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc2(nsIFile& aFile) {
- *   // MOZ_TRY can't return a custom return value
- *
- *   return NS_OK;
- * }
- *
- * void MyFunc3(nsIFile& aFile) {
- *   // MOZ_TRY can't return a custom return value, "void" in this case
- * }
- *
- * nsresult MyFunc4(nsIFile& aFile) {
- *   // MOZ_TRY can't return a custom return value and run an additional
- *   // cleanup function
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc5(nsIFile& aFile) {
- *   // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc6(nsIFile& aFile) {
- *   // There's no MOZ_FAIL, MOZ_TRY can't return a custom return value and run
- *   // an additional cleanup function
- *
- *   return NS_OK;
- * }
- *
- * 3. Using NS_WARN_IF and NS_WARNING macro with own control flow handling
- *
- * Typical use cases:
- *
- * nsresult MyFunc1(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   if (NS_WARN_IF(NS_FAILED(rv)) {
- *     return rv;
- *   }
- *   if (NS_WARN_IF(!exists) {
- *     return NS_ERROR_FAILURE;
- *   }
- *
- *   // The file exists, and data could be read from it here.
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc2(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   if (NS_WARN_IF(NS_FAILED(rv)) {
- *     return NS_ERROR_UNEXPECTED;
- *   }
- *   if (NS_WARN_IF(!exists) {
- *     return NS_ERROR_UNEXPECTED;
- *   }
- *
- *   // The file exists, and data could be read from it here.
- *
- *   return NS_OK;
- * }
- *
- * void MyFunc3(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   if (NS_WARN_IF(NS_FAILED(rv)) {
- *     return;
- *   }
- *   if (NS_WARN_IF(!exists) {
- *     return;
- *   }
- *
- *   // The file exists, and data could be read from it here.
- * }
- *
- * nsresult MyFunc4(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   if (NS_WARN_IF(NS_FAILED(rv)) {
- *     NS_WARNING("The Exists call failed!");
- *     return rv;
- *   }
- *   if (NS_WARN_IF(!exists) {
- *     NS_WARNING("The file doesn't exist!");
- *     return NS_ERROR_FAILURE;
- *   }
- *
- *   // The file exists, and data could be read from it here.
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc5(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   if (NS_WARN_IF(NS_FAILED(rv)) {
- *     return rv;
- *   }
- *   if (exists) {
- *     // The file exists, and data could be read from it here.
- *   } else {
- *     return NS_ERROR_FAILURE;
- *   }
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc6(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   if (NS_WARN_IF(NS_FAILED(rv)) {
- *     return rv;
- *   }
- *   if (exists) {
- *     // The file exists, and data could be read from it here.
- *   } else {
- *     NS_WARNING("The file doesn't exist!");
- *     return NS_ERROR_FAILURE;
- *   }
- *
- *   return NS_OK;
- * }
- *
- * 4. Using NS_ENSURE_* macros
- *
- * Mainly:
- * - NS_ENSURE_SUCCESS
- * - NS_ENSURE_SUCCESS_VOID
- * - NS_ENSURE_TRUE
- * - NS_ENSURE_TRUE_VOID
- *
- * Typical use cases:
- *
- * nsresult MyFunc1(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   NS_ENSURE_SUCCESS(rv, rv);
- *   NS_ENSURE_TRUE(exists, NS_ERROR_FAILURE);
- *
- *   // The file exists, and data could be read from it here.
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc2(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
- *   NS_ENSURE_TRUE(exists, NS_ERROR_UNEXPECTED);
- *
- *   // The file exists, and data could be read from it here.
- *
- *   return NS_OK;
- * }
- *
- * void MyFunc3(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   NS_ENSURE_SUCCESS_VOID(rv);
- *   NS_ENSURE_TRUE_VOID(exists);
- *
- *   // The file exists, and data could be read from it here.
- * }
- *
- * nsresult MyFunc4(nsIFile& aFile) {
- *   // NS_ENSURE_SUCCESS/NS_ENSURE_TRUE can't run an additional cleanup
- *   // function
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc5(nsIFile& aFile) {
- *   bool exists;
- *   nsresult rv = aFile.Exists(&exists);
- *   NS_ENSURE_SUCCESS(rv, rv);
- *   if (exists) {
- *     // The file exists, and data could be read from it here.
- *   } else {
- *     NS_ENSURE_TRUE(false, NS_ERROR_FAILURE);
- *   }
- *
- *   return NS_OK;
- * }
- *
- * nsresult MyFunc6(nsIFile& aFile) {
- *   // NS_ENSURE_TRUE can't run an additional cleanup function
- *
- *   return NS_OK;
- * }
- *
- * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT is like MOZ_TRY/MOZ_TRY_VAR but if an
- * error occurs it additionally calls a generic function HandleError to handle
- * the error and it can be used to return custom return values as well and even
- * call an additional cleanup function.
- * HandleError currently only warns in debug builds, it will report to the
- * browser console and telemetry in the future.
- * The other advantage of QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT is that a local
- * nsresult is not needed at all in all cases, all calls can be wrapped
- * directly. If an error occurs, the warning contains a concrete call instead
- * of the rv local variable. For example:
- *
- * 1. WARNING: NS_ENSURE_SUCCESS(rv, rv) failed with result 0x80004005
- * (NS_ERROR_FAILURE): file XYZ, line N
- *
- * 2. WARNING: 'NS_FAILED(rv)', file XYZ, line N
- *
- * 3. Nothing (MOZ_TRY doesn't warn)
- *
- * 4. WARNING: Error: 'aFile.Exists(&exists)', file XYZ, line N
- *
- * QM_TRY_RETURN is a supplementary macro for cases when the result's success
- * value can be directly returned (instead of assigning to a variable as in the
- * QM_TRY_UNWRAP/QM_TRY_INSPECT case).
- *
- * QM_FAIL is a supplementary macro for cases when an error needs to be
- * returned without evaluating an expression. It's possible to write
- * QM_TRY(OkIf(false), NS_ERROR_FAILURE), but QM_FAIL(NS_ERROR_FAILURE) looks
- * more straightforward.
- *
- * It's highly recommended to use
- * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_TRY_RETURN/QM_FAIL in new code for
- * quota manager and quota clients. Existing code should be incrementally
- * converted as needed.
- *
- * QM_TRY_VOID/QM_TRY_UNWRAP_VOID/QM_TRY_INSPECT_VOID/QM_FAIL_VOID is not
- * defined on purpose since it's possible to use
- * QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_FAIL even in void functions.
- * However, QM_TRY(Task(), ) would look odd so it's recommended to use a dummy
- * define QM_VOID that evaluates to nothing instead: QM_TRY(Task(), QM_VOID)
- */
-
-#define QM_VOID
-
-#define QM_PROPAGATE Err(tryTempError)
-
-#ifdef DEBUG
-#  define QM_ASSERT_UNREACHABLE                       \
-    []() -> ::mozilla::GenericErrorResult<nsresult> { \
-      MOZ_CRASH("Should never be reached.");          \
-    }()
-
-#  define QM_ASSERT_UNREACHABLE_VOID \
-    [] { MOZ_CRASH("Should never be reached."); }()
-#endif
-
-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-#  define QM_DIAGNOSTIC_ASSERT_UNREACHABLE            \
-    []() -> ::mozilla::GenericErrorResult<nsresult> { \
-      MOZ_CRASH("Should never be reached.");          \
-    }()
-
-#  define QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID \
-    [] { MOZ_CRASH("Should never be reached."); }()
-#endif
-
-// QM_MISSING_ARGS and QM_HANDLE_ERROR macros are implementation details of
-// QM_TRY/QM_TRY_UNWRAP/QM_TRY_INSPECT/QM_FAIL and shouldn't be used directly.
-
-#define QM_MISSING_ARGS(...)                           \
-  do {                                                 \
-    static_assert(false, "Did you forget arguments?"); \
-  } while (0)
-
-#ifdef DEBUG
-#  define QM_HANDLE_ERROR(expr, error, severity) \
-    HandleError(#expr, error, __FILE__, __LINE__, severity)
-#else
-#  define QM_HANDLE_ERROR(expr, error, severity) \
-    HandleError("Unavailable", error, __FILE__, __LINE__, severity)
-#endif
-
-#ifdef DEBUG
-#  define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
-    HandleErrorReturnNothing(#expr, error, __FILE__, __LINE__, severity)
-#else
-#  define QM_HANDLE_ERROR_RETURN_NOTHING(expr, error, severity) \
-    HandleErrorReturnNothing("Unavailable", error, __FILE__, __LINE__, severity)
-#endif
-
-#ifdef DEBUG
-#  define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
-                                                      cleanup)               \
-    HandleErrorWithCleanupReturnNothing(#expr, error, __FILE__, __LINE__,    \
-                                        severity, cleanup)
-#else
-#  define QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING(expr, error, severity, \
-                                                      cleanup)               \
-    HandleErrorWithCleanupReturnNothing("Unavailable", error, __FILE__,      \
-                                        __LINE__, severity, cleanup)
-#endif
-
-// QM_TRY_PROPAGATE_ERR, QM_TRY_CUSTOM_RET_VAL,
-// QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_GLUE macros are implementation
-// details of QM_TRY and shouldn't be used directly.
-
-// Handles the two arguments case when the error is propagated.
-#define QM_TRY_PROPAGATE_ERR(tryResult, expr)                                \
-  auto tryResult = ::mozilla::ToResult(expr);                                \
-  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);     \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                     \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                    \
-        expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
-    return tryResult.propagateErr();                                         \
-  }
-
-// Handles the three arguments case when a custom return value needs to be
-// returned
-#define QM_TRY_CUSTOM_RET_VAL(tryResult, expr, customRetVal)             \
-  auto tryResult = ::mozilla::ToResult(expr);                            \
-  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>); \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                 \
-    auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr();          \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                \
-        expr, tryTempError, mozilla::dom::quota::Severity::Error);       \
-    return customRetVal;                                                 \
-  }
-
-// Handles the four arguments case when a cleanup function needs to be called
-// before a custom return value is returned
-#define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, customRetVal, \
-                                           cleanup)                       \
-  auto tryResult = ::mozilla::ToResult(expr);                             \
-  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);  \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                  \
-    auto tryTempError = tryResult.unwrapErr();                            \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                 \
-        expr, tryTempError, mozilla::dom::quota::Severity::Error);        \
-    cleanup(tryTempError);                                                \
-    return customRetVal;                                                  \
-  }
-
-// Chooses the final implementation macro for given argument count.
-// This could use MOZ_PASTE_PREFIX_AND_ARG_COUNT, but explicit named suffxes
-// read slightly better than plain numbers.
-// See also
-// https://stackoverflow.com/questions/3046889/optional-parameters-with-c-macros
-#define QM_TRY_META(...)                                                       \
-  {                                                                            \
-    MOZ_ARG_6(                                                                 \
-        , ##__VA_ARGS__, QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__),      \
-        QM_TRY_CUSTOM_RET_VAL(__VA_ARGS__), QM_TRY_PROPAGATE_ERR(__VA_ARGS__), \
-        QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))            \
-  }
-
-// Generates unique variable name. This extra internal macro (along with
-// __COUNTER__) allows nesting of the final macro.
-#define QM_TRY_GLUE(...) QM_TRY_META(MOZ_UNIQUE_VAR(tryResult), ##__VA_ARGS__)
-
-/**
- * QM_TRY(expr[, customRetVal, cleanup]) is the C++ equivalent of Rust's
- * `try!(expr);`. First, it evaluates expr, which must produce a Result value
- * with empty ok_type. On Success, it does nothing else. On error, it calls
- * HandleError and an additional cleanup function (if the third argument was
- * passed) and finally returns an error Result from the enclosing function or a
- * custom return value (if the second argument was passed).
- */
-#define QM_TRY(...) QM_TRY_GLUE(__VA_ARGS__)
-
-// QM_TRY_ASSIGN_PROPAGATE_ERR, QM_TRY_ASSIGN_CUSTOM_RET_VAL,
-// QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_ASSIGN_GLUE macros are
-// implementation details of QM_TRY_UNWRAP/QM_TRY_INSPECT and shouldn't be used
-// directly.
-
-// Handles the four arguments case when the error is propagated.
-#define QM_TRY_ASSIGN_PROPAGATE_ERR(tryResult, accessFunction, target, expr) \
-  auto tryResult = (expr);                                                   \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                     \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                    \
-        expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
-    return tryResult.propagateErr();                                         \
-  }                                                                          \
-  MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
-
-// Handles the five arguments case when a custom return value needs to be
-// returned
-#define QM_TRY_ASSIGN_CUSTOM_RET_VAL(tryResult, accessFunction, target, expr, \
-                                     customRetVal)                            \
-  auto tryResult = (expr);                                                    \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                      \
-    auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr();               \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                     \
-        expr, tryTempError, mozilla::dom::quota::Severity::Error);            \
-    return customRetVal;                                                      \
-  }                                                                           \
-  MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
-
-// Handles the six arguments case when a cleanup function needs to be called
-// before a custom return value is returned
-#define QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP(                  \
-    tryResult, accessFunction, target, expr, customRetVal, cleanup) \
-  auto tryResult = (expr);                                          \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                            \
-    auto tryTempError = tryResult.unwrapErr();                      \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                           \
-        expr, tryTempError, mozilla::dom::quota::Severity::Error);  \
-    cleanup(tryTempError);                                          \
-    return customRetVal;                                            \
-  }                                                                 \
-  MOZ_REMOVE_PAREN(target) = tryResult.accessFunction();
-
-// Chooses the final implementation macro for given argument count.
-// See also the comment for QM_TRY_META.
-#define QM_TRY_ASSIGN_META(...)                                         \
-  MOZ_ARG_8(, ##__VA_ARGS__,                                            \
-            QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__),     \
-            QM_TRY_ASSIGN_CUSTOM_RET_VAL(__VA_ARGS__),                  \
-            QM_TRY_ASSIGN_PROPAGATE_ERR(__VA_ARGS__),                   \
-            QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
-            QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
-
-// Generates unique variable name. This extra internal macro (along with
-// __COUNTER__) allows nesting of the final macro.
-#define QM_TRY_ASSIGN_GLUE(accessFunction, ...) \
-  QM_TRY_ASSIGN_META(MOZ_UNIQUE_VAR(tryResult), accessFunction, ##__VA_ARGS__)
-
-/**
- * QM_TRY_UNWRAP(target, expr[, customRetVal, cleanup]) is the C++ equivalent of
- * Rust's `target = try!(expr);`. First, it evaluates expr, which must produce
- * a Result value. On success, the result's success value is unwrapped and
- * assigned to target. On error, it calls HandleError and an additional cleanup
- * function (if the fourth argument was passed) and finally returns the error
- * result or a custom return value (if the third argument was passed). |target|
- * must be an lvalue.
- */
-#define QM_TRY_UNWRAP(...) QM_TRY_ASSIGN_GLUE(unwrap, __VA_ARGS__)
-
-/**
- * QM_TRY_INSPECT is similar to QM_TRY_UNWRAP, but it does not unwrap a success
- * value, but inspects it and binds it to the target. It can therefore only be
- * used when the target declares a const&. In general,
- *
- *   QM_TRY_INSPECT(const auto &target, DoSomething())
- *
- * should be preferred over
- *
- *   QM_TRY_UNWRAP(const auto target, DoSomething())
- *
- * as it avoids unnecessary moves/copies.
- */
-#define QM_TRY_INSPECT(...) QM_TRY_ASSIGN_GLUE(inspect, __VA_ARGS__)
-
-// QM_TRY_RETURN_PROPAGATE_ERR, QM_TRY_RETURN_CUSTOM_RET_VAL,
-// QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_RETURN_GLUE macros are
-// implementation details of QM_TRY_RETURN and shouldn't be used directly.
-
-// Handles the two arguments case when the error is (also) propagated.
-// Note that this deliberately uses a single return statement without going
-// through unwrap/unwrapErr/propagateErr, so that this does not prevent NRVO or
-// tail call optimizations when possible.
-#define QM_TRY_RETURN_PROPAGATE_ERR(tryResult, expr)                         \
-  auto tryResult = ::mozilla::ToResult(expr);                                \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                     \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                    \
-        expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
-  }                                                                          \
-  return tryResult;
-
-// Handles the three arguments case when a custom return value needs to be
-// returned
-#define QM_TRY_RETURN_CUSTOM_RET_VAL(tryResult, expr, customRetVal)          \
-  auto tryResult = ::mozilla::ToResult(expr);                                \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                     \
-    auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr();              \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                    \
-        expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \
-    return customRetVal;                                                     \
-  }                                                                          \
-  return tryResult.unwrap();
-
-// Handles the four arguments case when a cleanup function needs to be called
-// before a custom return value is returned
-#define QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr,       \
-                                                  customRetVal, cleanup) \
-  auto tryResult = ::mozilla::ToResult(expr);                            \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                 \
-    auto tryTempError = tryResult.unwrapErr();                           \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                \
-        expr, tryTempError, mozilla::dom::quota::Severity::Error);       \
-    cleanup(tryTempError);                                               \
-    return customRetVal;                                                 \
-  }                                                                      \
-  return tryResult.unwrap();
-
-// Chooses the final implementation macro for given argument count.
-// See also the comment for QM_TRY_META.
-#define QM_TRY_RETURN_META(...)                                           \
-  {                                                                       \
-    MOZ_ARG_6(, ##__VA_ARGS__,                                            \
-              QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(__VA_ARGS__),     \
-              QM_TRY_RETURN_CUSTOM_RET_VAL(__VA_ARGS__),                  \
-              QM_TRY_RETURN_PROPAGATE_ERR(__VA_ARGS__),                   \
-              QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__)) \
-  }
-
-// Generates unique variable name. This extra internal macro (along with
-// __COUNTER__) allows nesting of the final macro.
-#define QM_TRY_RETURN_GLUE(...) \
-  QM_TRY_RETURN_META(MOZ_UNIQUE_VAR(tryResult), ##__VA_ARGS__)
-
-/**
- * QM_TRY_RETURN(expr[, customRetVal, cleanup]) evaluates expr, which must
- * produce a Result value. On success, the result's success value is returned.
- * On error, it calls HandleError and an additional cleanup function (if the
- * third argument was passed) and finally returns the error result or a custom
- * return value (if the second argument was passed).
- */
-#define QM_TRY_RETURN(...) QM_TRY_RETURN_GLUE(__VA_ARGS__)
-
-// QM_FAIL_RET_VAL and QM_FAIL_RET_VAL_WITH_CLEANUP macros are implementation
-// details of QM_FAIL and shouldn't be used directly.
-
-// Handles the one argument case when just an error is returned
-#define QM_FAIL_RET_VAL(retVal)                                               \
-  mozilla::dom::quota::QM_HANDLE_ERROR(Failure, 0,                            \
-                                       mozilla::dom::quota::Severity::Error); \
-  return retVal;
-
-// Handles the two arguments case when a cleanup function needs to be called
-// before a return value is returned
-#define QM_FAIL_RET_VAL_WITH_CLEANUP(retVal, cleanup)                         \
-  mozilla::dom::quota::QM_HANDLE_ERROR(Failure, 0,                            \
-                                       mozilla::dom::quota::Severity::Error); \
-  cleanup();                                                                  \
-  return retVal;
-
-// Chooses the final implementation macro for given argument count.
-// See also the comment for QM_TRY_META.
-#define QM_FAIL_META(...)                                               \
-  MOZ_ARG_4(, ##__VA_ARGS__, QM_FAIL_RET_VAL_WITH_CLEANUP(__VA_ARGS__), \
-            QM_FAIL_RET_VAL(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
-
-// This extra internal macro allows nesting of the final macro.
-#define QM_FAIL_GLUE(...) QM_FAIL_META(__VA_ARGS__)
-
-/**
- * QM_FAIL(retVal[, cleanup]) calls HandleError and an additional cleanup
- * function (if the second argument was passed) and returns a return value.
- */
-#define QM_FAIL(...) QM_FAIL_GLUE(__VA_ARGS__)
-
-// QM_REPORTONLY_TRY, QM_REPORTONLY_TRY_WITH_CLEANUP, QM_REPORTONLY_TRY_GLUE
-// macros are implementation details of QM_WARNONLY_TRY/QM_NOTEONLY_TRY and
-// shouldn't be used directly.
-
-// Handles the three arguments case when only a warning/note is reported.
-#define QM_REPORTONLY_TRY(tryResult, severity, expr)                           \
-  auto tryResult = ::mozilla::ToResult(expr);                                  \
-  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);       \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                       \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                      \
-        expr, tryResult.unwrapErr(), mozilla::dom::quota::Severity::severity); \
-  }
-
-// Handles the four arguments case when a cleanup function needs to be called
-#define QM_REPORTONLY_TRY_WITH_CLEANUP(tryResult, severity, expr, cleanup) \
-  auto tryResult = ::mozilla::ToResult(expr);                              \
-  static_assert(std::is_empty_v<typename decltype(tryResult)::ok_type>);   \
-  if (MOZ_UNLIKELY(tryResult.isErr())) {                                   \
-    auto tryTempError = tryResult.unwrapErr();                             \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                                  \
-        expr, tryTempError, mozilla::dom::quota::Severity::severity);      \
-    cleanup(tryTempError);                                                 \
-  }
-
-// Chooses the final implementation macro for given argument count.
-// See also the comment for QM_TRY_META.
-#define QM_REPORTONLY_TRY_META(...)                                         \
-  {                                                                         \
-    MOZ_ARG_6(, ##__VA_ARGS__, QM_REPORTONLY_TRY_WITH_CLEANUP(__VA_ARGS__), \
-              QM_REPORTONLY_TRY(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
-              QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))   \
-  }
-
-// Generates unique variable name. This extra internal macro (along with
-// __COUNTER__) allows nesting of the final macro.
-#define QM_REPORTONLY_TRY_GLUE(severity, ...) \
-  QM_REPORTONLY_TRY_META(MOZ_UNIQUE_VAR(tryResult), severity, ##__VA_ARGS__)
-
-/**
- * QM_WARNONLY_TRY(expr[, cleanup]) evaluates expr, which must produce a
- * Result value with empty ok_type. On Success, it does nothing else. On error,
- * it calls HandleError and an additional cleanup function (if the second
- * argument was passed). This macro never returns and failures are always
- * reported using a lower level of severity relative to failures reported by
- * QM_TRY. The macro is intended for failures that should not be propagated.
- */
-#define QM_WARNONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Warning, __VA_ARGS__)
-
-/**
- * QM_NOTEONLY_TRY is like QM_WARNONLY_TRY. The only difference is that
- * failures are reported using a lower level of severity relative to failures
- * reported by QM_WARNONLY_TRY.
- */
-#define QM_NOTEONLY_TRY(...) QM_REPORTONLY_TRY_GLUE(Note, __VA_ARGS__)
-
-// QM_REPORTONLY_TRY_ASSIGN, QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP,
-// QM_REPORTONLY_TRY_ASSIGN_GLUE macros are implementation details of
-// QM_WARNONLY_TRY_UNWRAP/QM_NOTEONLY_TRY_UNWRAP and shouldn't be used
-// directly.
-
-// Handles the four arguments case when only a warning/note is reported.
-#define QM_REPORTONLY_TRY_ASSIGN(tryResult, severity, target, expr) \
-  auto tryResult = (expr);                                          \
-  MOZ_REMOVE_PAREN(target) =                                        \
-      MOZ_LIKELY(tryResult.isOk())                                  \
-          ? Some(tryResult.unwrap())                                \
-          : mozilla::dom::quota::QM_HANDLE_ERROR_RETURN_NOTHING(    \
-                expr, tryResult.unwrapErr(),                        \
-                mozilla::dom::quota::Severity::severity);
-
-// Handles the five arguments case when a cleanup function needs to be called
-#define QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP(tryResult, severity, target,    \
-                                              expr, cleanup)                  \
-  auto tryResult = (expr);                                                    \
-  MOZ_REMOVE_PAREN(target) =                                                  \
-      MOZ_LIKELY(tryResult.isOk())                                            \
-          ? Some(tryResult.unwrap())                                          \
-          : mozilla::dom::quota::QM_HANDLE_ERROR_WITH_CLEANUP_RETURN_NOTHING( \
-                expr, tryResult.unwrapErr(),                                  \
-                mozilla::dom::quota::Severity::severity, cleanup);
-
-// Chooses the final implementation macro for given argument count.
-// See also the comment for QM_TRY_META.
-#define QM_REPORTONLY_TRY_ASSIGN_META(...)                              \
-  MOZ_ARG_7(, ##__VA_ARGS__,                                            \
-            QM_REPORTONLY_TRY_ASSIGN_WITH_CLEANUP(__VA_ARGS__),         \
-            QM_REPORTONLY_TRY_ASSIGN(__VA_ARGS__),                      \
-            QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__), \
-            QM_MISSING_ARGS(__VA_ARGS__), QM_MISSING_ARGS(__VA_ARGS__))
-
-// Generates unique variable name. This extra internal macro (along with
-// __COUNTER__) allows nesting of the final macro.
-#define QM_REPORTONLY_TRY_ASSIGN_GLUE(severity, ...)                 \
-  QM_REPORTONLY_TRY_ASSIGN_META(MOZ_UNIQUE_VAR(tryResult), severity, \
-                                ##__VA_ARGS__)
-
-/**
- * QM_WARNONLY_TRY_UNWRAP(target, expr[, cleanup]) evaluates expr, which must
- * produce a Result value. On success, the result's success value is first
- * unwrapped from the Result, then wrapped in a Maybe and finally assigned to
- * target. On error, it calls HandleError and an additional cleanup
- * function (if the third argument was passed) and finally assigns Nothing to
- * target. |target| must be an lvalue. This macro never returns and failures
- * are always reported using a lower level of severity relative to failures
- * reported by QM_TRY_UNWRAP/QM_TRY_INSPECT. The macro is intended for failures
- * that should not be propagated.
- */
-#define QM_WARNONLY_TRY_UNWRAP(...) \
-  QM_REPORTONLY_TRY_ASSIGN_GLUE(Warning, __VA_ARGS__)
-
-// QM_WARNONLY_TRY_INSPECT doesn't make sense.
-
-/**
- * QM_NOTEONLY_TRY_UNWRAP is like QM_WARN_CHECK_UNWRAP. The only difference is
- * that failures are reported using a lower level of severity relative to
- * failures reported by QM_WARN_CHECK_UNWRAP.
- */
-#define QM_NOTEONLY_TRY_UNWRAP(...) \
-  QM_REPORTONLY_TRY_ASSIGN_GLUE(Note, __VA_ARGS__)
-
-// QM_NOTEONLY_TRY_INSPECT doesn't make sense.
-
-// QM_OR_ELSE_REPORT macro is an implementation detail of
-// QM_OR_ELSE_WARN/QM_OR_ELSE_NOTE/QM_OR_ELSE_LOG_VERBOSE and shouldn't be used
-// directly.
-
-#define QM_OR_ELSE_REPORT(severity, expr, fallback)                \
-  (expr).orElse([&](const auto& firstRes) {                        \
-    mozilla::dom::quota::QM_HANDLE_ERROR(                          \
-        #expr, firstRes, mozilla::dom::quota::Severity::severity); \
-    return fallback(firstRes);                                     \
-  })
-
-/*
- * QM_OR_ELSE_WARN(expr, fallback) evaluates expr, which must produce a Result
- * value. On Success, it just moves the success over. On error, it calls
- * HandleError (with the Warning severity) and a fallback function (passed as
- * the second argument) which produces a new result. Failed expr is always
- * reported as a warning (the macro essentially wraps the fallback function
- * with a warning). QM_OR_ELSE_WARN is a sub macro and is intended to be used
- * along with one of the main macros such as QM_TRY.
- */
-#define QM_OR_ELSE_WARN(...) QM_OR_ELSE_REPORT(Warning, __VA_ARGS__)
-
-/**
- * QM_OR_ELSE_NOTE is like QM_OR_ELSE_WARN. The only difference is that
- * failures are reported using a lower level of severity relative to failures
- * reported by QM_OR_ELSE_WARN.
- */
-#define QM_OR_ELSE_NOTE(...) QM_OR_ELSE_REPORT(Note, __VA_ARGS__)
-
-/**
- * QM_OR_ELSE_LOG_VERBOSE is like QM_OR_ELSE_WARN. The only difference is that
- * failures are reported using the lowest severity which is currently ignored
- * in LogError, so nothing goes to the console, browser console and telemetry.
- * Since nothing goes to the telemetry, the macro can't signal the end of the
- * underlying error stack or change the type of the error stack in the
- * telemetry. For that reason, the expression shouldn't contain nested QM_TRY
- * macro uses.
- */
-#define QM_OR_ELSE_LOG_VERBOSE(...) QM_OR_ELSE_REPORT(Log, __VA_ARGS__)
-
 namespace mozilla::dom::quota {
 
-// XXX Support orElseIf directly in mozilla::Result
-template <typename V, typename E, typename P, typename F>
-auto OrElseIf(Result<V, E>&& aResult, P&& aPred, F&& aFunc) -> Result<V, E> {
-  return MOZ_UNLIKELY(aResult.isErr())
-             ? (std::forward<P>(aPred)(aResult.inspectErr()))
-                   ? std::forward<F>(aFunc)(aResult.unwrapErr())
-                   : aResult.propagateErr()
-             : aResult.unwrap();
-}
-
-}  // namespace mozilla::dom::quota
-
-// QM_OR_ELSE_REPORT_IF macro is an implementation detail of
-// QM_OR_ELSE_WARN_IF/QM_OR_ELSE_NOTE_IF/QM_OR_ELSE_LOG_VERBOSE_IF and
-// shouldn't be used directly.
-
-#define QM_OR_ELSE_REPORT_IF(severity, expr, predicate, fallback) \
-  mozilla::dom::quota::OrElseIf(                                  \
-      (expr),                                                     \
-      [&](const auto& firstRes) {                                 \
-        bool res = predicate(firstRes);                           \
-        mozilla::dom::quota::QM_HANDLE_ERROR(                     \
-            #expr, firstRes,                                      \
-            res ? mozilla::dom::quota::Severity::severity         \
-                : mozilla::dom::quota::Severity::Error);          \
-        return res;                                               \
-      },                                                          \
-      fallback)
-
-/*
- * QM_OR_ELSE_WARN_IF(expr, predicate, fallback) evaluates expr first, which
- * must produce a Result value. On Success, it just moves the success over.
- * On error, it calls a predicate function (passed as the second argument) and
- * then it either calls HandleError (with the Warning severity) and a fallback
- * function (passed as the third argument) which produces a new result if the
- * predicate returned true. Or it calls HandleError (with the Error severity)
- * and propagates the error result if the predicate returned false. So failed
- * expr can be reported as a warning or as an error depending on the predicate.
- * QM_OR_ELSE_WARN_IF is a sub macro and is intended to be used along with one
- * of the main macros such as QM_TRY.
- */
-#define QM_OR_ELSE_WARN_IF(...) QM_OR_ELSE_REPORT_IF(Warning, __VA_ARGS__)
-
-/**
- * QM_OR_ELSE_NOTE_IF is like QM_OR_ELSE_WARN_IF. The only difference is that
- * failures are reported using a lower level of severity relative to failures
- * reported by QM_OR_ELSE_WARN_IF.
- */
-#define QM_OR_ELSE_NOTE_IF(...) QM_OR_ELSE_REPORT_IF(Note, __VA_ARGS__)
-
-/**
- * QM_OR_ELSE_LOG_VERBOSE_IF is like QM_OR_ELSE_WARN_IF. The only difference is
- * that failures are reported using the lowest severity which is currently
- * ignored in LogError, so nothing goes to the console, browser console and
- * telemetry. Since nothing goes to the telemetry, the macro can't signal the
- * end of the underlying error stack or change the type of the error stack in
- * the telemetry. For that reason, the expression shouldn't contain nested
- * QM_TRY macro uses.
- */
-#define QM_OR_ELSE_LOG_VERBOSE_IF(...) \
-  QM_OR_ELSE_REPORT_IF(Verbose, __VA_ARGS__)
-
-// Telemetry probes to collect number of failure during the initialization.
-#ifdef NIGHTLY_BUILD
-#  define RECORD_IN_NIGHTLY(_recorder, _status) \
-    do {                                        \
-      if (NS_SUCCEEDED(_recorder)) {            \
-        _recorder = _status;                    \
-      }                                         \
-    } while (0)
-
-#  define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS \
-    Ok {}
-
-#  define RETURN_STATUS_OR_RESULT(_status, _rv) \
-    return Err(NS_FAILED(_status) ? (_status) : (_rv))
-#else
-#  define RECORD_IN_NIGHTLY(_dummy, _status) \
-    {}
-
-#  define OK_IN_NIGHTLY_PROPAGATE_IN_OTHERS QM_PROPAGATE
-
-#  define RETURN_STATUS_OR_RESULT(_status, _rv) return Err(_rv)
-#endif
-
-class mozIStorageConnection;
-class mozIStorageStatement;
-class nsIFile;
-
-namespace mozilla {
-
-class LogModule;
-
-struct NotOk {};
-
-// Allow MOZ_TRY/QM_TRY to handle `bool` values by wrapping them with OkIf.
-// TODO: Maybe move this to mfbt/ResultExtensions.h
-inline Result<Ok, NotOk> OkIf(bool aValue) {
-  if (aValue) {
-    return Ok();
-  }
-  return Err(NotOk());
-}
-
-// TODO: Maybe move this to mfbt/ResultExtensions.h
-template <auto SuccessValue>
-auto OkToOk(Ok) -> Result<decltype(SuccessValue), nsresult> {
-  return SuccessValue;
-}
-
-template <nsresult ErrorValue, auto SuccessValue,
-          typename V = decltype(SuccessValue)>
-auto ErrToOkOrErr(nsresult aValue) -> Result<V, nsresult> {
-  if (aValue == ErrorValue) {
-    return V{SuccessValue};
-  }
-  return Err(aValue);
-}
-
-template <nsresult ErrorValue, typename V = mozilla::Ok>
-auto ErrToDefaultOkOrErr(nsresult aValue) -> Result<V, nsresult> {
-  if (aValue == ErrorValue) {
-    return V{};
-  }
-  return Err(aValue);
-}
-
-// Helper template function so that QM_TRY predicates checking for a specific
-// error can be concisely written as IsSpecificError<NS_SOME_ERROR> instead of
-// as a more verbose lambda.
-template <nsresult ErrorValue>
-bool IsSpecificError(const nsresult aValue) {
-  return aValue == ErrorValue;
-}
-
-// Helper template function so that QM_TRY fallback functions that are
-// converting errors into specific in-band success values can be concisely
-// written as ErrToOk<SuccessValueToReturn> (with the return type inferred).
-// For example, many file-related APIs that access information about a file may
-// return an nsresult error code if the file does not exist. From an
-// application perspective, the file not existing is not actually exceptional
-// and can instead be handled by the success case.
-template <auto SuccessValue, typename V = decltype(SuccessValue)>
-auto ErrToOk(const nsresult aValue) -> Result<V, nsresult> {
-  return V{SuccessValue};
-}
-
-// Helper template function so that QM_TRY fallback functions that are
-// suppressing errors by converting them into (generic) success can be
-// concisely written as ErrToDefaultOk<>.
-template <typename V = mozilla::Ok>
-auto ErrToDefaultOk(const nsresult aValue) -> Result<V, nsresult> {
-  return V{};
-}
-
-// TODO: Maybe move this to mfbt/ResultExtensions.h
-template <typename R, typename Func, typename... Args>
-Result<R, nsresult> ToResultGet(const Func& aFunc, Args&&... aArgs) {
-  nsresult rv;
-  R res = aFunc(std::forward<Args>(aArgs)..., &rv);
-  if (NS_FAILED(rv)) {
-    return Err(rv);
-  }
-  return res;
-}
-
-// Like Rust's collect with a step function, not a generic iterator/range.
-//
-// Cond must be a function type with a return type to Result<V, E>, where
-// V is convertible to bool
-// - success converts to true indicates that collection shall continue
-// - success converts to false indicates that collection is completed
-// - error indicates that collection shall stop, propagating the error
-//
-// Body must a function type accepting a V xvalue with a return type convertible
-// to Result<empty, E>.
-template <typename Step, typename Body>
-auto CollectEach(Step aStep, const Body& aBody)
-    -> Result<mozilla::Ok, typename std::result_of_t<Step()>::err_type> {
-  using StepResultType = typename std::result_of_t<Step()>::ok_type;
-
-  static_assert(std::is_empty_v<
-                typename std::result_of_t<Body(StepResultType &&)>::ok_type>);
-
-  while (true) {
-    StepResultType element;
-    MOZ_TRY_VAR(element, aStep());
-
-    if (!static_cast<bool>(element)) {
-      break;
-    }
-
-    MOZ_TRY(aBody(std::move(element)));
-  }
-
-  return mozilla::Ok{};
-}
-
-// This is like std::reduce with a to-be-defined execution policy (we don't want
-// to std::terminate on an error, but probably it's fine to just propagate any
-// error that occurred), operating not on a pair of iterators but rather a
-// generator function.
-template <typename InputGenerator, typename T, typename BinaryOp>
-auto ReduceEach(InputGenerator aInputGenerator, T aInit,
-                const BinaryOp& aBinaryOp)
-    -> Result<T, typename std::invoke_result_t<InputGenerator>::err_type> {
-  T res = std::move(aInit);
-
-  // XXX This can be done in parallel!
-  MOZ_TRY(CollectEach(
-      std::move(aInputGenerator),
-      [&res, &aBinaryOp](const auto& element)
-          -> Result<Ok,
-                    typename std::invoke_result_t<InputGenerator>::err_type> {
-        MOZ_TRY_VAR(res, aBinaryOp(std::move(res), element));
-
-        return Ok{};
-      }));
-
-  return std::move(res);
-}
-
-// This is like std::reduce with a to-be-defined execution policy (we don't want
-// to std::terminate on an error, but probably it's fine to just propagate any
-// error that occurred).
-template <typename Range, typename T, typename BinaryOp>
-auto Reduce(Range&& aRange, T aInit, const BinaryOp& aBinaryOp) {
-  using std::begin;
-  using std::end;
-  return ReduceEach(
-      [it = begin(aRange), end = end(aRange)]() mutable {
-        auto res = ToMaybeRef(it != end ? &*it++ : nullptr);
-        return Result<decltype(res), typename std::invoke_result_t<
-                                         BinaryOp, T, decltype(res)>::err_type>(
-            res);
-      },
-      aInit, aBinaryOp);
-}
-
-template <typename Range, typename Body>
-auto CollectEachInRange(Range&& aRange, const Body& aBody)
-    -> Result<mozilla::Ok, nsresult> {
-  for (auto&& element : aRange) {
-    MOZ_TRY(aBody(element));
-  }
-
-  return mozilla::Ok{};
-}
-
-// Like Rust's collect with a while loop, not a generic iterator/range.
-//
-// Cond must be a function type accepting no parameters with a return type
-// convertible to Result<bool, E>, where
-// - success true indicates that collection shall continue
-// - success false indicates that collection is completed
-// - error indicates that collection shall stop, propagating the error
-//
-// Body must a function type accepting no parameters with a return type
-// convertible to Result<empty, E>.
-template <typename Cond, typename Body>
-auto CollectWhile(const Cond& aCond, const Body& aBody)
-    -> Result<mozilla::Ok, typename std::result_of_t<Cond()>::err_type> {
-  return CollectEach(aCond, [&aBody](bool) { return aBody(); });
-}
-
-template <>
-class MOZ_MUST_USE_TYPE GenericErrorResult<mozilla::ipc::IPCResult> {
-  mozilla::ipc::IPCResult mErrorValue;
-
-  template <typename V, typename E2>
-  friend class Result;
-
- public:
-  explicit GenericErrorResult(mozilla::ipc::IPCResult aErrorValue)
-      : mErrorValue(aErrorValue) {
-    MOZ_ASSERT(!aErrorValue);
-  }
-
-  GenericErrorResult(mozilla::ipc::IPCResult aErrorValue,
-                     const ErrorPropagationTag&)
-      : GenericErrorResult(aErrorValue) {}
-
-  operator mozilla::ipc::IPCResult() const { return mErrorValue; }
-};
-
-namespace dom {
-namespace quota {
-
-extern const char kQuotaGenericDelimiter;
-
-// Telemetry keys to indicate types of errors.
-#ifdef NIGHTLY_BUILD
-extern const nsLiteralCString kQuotaInternalError;
-extern const nsLiteralCString kQuotaExternalError;
-#else
-// No need for these when we're not collecting telemetry.
-#  define kQuotaInternalError
-#  define kQuotaExternalError
-#endif
-
-class BackgroundThreadObject {
- protected:
-  nsCOMPtr<nsIEventTarget> mOwningThread;
-
- public:
-  void AssertIsOnOwningThread() const
-#ifdef DEBUG
-      ;
-#else
-  {
-  }
-#endif
-
-  nsIEventTarget* OwningThread() const;
-
- protected:
-  BackgroundThreadObject();
-
-  explicit BackgroundThreadObject(nsIEventTarget* aOwningThread);
-};
-
-void AssertIsOnIOThread();
-
-void AssertCurrentThreadOwnsQuotaMutex();
-
-bool IsOnIOThread();
-
-MOZ_COLD void ReportInternalError(const char* aFile, uint32_t aLine,
-                                  const char* aStr);
-
-LogModule* GetQuotaManagerLogger();
-
-void AnonymizeCString(nsACString& aCString);
-
-inline auto AnonymizedCString(const nsACString& aCString) {
-  nsAutoCString result{aCString};
-  AnonymizeCString(result);
-  return result;
-}
-
-void AnonymizeOriginString(nsACString& aOriginString);
-
-inline auto AnonymizedOriginString(const nsACString& aOriginString) {
-  nsAutoCString result{aOriginString};
-  AnonymizeOriginString(result);
-  return result;
-}
-
-#ifdef XP_WIN
-void CacheUseDOSDevicePathSyntaxPrefValue();
-#endif
-
-Result<nsCOMPtr<nsIFile>, nsresult> QM_NewLocalFile(const nsAString& aPath);
-
-nsDependentCSubstring GetLeafName(const nsACString& aPath);
-
-Result<nsCOMPtr<nsIFile>, nsresult> CloneFileAndAppend(
-    nsIFile& aDirectory, const nsAString& aPathElement);
-
-enum class nsIFileKind {
-  ExistsAsDirectory,
-  ExistsAsFile,
-  DoesNotExist,
-};
-
-// XXX We can use this outside of QM and its clients as well, probably. Maybe it
-// could be moved to xpcom/io?
-Result<nsIFileKind, nsresult> GetDirEntryKind(nsIFile& aFile);
-
-Result<nsCOMPtr<mozIStorageStatement>, nsresult> CreateStatement(
-    mozIStorageConnection& aConnection, const nsACString& aStatementString);
-
-enum class SingleStepResult { AssertHasResult, ReturnNullIfNoResult };
-
-template <SingleStepResult ResultHandling>
-using SingleStepSuccessType =
-    std::conditional_t<ResultHandling == SingleStepResult::AssertHasResult,
-                       NotNull<nsCOMPtr<mozIStorageStatement>>,
-                       nsCOMPtr<mozIStorageStatement>>;
-
-template <SingleStepResult ResultHandling>
-Result<SingleStepSuccessType<ResultHandling>, nsresult> ExecuteSingleStep(
-    nsCOMPtr<mozIStorageStatement>&& aStatement);
-
-// Creates a statement with the specified aStatementString, executes a single
-// step, and returns the statement.
-// Depending on the value ResultHandling,
-// - it is asserted that there is a result (default resp.
-//   SingleStepResult::AssertHasResult), and the success type is
-//   MovingNotNull<nsCOMPtr<mozIStorageStatement>>
-// - it is asserted that there is no result, and the success type is Ok
-// - in case there is no result, nullptr is returned, and the success type is
-//   nsCOMPtr<mozIStorageStatement>
-// Any other errors are always propagated.
-template <SingleStepResult ResultHandling = SingleStepResult::AssertHasResult>
-Result<SingleStepSuccessType<ResultHandling>, nsresult>
-CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection,
-                                    const nsACString& aStatementString);
-
-namespace detail {
-
-// Determine the absolute path of the root of our built source tree so we can
-// derive source-relative paths for non-exported header files in
-// MakeSourceFileRelativePath. Exported header files end up in the objdir and
-// we have GetObjdirDistIncludeTreeBase for that.
-nsDependentCSubstring GetSourceTreeBase();
-
-// Determine the absolute path of the root of our built OBJDIR/dist/include
-// directory. The aQuotaCommonHPath argument cleverly defaults to __FILE__
-// initialized in our exported header; no argument should ever be provided to
-// this method. GetSourceTreeBase handles identifying the root of the source
-// tree.
-nsDependentCSubstring GetObjdirDistIncludeTreeBase(
-    const nsLiteralCString& aQuotaCommonHPath = nsLiteralCString(__FILE__));
-
-nsDependentCSubstring MakeSourceFileRelativePath(
-    const nsACString& aSourceFilePath);
-
-}  // namespace detail
-
-enum class Severity {
-  Error,
-  Warning,
-  Note,
-  Verbose,
-};
-
-#ifdef QM_LOG_ERROR_ENABLED
-#  ifdef QM_ERROR_STACKS_ENABLED
-using ResultType = Variant<QMResult, nsresult, Nothing>;
-
-void LogError(const nsACString& aExpr, const ResultType& aResult,
-              const nsACString& aSourceFilePath, int32_t aSourceFileLine,
-              Severity aSeverity)
-#  else
-void LogError(const nsACString& aExpr, Maybe<nsresult> aMaybeRv,
-              const nsACString& aSourceFilePath, int32_t aSourceFileLine,
-              Severity aSeverity)
-#  endif
-    ;
-#endif
-
-#ifdef DEBUG
-Result<bool, nsresult> WarnIfFileIsUnknown(nsIFile& aFile,
-                                           const char* aSourceFilePath,
-                                           int32_t aSourceFileLine);
-#endif
-
 struct MOZ_STACK_CLASS ScopedLogExtraInfo {
   static constexpr const char kTagQuery[] = "query";
   static constexpr const char kTagContext[] = "context";
 
 #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
  private:
   static auto FindSlot(const char* aTag);
 
@@ -1388,212 +65,11 @@ struct MOZ_STACK_CLASS ScopedLogExtraInf
   template <size_t N>
   ScopedLogExtraInfo(const char (&aTag)[N], const nsACString& aExtraInfo) {}
 
   // user-defined to silence unused variable warnings
   ~ScopedLogExtraInfo() {}
 #endif
 };
 
-// As HandleError is a function that will only be called in error cases, it is
-// marked with MOZ_COLD to avoid bloating the code of calling functions, if it's
-// not empty.
-//
-// For the same reason, the string-ish parameters are of type const char* rather
-// than any ns*String type, to minimize the code at each call site. This
-// deliberately de-optimizes runtime performance, which is uncritical during
-// error handling.
-//
-// This functions are not intended to be called
-// directly, they should only be called from the QM_* macros.
-#ifdef QM_LOG_ERROR_ENABLED
-template <typename T>
-MOZ_COLD MOZ_NEVER_INLINE void HandleError(const char* aExpr, const T& aRv,
-                                           const char* aSourceFilePath,
-                                           int32_t aSourceFileLine,
-                                           const Severity aSeverity) {
-#  ifdef QM_ERROR_STACKS_ENABLED
-  if constexpr (std::is_same_v<T, QMResult> || std::is_same_v<T, nsresult>) {
-    mozilla::dom::quota::LogError(nsDependentCString(aExpr), ResultType(aRv),
-                                  nsDependentCString(aSourceFilePath),
-                                  aSourceFileLine, aSeverity);
-  } else {
-    mozilla::dom::quota::LogError(
-        nsDependentCString(aExpr), ResultType(Nothing{}),
-        nsDependentCString(aSourceFilePath), aSourceFileLine, aSeverity);
-  }
-#  else
-  if constexpr (std::is_same_v<T, nsresult>) {
-    mozilla::dom::quota::LogError(nsDependentCString(aExpr), Some(aRv),
-                                  nsDependentCString(aSourceFilePath),
-                                  aSourceFileLine, aSeverity);
-  } else {
-    mozilla::dom::quota::LogError(nsDependentCString(aExpr), Nothing{},
-                                  nsDependentCString(aSourceFilePath),
-                                  aSourceFileLine, aSeverity);
-  }
-#  endif
-}
-#else
-template <typename T>
-MOZ_ALWAYS_INLINE constexpr void HandleError(const char* aExpr, const T& aRv,
-                                             const char* aSourceFilePath,
-                                             int32_t aSourceFileLine,
-                                             const Severity aSeverity) {}
-#endif
-
-template <typename T>
-Nothing HandleErrorReturnNothing(const char* aExpr, const T& aRv,
-                                 const char* aSourceFilePath,
-                                 int32_t aSourceFileLine,
-                                 const Severity aSeverity) {
-  HandleError(aExpr, aRv, aSourceFilePath, aSourceFileLine, aSeverity);
-  return Nothing();
-}
-
-template <typename T, typename CleanupFunc>
-Nothing HandleErrorWithCleanupReturnNothing(const char* aExpr, const T& aRv,
-                                            const char* aSourceFilePath,
-                                            int32_t aSourceFileLine,
-                                            const Severity aSeverity,
-                                            CleanupFunc&& aCleanupFunc) {
-  HandleError(aExpr, aRv, aSourceFilePath, aSourceFileLine, aSeverity);
-  std::forward<CleanupFunc>(aCleanupFunc)(aRv);
-  return Nothing();
-}
-
-template <SingleStepResult ResultHandling = SingleStepResult::AssertHasResult,
-          typename BindFunctor>
-Result<SingleStepSuccessType<ResultHandling>, nsresult>
-CreateAndExecuteSingleStepStatement(mozIStorageConnection& aConnection,
-                                    const nsACString& aStatementString,
-                                    BindFunctor aBindFunctor) {
-  QM_TRY_UNWRAP(auto stmt, CreateStatement(aConnection, aStatementString));
-
-  QM_TRY(aBindFunctor(*stmt));
-
-  return ExecuteSingleStep<ResultHandling>(std::move(stmt));
-}
-
-template <typename StepFunc>
-Result<Ok, nsresult> CollectWhileHasResult(mozIStorageStatement& aStmt,
-                                           StepFunc&& aStepFunc) {
-  return CollectWhile(
-      [&aStmt] { QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE(aStmt, ExecuteStep)); },
-      [&aStmt, &aStepFunc] { return aStepFunc(aStmt); });
-}
-
-template <typename StepFunc,
-          typename ArrayType = nsTArray<typename std::invoke_result_t<
-              StepFunc, mozIStorageStatement&>::ok_type>>
-auto CollectElementsWhileHasResult(mozIStorageStatement& aStmt,
-                                   StepFunc&& aStepFunc)
-    -> Result<ArrayType, nsresult> {
-  ArrayType res;
+}  // namespace mozilla::dom::quota
 
-  QM_TRY(CollectWhileHasResult(
-      aStmt, [&aStepFunc, &res](auto& stmt) -> Result<Ok, nsresult> {
-        QM_TRY_UNWRAP(auto element, aStepFunc(stmt));
-        res.AppendElement(std::move(element));
-        return Ok{};
-      }));
-
-  return std::move(res);
-}
-
-template <typename ArrayType, typename StepFunc>
-auto CollectElementsWhileHasResultTyped(mozIStorageStatement& aStmt,
-                                        StepFunc&& aStepFunc) {
-  return CollectElementsWhileHasResult<StepFunc, ArrayType>(
-      aStmt, std::forward<StepFunc>(aStepFunc));
-}
-
-namespace detail {
-template <typename Cancel, typename Body>
-Result<mozilla::Ok, nsresult> CollectEachFile(nsIFile& aDirectory,
-                                              const Cancel& aCancel,
-                                              const Body& aBody) {
-  QM_TRY_INSPECT(const auto& entries,
-                 MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIDirectoryEnumerator>,
-                                            aDirectory, GetDirectoryEntries));
-
-  return CollectEach(
-      [&entries, &aCancel]() -> Result<nsCOMPtr<nsIFile>, nsresult> {
-        if (aCancel()) {
-          return nsCOMPtr<nsIFile>{};
-        }
-
-        QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIFile>, entries,
-                                                 GetNextFile));
-      },
-      aBody);
-}
-}  // namespace detail
-
-template <typename Body>
-Result<mozilla::Ok, nsresult> CollectEachFile(nsIFile& aDirectory,
-                                              const Body& aBody) {
-  return detail::CollectEachFile(
-      aDirectory, [] { return false; }, aBody);
-}
-
-template <typename Body>
-Result<mozilla::Ok, nsresult> CollectEachFileAtomicCancelable(
-    nsIFile& aDirectory, const Atomic<bool>& aCanceled, const Body& aBody) {
-  return detail::CollectEachFile(
-      aDirectory, [&aCanceled] { return static_cast<bool>(aCanceled); }, aBody);
-}
-
-template <typename T, typename Body>
-auto ReduceEachFileAtomicCancelable(nsIFile& aDirectory,
-                                    const Atomic<bool>& aCanceled, T aInit,
-                                    const Body& aBody) -> Result<T, nsresult> {
-  QM_TRY_INSPECT(const auto& entries,
-                 MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIDirectoryEnumerator>,
-                                            aDirectory, GetDirectoryEntries));
-
-  return ReduceEach(
-      [&entries, &aCanceled]() -> Result<nsCOMPtr<nsIFile>, nsresult> {
-        if (aCanceled) {
-          return nsCOMPtr<nsIFile>{};
-        }
-
-        QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<nsIFile>, entries,
-                                                 GetNextFile));
-      },
-      std::move(aInit), aBody);
-}
-
-constexpr bool IsDatabaseCorruptionError(const nsresult aRv) {
-  return aRv == NS_ERROR_FILE_CORRUPTED || aRv == NS_ERROR_STORAGE_IOERR;
-}
-
-template <typename Func>
-auto CallWithDelayedRetriesIfAccessDenied(Func&& aFunc, uint32_t aMaxRetries,
-                                          uint32_t aDelayMs)
-    -> Result<typename std::result_of_t<Func()>::ok_type, nsresult> {
-  uint32_t retries = 0;
-
-  while (true) {
-    auto result = std::forward<Func>(aFunc)();
-
-    if (result.isOk()) {
-      return result;
-    }
-
-    if (result.inspectErr() != NS_ERROR_FILE_IS_LOCKED &&
-        result.inspectErr() != NS_ERROR_FILE_ACCESS_DENIED) {
-      return result;
-    }
-
-    if (retries++ >= aMaxRetries) {
-      return result;
-    }
-
-    PR_Sleep(PR_MillisecondsToInterval(aDelayMs));
-  }
-}
-
-}  // namespace quota
-}  // namespace dom
-}  // namespace mozilla
-
-#endif  // mozilla_dom_quota_quotacommon_h__
+#endif  // DOM_QUOTA_SCOPEDLOGEXTRAINFO_H_
--- a/dom/quota/moz.build
+++ b/dom/quota/moz.build
@@ -48,16 +48,17 @@ EXPORTS.mozilla.dom.quota += [
     "MemoryOutputStream.h",
     "NSSCipherStrategy.h",
     "OriginScope.h",
     "PersistenceType.h",
     "QuotaCommon.h",
     "QuotaManager.h",
     "QuotaManagerService.h",
     "QuotaObject.h",
+    "ScopedLogExtraInfo.h",
     "SerializationHelpers.h",
     "UsageInfo.h",
 ]
 
 XPCOM_MANIFESTS += [
     "components.conf",
 ]
 
@@ -75,16 +76,17 @@ UNIFIED_SOURCES += [
     "nsIndexedDBProtocolHandler.cpp",
     "NSSCipherStrategy.cpp",
     "PersistenceType.cpp",
     "QMResult.cpp",
     "QuotaCommon.cpp",
     "QuotaManagerService.cpp",
     "QuotaRequests.cpp",
     "QuotaResults.cpp",
+    "ScopedLogExtraInfo.cpp",
     "StorageManager.cpp",
 ]
 
 IPDL_SOURCES += [
     "PQuota.ipdl",
     "PQuotaRequest.ipdl",
     "PQuotaUsageRequest.ipdl",
 ]
--- a/dom/quota/test/gtest/TestQuotaCommon.cpp
+++ b/dom/quota/test/gtest/TestQuotaCommon.cpp
@@ -1811,70 +1811,16 @@ TEST(QuotaCommon_Reduce, Success)
         return val + add.ref();
       });
   static_assert(std::is_same_v<decltype(result), const Result<int, Failed>>);
 
   MOZ_RELEASE_ASSERT(result.isOk());
   MOZ_RELEASE_ASSERT(15 == result.inspect());
 }
 
-TEST(QuotaCommon_ScopedLogExtraInfo, AddAndRemove)
-{
-  static constexpr auto text = "foo"_ns;
-
-  {
-    const auto extraInfo =
-        ScopedLogExtraInfo{ScopedLogExtraInfo::kTagQuery, text};
-
-#ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
-    const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
-
-    EXPECT_EQ(text, *extraInfoMap.at(ScopedLogExtraInfo::kTagQuery));
-#endif
-  }
-
-#ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
-  const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
-
-  EXPECT_EQ(0u, extraInfoMap.count(ScopedLogExtraInfo::kTagQuery));
-#endif
-}
-
-TEST(QuotaCommon_ScopedLogExtraInfo, Nested)
-{
-  static constexpr auto text = "foo"_ns;
-  static constexpr auto nestedText = "bar"_ns;
-
-  {
-    const auto extraInfo =
-        ScopedLogExtraInfo{ScopedLogExtraInfo::kTagQuery, text};
-
-    {
-      const auto extraInfo =
-          ScopedLogExtraInfo{ScopedLogExtraInfo::kTagQuery, nestedText};
-
-#ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
-      const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
-      EXPECT_EQ(nestedText, *extraInfoMap.at(ScopedLogExtraInfo::kTagQuery));
-#endif
-    }
-
-#ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
-    const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
-    EXPECT_EQ(text, *extraInfoMap.at(ScopedLogExtraInfo::kTagQuery));
-#endif
-  }
-
-#ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
-  const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
-
-  EXPECT_EQ(0u, extraInfoMap.count(ScopedLogExtraInfo::kTagQuery));
-#endif
-}
-
 TEST(QuotaCommon_CallWithDelayedRetriesIfAccessDenied, NoFailures)
 {
   uint32_t tries = 0;
 
   auto res = CallWithDelayedRetriesIfAccessDenied(
       [&tries]() -> Result<Ok, nsresult> {
         ++tries;
         return Ok{};
copy from dom/quota/test/gtest/TestQuotaCommon.cpp
copy to dom/quota/test/gtest/TestScopedLogExtraInfo.cpp
--- a/dom/quota/test/gtest/TestQuotaCommon.cpp
+++ b/dom/quota/test/gtest/TestScopedLogExtraInfo.cpp
@@ -1,1827 +1,21 @@
 /* -*- 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 "mozilla/dom/quota/QuotaCommon.h"
+#include "mozilla/dom/quota/ScopedLogExtraInfo.h"
 
 #include "gtest/gtest.h"
 
-#include <algorithm>
-#include <array>
-#include <cstddef>
-#include <cstdint>
-#include <map>
-#include <new>
-#include <ostream>
-#include <type_traits>
-#include <utility>
-#include <vector>
-#include "ErrorList.h"
-#include "mozilla/Assertions.h"
-#include "mozilla/Result.h"
-#include "mozilla/ResultExtensions.h"
-#include "mozilla/ResultVariant.h"
-#include "mozilla/Unused.h"
-#include "mozilla/fallible.h"
-#include "nsCOMPtr.h"
-#include "nsLiteralString.h"
-#include "nsString.h"
-#include "nsStringFwd.h"
-#include "nsTLiteralString.h"
-
-class nsISupports;
-
-using namespace mozilla;
 using namespace mozilla::dom::quota;
 
-#ifdef __clang__
-#  pragma clang diagnostic push
-#  pragma clang diagnostic ignored "-Wunreachable-code"
-#endif
-
-TEST(QuotaCommon_Try, Success)
-{
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&tryDidNotReturn]() -> nsresult {
-    QM_TRY(NS_OK);
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-#ifdef DEBUG
-TEST(QuotaCommon_Try, Success_CustomErr_AssertUnreachable)
-{
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&tryDidNotReturn]() -> nsresult {
-    QM_TRY(NS_OK, QM_ASSERT_UNREACHABLE);
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_Try, Success_NoErr_AssertUnreachable)
-{
-  bool tryDidNotReturn = false;
-
-  [&tryDidNotReturn]() -> void {
-    QM_TRY(NS_OK, QM_ASSERT_UNREACHABLE_VOID);
-
-    tryDidNotReturn = true;
-  }();
-
-  EXPECT_TRUE(tryDidNotReturn);
-}
-#else
-#  if defined(QM_ASSERT_UNREACHABLE) || defined(QM_ASSERT_UNREACHABLE_VOID)
-#error QM_ASSERT_UNREACHABLE and QM_ASSERT_UNREACHABLE_VOID should not be defined.
-#  endif
-#endif
-
-#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
-TEST(QuotaCommon_Try, Success_CustomErr_DiagnosticAssertUnreachable)
-{
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&tryDidNotReturn]() -> nsresult {
-    QM_TRY(NS_OK, QM_DIAGNOSTIC_ASSERT_UNREACHABLE);
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_Try, Success_NoErr_DiagnosticAssertUnreachable)
-{
-  bool tryDidNotReturn = false;
-
-  [&tryDidNotReturn]() -> void {
-    QM_TRY(NS_OK, QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID);
-
-    tryDidNotReturn = true;
-  }();
-
-  EXPECT_TRUE(tryDidNotReturn);
-}
-#else
-#  if defined(QM_DIAGNOSTIC_ASSERT_UNREACHABLE) || \
-      defined(QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID)
-#error QM_DIAGNOSTIC_ASSERT_UNREACHABLE and QM_DIAGNOSTIC_ASSERT_UNREACHABLE_VOID should not be defined.
-#  endif
-#endif
-
-TEST(QuotaCommon_Try, Success_WithCleanup)
-{
-  bool tryCleanupRan = false;
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&tryCleanupRan, &tryDidNotReturn]() -> nsresult {
-    QM_TRY(NS_OK, QM_PROPAGATE,
-           [&tryCleanupRan](const auto&) { tryCleanupRan = true; });
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(tryCleanupRan);
-  EXPECT_TRUE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_Try, Failure_PropagateErr)
-{
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&tryDidNotReturn]() -> nsresult {
-    QM_TRY(NS_ERROR_FAILURE);
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_Try, Failure_CustomErr)
-{
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&tryDidNotReturn]() -> nsresult {
-    QM_TRY(NS_ERROR_FAILURE, NS_ERROR_UNEXPECTED);
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_Try, Failure_NoErr)
-{
-  bool tryDidNotReturn = false;
-
-  [&tryDidNotReturn]() -> void {
-    QM_TRY(NS_ERROR_FAILURE, QM_VOID);
-
-    tryDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryDidNotReturn);
-}
-
-TEST(QuotaCommon_Try, Failure_WithCleanup)
-{
-  bool tryCleanupRan = false;
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&tryCleanupRan, &tryDidNotReturn]() -> nsresult {
-    QM_TRY(NS_ERROR_FAILURE, QM_PROPAGATE,
-           [&tryCleanupRan](const auto& result) {
-             EXPECT_EQ(result, NS_ERROR_FAILURE);
-
-             tryCleanupRan = true;
-           });
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(tryCleanupRan);
-  EXPECT_FALSE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_Try, Failure_WithCleanup_UnwrapErr)
-{
-  bool tryCleanupRan = false;
-  bool tryDidNotReturn = false;
-
-  nsresult rv;
-
-  [&tryCleanupRan, &tryDidNotReturn](nsresult& aRv) -> void {
-    QM_TRY(NS_ERROR_FAILURE, QM_VOID, ([&tryCleanupRan, &aRv](auto& result) {
-             EXPECT_EQ(result, NS_ERROR_FAILURE);
-
-             aRv = result;
-
-             tryCleanupRan = true;
-           }));
-
-    tryDidNotReturn = true;
-
-    aRv = NS_OK;
-  }(rv);
-
-  EXPECT_TRUE(tryCleanupRan);
-  EXPECT_FALSE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_Try, SameLine)
-{
-  // clang-format off
-  QM_TRY(NS_OK, QM_VOID); QM_TRY(NS_OK, QM_VOID);
-  // clang-format on
-}
-
-TEST(QuotaCommon_Try, NestingMadness_Success)
-{
-  bool nestedTryDidNotReturn = false;
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&nestedTryDidNotReturn, &tryDidNotReturn]() -> nsresult {
-    QM_TRY(([&nestedTryDidNotReturn]() -> Result<Ok, nsresult> {
-      QM_TRY(NS_OK);
-
-      nestedTryDidNotReturn = true;
-
-      return Ok();
-    }()));
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(nestedTryDidNotReturn);
-  EXPECT_TRUE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_Try, NestingMadness_Failure)
-{
-  bool nestedTryDidNotReturn = false;
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&nestedTryDidNotReturn, &tryDidNotReturn]() -> nsresult {
-    QM_TRY(([&nestedTryDidNotReturn]() -> Result<Ok, nsresult> {
-      QM_TRY(NS_ERROR_FAILURE);
-
-      nestedTryDidNotReturn = true;
-
-      return Ok();
-    }()));
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(nestedTryDidNotReturn);
-  EXPECT_FALSE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_Try, NestingMadness_Multiple_Success)
-{
-  bool nestedTry1DidNotReturn = false;
-  bool nestedTry2DidNotReturn = false;
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&nestedTry1DidNotReturn, &nestedTry2DidNotReturn,
-                 &tryDidNotReturn]() -> nsresult {
-    QM_TRY(([&nestedTry1DidNotReturn,
-             &nestedTry2DidNotReturn]() -> Result<Ok, nsresult> {
-      QM_TRY(NS_OK);
-
-      nestedTry1DidNotReturn = true;
-
-      QM_TRY(NS_OK);
-
-      nestedTry2DidNotReturn = true;
-
-      return Ok();
-    }()));
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(nestedTry1DidNotReturn);
-  EXPECT_TRUE(nestedTry2DidNotReturn);
-  EXPECT_TRUE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_Try, NestingMadness_Multiple_Failure1)
-{
-  bool nestedTry1DidNotReturn = false;
-  bool nestedTry2DidNotReturn = false;
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&nestedTry1DidNotReturn, &nestedTry2DidNotReturn,
-                 &tryDidNotReturn]() -> nsresult {
-    QM_TRY(([&nestedTry1DidNotReturn,
-             &nestedTry2DidNotReturn]() -> Result<Ok, nsresult> {
-      QM_TRY(NS_ERROR_FAILURE);
-
-      nestedTry1DidNotReturn = true;
-
-      QM_TRY(NS_OK);
-
-      nestedTry2DidNotReturn = true;
-
-      return Ok();
-    }()));
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(nestedTry1DidNotReturn);
-  EXPECT_FALSE(nestedTry2DidNotReturn);
-  EXPECT_FALSE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_Try, NestingMadness_Multiple_Failure2)
-{
-  bool nestedTry1DidNotReturn = false;
-  bool nestedTry2DidNotReturn = false;
-  bool tryDidNotReturn = false;
-
-  nsresult rv = [&nestedTry1DidNotReturn, &nestedTry2DidNotReturn,
-                 &tryDidNotReturn]() -> nsresult {
-    QM_TRY(([&nestedTry1DidNotReturn,
-             &nestedTry2DidNotReturn]() -> Result<Ok, nsresult> {
-      QM_TRY(NS_OK);
-
-      nestedTry1DidNotReturn = true;
-
-      QM_TRY(NS_ERROR_FAILURE);
-
-      nestedTry2DidNotReturn = true;
-
-      return Ok();
-    }()));
-
-    tryDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(nestedTry1DidNotReturn);
-  EXPECT_FALSE(nestedTry2DidNotReturn);
-  EXPECT_FALSE(tryDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryInspect, Success)
-{
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(const auto& x, (Result<int32_t, nsresult>{42}));
-    EXPECT_EQ(x, 42);
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-#ifdef DEBUG
-TEST(QuotaCommon_TryInspect, Success_CustomErr_AssertUnreachable)
-{
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(const auto& x, (Result<int32_t, nsresult>{42}),
-                   QM_ASSERT_UNREACHABLE);
-    EXPECT_EQ(x, 42);
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_TryInspect, Success_NoErr_AssertUnreachable)
-{
-  bool tryInspectDidNotReturn = false;
-
-  [&tryInspectDidNotReturn]() -> void {
-    QM_TRY_INSPECT(const auto& x, (Result<int32_t, nsresult>{42}),
-                   QM_ASSERT_UNREACHABLE_VOID);
-    EXPECT_EQ(x, 42);
-
-    tryInspectDidNotReturn = true;
-  }();
-
-  EXPECT_TRUE(tryInspectDidNotReturn);
-}
-#endif
-
-TEST(QuotaCommon_TryInspect, Success_WithCleanup)
-{
-  bool tryInspectCleanupRan = false;
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&tryInspectCleanupRan, &tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(
-        const auto& x, (Result<int32_t, nsresult>{42}), QM_PROPAGATE,
-        [&tryInspectCleanupRan](const auto&) { tryInspectCleanupRan = true; });
-    EXPECT_EQ(x, 42);
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(tryInspectCleanupRan);
-  EXPECT_TRUE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_TryInspect, Failure_PropagateErr)
-{
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(const auto& x,
-                   (Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}));
-    Unused << x;
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryInspect, Failure_CustomErr)
-{
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(const auto& x,
-                   (Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}),
-                   NS_ERROR_UNEXPECTED);
-    Unused << x;
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_TryInspect, Failure_NoErr)
-{
-  bool tryInspectDidNotReturn = false;
-
-  [&tryInspectDidNotReturn]() -> void {
-    QM_TRY_INSPECT(const auto& x,
-                   (Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}), QM_VOID);
-    Unused << x;
-
-    tryInspectDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryInspectDidNotReturn);
-}
-
-TEST(QuotaCommon_TryInspect, Failure_WithCleanup)
-{
-  bool tryInspectCleanupRan = false;
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&tryInspectCleanupRan, &tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(const auto& x,
-                   (Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}),
-                   QM_PROPAGATE, [&tryInspectCleanupRan](const auto& result) {
-                     EXPECT_EQ(result, NS_ERROR_FAILURE);
-
-                     tryInspectCleanupRan = true;
-                   });
-    Unused << x;
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(tryInspectCleanupRan);
-  EXPECT_FALSE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryInspect, Failure_WithCleanup_UnwrapErr)
-{
-  bool tryInspectCleanupRan = false;
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv;
-
-  [&tryInspectCleanupRan, &tryInspectDidNotReturn](nsresult& aRv) -> void {
-    QM_TRY_INSPECT(const auto& x,
-                   (Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}), QM_VOID,
-                   ([&tryInspectCleanupRan, &aRv](auto& result) {
-                     EXPECT_EQ(result, NS_ERROR_FAILURE);
-
-                     aRv = result;
-
-                     tryInspectCleanupRan = true;
-                   }));
-    Unused << x;
-
-    tryInspectDidNotReturn = true;
-
-    aRv = NS_OK;
-  }(rv);
-
-  EXPECT_TRUE(tryInspectCleanupRan);
-  EXPECT_FALSE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryInspect, ConstDecl)
-{
-  QM_TRY_INSPECT(const int32_t& x, (Result<int32_t, nsresult>{42}), QM_VOID);
-
-  static_assert(std::is_same_v<decltype(x), const int32_t&>);
-
-  EXPECT_EQ(x, 42);
-}
-
-TEST(QuotaCommon_TryInspect, SameScopeDecl)
-{
-  QM_TRY_INSPECT(const int32_t& x, (Result<int32_t, nsresult>{42}), QM_VOID);
-  EXPECT_EQ(x, 42);
-
-  QM_TRY_INSPECT(const int32_t& y, (Result<int32_t, nsresult>{42}), QM_VOID);
-  EXPECT_EQ(y, 42);
-}
-
-TEST(QuotaCommon_TryInspect, SameLine)
-{
-  // clang-format off
-  QM_TRY_INSPECT(const auto &x, (Result<int32_t, nsresult>{42}), QM_VOID); QM_TRY_INSPECT(const auto &y, (Result<int32_t, nsresult>{42}), QM_VOID);
-  // clang-format on
-
-  EXPECT_EQ(x, 42);
-  EXPECT_EQ(y, 42);
-}
-
-TEST(QuotaCommon_TryInspect, NestingMadness_Success)
-{
-  bool nestedTryInspectDidNotReturn = false;
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&nestedTryInspectDidNotReturn,
-                 &tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(
-        const auto& x,
-        ([&nestedTryInspectDidNotReturn]() -> Result<int32_t, nsresult> {
-          QM_TRY_INSPECT(const auto& x, (Result<int32_t, nsresult>{42}));
-
-          nestedTryInspectDidNotReturn = true;
-
-          return x;
-        }()));
-    EXPECT_EQ(x, 42);
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(nestedTryInspectDidNotReturn);
-  EXPECT_TRUE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_TryInspect, NestingMadness_Failure)
-{
-  bool nestedTryInspectDidNotReturn = false;
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&nestedTryInspectDidNotReturn,
-                 &tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(
-        const auto& x,
-        ([&nestedTryInspectDidNotReturn]() -> Result<int32_t, nsresult> {
-          QM_TRY_INSPECT(const auto& x,
-                         (Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}));
-
-          nestedTryInspectDidNotReturn = true;
-
-          return x;
-        }()));
-    Unused << x;
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(nestedTryInspectDidNotReturn);
-  EXPECT_FALSE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryInspect, NestingMadness_Multiple_Success)
-{
-  bool nestedTryInspect1DidNotReturn = false;
-  bool nestedTryInspect2DidNotReturn = false;
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn,
-                 &tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(
-        const auto& z,
-        ([&nestedTryInspect1DidNotReturn,
-          &nestedTryInspect2DidNotReturn]() -> Result<int32_t, nsresult> {
-          QM_TRY_INSPECT(const auto& x, (Result<int32_t, nsresult>{42}));
-
-          nestedTryInspect1DidNotReturn = true;
-
-          QM_TRY_INSPECT(const auto& y, (Result<int32_t, nsresult>{42}));
-
-          nestedTryInspect2DidNotReturn = true;
-
-          return x + y;
-        }()));
-    EXPECT_EQ(z, 84);
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(nestedTryInspect1DidNotReturn);
-  EXPECT_TRUE(nestedTryInspect2DidNotReturn);
-  EXPECT_TRUE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_OK);
-}
-
-TEST(QuotaCommon_TryInspect, NestingMadness_Multiple_Failure1)
-{
-  bool nestedTryInspect1DidNotReturn = false;
-  bool nestedTryInspect2DidNotReturn = false;
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn,
-                 &tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(
-        const auto& z,
-        ([&nestedTryInspect1DidNotReturn,
-          &nestedTryInspect2DidNotReturn]() -> Result<int32_t, nsresult> {
-          QM_TRY_INSPECT(const auto& x,
-                         (Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}));
-
-          nestedTryInspect1DidNotReturn = true;
-
-          QM_TRY_INSPECT(const auto& y, (Result<int32_t, nsresult>{42}));
-
-          nestedTryInspect2DidNotReturn = true;
-
-          return x + y;
-        }()));
-    Unused << z;
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(nestedTryInspect1DidNotReturn);
-  EXPECT_FALSE(nestedTryInspect2DidNotReturn);
-  EXPECT_FALSE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryInspect, NestingMadness_Multiple_Failure2)
-{
-  bool nestedTryInspect1DidNotReturn = false;
-  bool nestedTryInspect2DidNotReturn = false;
-  bool tryInspectDidNotReturn = false;
-
-  nsresult rv = [&nestedTryInspect1DidNotReturn, &nestedTryInspect2DidNotReturn,
-                 &tryInspectDidNotReturn]() -> nsresult {
-    QM_TRY_INSPECT(
-        const auto& z,
-        ([&nestedTryInspect1DidNotReturn,
-          &nestedTryInspect2DidNotReturn]() -> Result<int32_t, nsresult> {
-          QM_TRY_INSPECT(const auto& x, (Result<int32_t, nsresult>{42}));
-
-          nestedTryInspect1DidNotReturn = true;
-
-          QM_TRY_INSPECT(const auto& y,
-                         (Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}));
-
-          nestedTryInspect2DidNotReturn = true;
-
-          return x + y;
-        }()));
-    Unused << z;
-
-    tryInspectDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(nestedTryInspect1DidNotReturn);
-  EXPECT_FALSE(nestedTryInspect2DidNotReturn);
-  EXPECT_FALSE(tryInspectDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-// We are not repeating all QM_TRY_INSPECT test cases for QM_TRY_UNWRAP, since
-// they are largely based on the same implementation. We just add some where
-// inspecting and unwrapping differ.
-
-TEST(QuotaCommon_TryUnwrap, NonConstDecl)
-{
-  QM_TRY_UNWRAP(int32_t x, (Result<int32_t, nsresult>{42}), QM_VOID);
-
-  static_assert(std::is_same_v<decltype(x), int32_t>);
-
-  EXPECT_EQ(x, 42);
-}
-
-TEST(QuotaCommon_TryUnwrap, RvalueDecl)
-{
-  QM_TRY_UNWRAP(int32_t && x, (Result<int32_t, nsresult>{42}), QM_VOID);
-
-  static_assert(std::is_same_v<decltype(x), int32_t&&>);
-
-  EXPECT_EQ(x, 42);
-}
-
-TEST(QuotaCommon_TryUnwrap, ParenDecl)
-{
-  QM_TRY_UNWRAP(
-      (auto&& [x, y]),
-      (Result<std::pair<int32_t, bool>, nsresult>{std::pair{42, true}}),
-      QM_VOID);
-
-  static_assert(std::is_same_v<decltype(x), int32_t>);
-  static_assert(std::is_same_v<decltype(y), bool>);
-
-  EXPECT_EQ(x, 42);
-  EXPECT_EQ(y, true);
-}
-
-TEST(QuotaCommon_TryReturn, Success)
-{
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&tryReturnDidNotReturn] {
-    QM_TRY_RETURN((Result<int32_t, nsresult>{42}));
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 42);
-}
-
-TEST(QuotaCommon_TryReturn, Success_nsresult)
-{
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&tryReturnDidNotReturn] {
-    QM_TRY_RETURN(NS_OK);
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isOk());
-}
-
-#ifdef DEBUG
-TEST(QuotaCommon_TryReturn, Success_CustomErr_AssertUnreachable)
-{
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&tryReturnDidNotReturn]() -> Result<int32_t, nsresult> {
-    QM_TRY_RETURN((Result<int32_t, nsresult>{42}), QM_ASSERT_UNREACHABLE);
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 42);
-}
-#endif
-
-TEST(QuotaCommon_TryReturn, Success_WithCleanup)
-{
-  bool tryReturnCleanupRan = false;
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&tryReturnCleanupRan,
-              &tryReturnDidNotReturn]() -> Result<int32_t, nsresult> {
-    QM_TRY_RETURN(
-        (Result<int32_t, nsresult>{42}), QM_PROPAGATE,
-        [&tryReturnCleanupRan](const auto&) { tryReturnCleanupRan = true; });
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryReturnCleanupRan);
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 42);
-}
-
-TEST(QuotaCommon_TryReturn, Failure_PropagateErr)
-{
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&tryReturnDidNotReturn] {
-    QM_TRY_RETURN((Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}));
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryReturn, Failure_PropagateErr_nsresult)
-{
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&tryReturnDidNotReturn] {
-    QM_TRY_RETURN(NS_ERROR_FAILURE);
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryReturn, Failure_CustomErr)
-{
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&tryReturnDidNotReturn]() -> Result<int32_t, nsresult> {
-    QM_TRY_RETURN((Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}),
-                  Err(NS_ERROR_UNEXPECTED));
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_TryReturn, Failure_WithCleanup)
-{
-  bool tryReturnCleanupRan = false;
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&tryReturnCleanupRan,
-              &tryReturnDidNotReturn]() -> Result<int32_t, nsresult> {
-    QM_TRY_RETURN((Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}),
-                  QM_PROPAGATE, [&tryReturnCleanupRan](const auto& result) {
-                    EXPECT_EQ(result, NS_ERROR_FAILURE);
-
-                    tryReturnCleanupRan = true;
-                  });
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_TRUE(tryReturnCleanupRan);
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_TryReturn, SameLine)
-{
-  // clang-format off
-  auto res1 = [] { QM_TRY_RETURN((Result<int32_t, nsresult>{42})); }(); auto res2 = []() -> Result<int32_t, nsresult> { QM_TRY_RETURN((Result<int32_t, nsresult>{42})); }();
-  // clang-format on
-
-  EXPECT_TRUE(res1.isOk());
-  EXPECT_EQ(res1.unwrap(), 42);
-  EXPECT_TRUE(res2.isOk());
-  EXPECT_EQ(res2.unwrap(), 42);
-}
-
-TEST(QuotaCommon_TryReturn, NestingMadness_Success)
-{
-  bool nestedTryReturnDidNotReturn = false;
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&nestedTryReturnDidNotReturn, &tryReturnDidNotReturn] {
-    QM_TRY_RETURN(([&nestedTryReturnDidNotReturn] {
-      QM_TRY_RETURN((Result<int32_t, nsresult>{42}));
-
-      nestedTryReturnDidNotReturn = true;
-    }()));
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(nestedTryReturnDidNotReturn);
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 42);
-}
-
-TEST(QuotaCommon_TryReturn, NestingMadness_Failure)
-{
-  bool nestedTryReturnDidNotReturn = false;
-  bool tryReturnDidNotReturn = false;
-
-  auto res = [&nestedTryReturnDidNotReturn, &tryReturnDidNotReturn] {
-    QM_TRY_RETURN(([&nestedTryReturnDidNotReturn] {
-      QM_TRY_RETURN((Result<int32_t, nsresult>{Err(NS_ERROR_FAILURE)}));
-
-      nestedTryReturnDidNotReturn = true;
-    }()));
-
-    tryReturnDidNotReturn = true;
-  }();
-
-  EXPECT_FALSE(nestedTryReturnDidNotReturn);
-  EXPECT_FALSE(tryReturnDidNotReturn);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_Fail, ReturnValue)
-{
-  bool failDidNotReturn = false;
-
-  nsresult rv = [&failDidNotReturn]() -> nsresult {
-    QM_FAIL(NS_ERROR_FAILURE);
-
-    failDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_FALSE(failDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_Fail, ReturnValue_WithCleanup)
-{
-  bool failCleanupRan = false;
-  bool failDidNotReturn = false;
-
-  nsresult rv = [&failCleanupRan, &failDidNotReturn]() -> nsresult {
-    QM_FAIL(NS_ERROR_FAILURE, [&failCleanupRan]() { failCleanupRan = true; });
-
-    failDidNotReturn = true;
-
-    return NS_OK;
-  }();
-
-  EXPECT_TRUE(failCleanupRan);
-  EXPECT_FALSE(failDidNotReturn);
-  EXPECT_EQ(rv, NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_WarnOnlyTry, Success)
-{
-  bool warnOnlyTryDidNotReturn = false;
-
-  const auto res =
-      [&warnOnlyTryDidNotReturn]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_WARNONLY_TRY(OkIf(true));
-
-    warnOnlyTryDidNotReturn = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_TRUE(warnOnlyTryDidNotReturn);
-}
-
-TEST(QuotaCommon_WarnOnlyTry, Success_WithCleanup)
-{
-  bool warnOnlyTryCleanupRan = false;
-  bool warnOnlyTryDidNotReturn = false;
-
-  const auto res =
-      [&warnOnlyTryCleanupRan,
-       &warnOnlyTryDidNotReturn]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_WARNONLY_TRY(OkIf(true), [&warnOnlyTryCleanupRan](const auto&) {
-      warnOnlyTryCleanupRan = true;
-    });
-
-    warnOnlyTryDidNotReturn = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_FALSE(warnOnlyTryCleanupRan);
-  EXPECT_TRUE(warnOnlyTryDidNotReturn);
-}
-
-TEST(QuotaCommon_WarnOnlyTry, Failure)
-{
-  bool warnOnlyTryDidNotReturn = false;
-
-  const auto res =
-      [&warnOnlyTryDidNotReturn]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_WARNONLY_TRY(OkIf(false));
-
-    warnOnlyTryDidNotReturn = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_TRUE(warnOnlyTryDidNotReturn);
-}
-
-TEST(QuotaCommon_WarnOnlyTry, Failure_WithCleanup)
-{
-  bool warnOnlyTryCleanupRan = false;
-  bool warnOnlyTryDidNotReturn = false;
-
-  const auto res =
-      [&warnOnlyTryCleanupRan,
-       &warnOnlyTryDidNotReturn]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_WARNONLY_TRY(OkIf(false), ([&warnOnlyTryCleanupRan](const auto&) {
-                      warnOnlyTryCleanupRan = true;
-                    }));
-
-    warnOnlyTryDidNotReturn = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_TRUE(warnOnlyTryCleanupRan);
-  EXPECT_TRUE(warnOnlyTryDidNotReturn);
-}
-
-TEST(QuotaCommon_WarnOnlyTryUnwrap, Success)
-{
-  bool warnOnlyTryUnwrapDidNotReturn = false;
-
-  const auto res = [&warnOnlyTryUnwrapDidNotReturn]()
-      -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_WARNONLY_TRY_UNWRAP(const auto x, (Result<int32_t, NotOk>{42}));
-    EXPECT_TRUE(x);
-    EXPECT_EQ(*x, 42);
-
-    warnOnlyTryUnwrapDidNotReturn = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_TRUE(warnOnlyTryUnwrapDidNotReturn);
-}
-
-TEST(QuotaCommon_WarnOnlyTryUnwrap, Success_WithCleanup)
-{
-  bool warnOnlyTryUnwrapCleanupRan = false;
-  bool warnOnlyTryUnwrapDidNotReturn = false;
-
-  const auto res = [&warnOnlyTryUnwrapCleanupRan,
-                    &warnOnlyTryUnwrapDidNotReturn]()
-      -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_WARNONLY_TRY_UNWRAP(const auto x, (Result<int32_t, NotOk>{42}),
-                           [&warnOnlyTryUnwrapCleanupRan](const auto&) {
-                             warnOnlyTryUnwrapCleanupRan = true;
-                           });
-    EXPECT_TRUE(x);
-    EXPECT_EQ(*x, 42);
-
-    warnOnlyTryUnwrapDidNotReturn = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_FALSE(warnOnlyTryUnwrapCleanupRan);
-  EXPECT_TRUE(warnOnlyTryUnwrapDidNotReturn);
-}
-
-TEST(QuotaCommon_WarnOnlyTryUnwrap, Failure)
-{
-  bool warnOnlyTryUnwrapDidNotReturn = false;
-
-  const auto res = [&warnOnlyTryUnwrapDidNotReturn]()
-      -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_WARNONLY_TRY_UNWRAP(const auto x,
-                           (Result<int32_t, NotOk>{Err(NotOk{})}));
-    EXPECT_FALSE(x);
-
-    warnOnlyTryUnwrapDidNotReturn = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_TRUE(warnOnlyTryUnwrapDidNotReturn);
-}
-
-TEST(QuotaCommon_WarnOnlyTryUnwrap, Failure_WithCleanup)
-{
-  bool warnOnlyTryUnwrapCleanupRan = false;
-  bool warnOnlyTryUnwrapDidNotReturn = false;
-
-  const auto res = [&warnOnlyTryUnwrapCleanupRan,
-                    &warnOnlyTryUnwrapDidNotReturn]()
-      -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_WARNONLY_TRY_UNWRAP(const auto x, (Result<int32_t, NotOk>{Err(NotOk{})}),
-                           [&warnOnlyTryUnwrapCleanupRan](const auto&) {
-                             warnOnlyTryUnwrapCleanupRan = true;
-                           });
-    EXPECT_FALSE(x);
-
-    warnOnlyTryUnwrapDidNotReturn = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_TRUE(warnOnlyTryUnwrapCleanupRan);
-  EXPECT_TRUE(warnOnlyTryUnwrapDidNotReturn);
-}
-
-TEST(QuotaCommon_OrElseWarn, Success)
-{
-  bool fallbackRun = false;
-  bool tryContinued = false;
-
-  const auto res = [&]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_TRY(QM_OR_ELSE_WARN(OkIf(true), ([&fallbackRun](const NotOk) {
-                             fallbackRun = true;
-                             return mozilla::Result<mozilla::Ok, NotOk>{
-                                 mozilla::Ok{}};
-                           })));
-
-    tryContinued = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_FALSE(fallbackRun);
-  EXPECT_TRUE(tryContinued);
-}
-
-TEST(QuotaCommon_OrElseWarn, Failure_MappedToSuccess)
-{
-  bool fallbackRun = false;
-  bool tryContinued = false;
-
-  // XXX Consider allowing to set a custom error handler, so that we can
-  // actually assert that a warning was emitted.
-  const auto res = [&]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_TRY(QM_OR_ELSE_WARN(OkIf(false), ([&fallbackRun](const NotOk) {
-                             fallbackRun = true;
-                             return mozilla::Result<mozilla::Ok, NotOk>{
-                                 mozilla::Ok{}};
-                           })));
-    tryContinued = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_TRUE(fallbackRun);
-  EXPECT_TRUE(tryContinued);
-}
-
-TEST(QuotaCommon_OrElseWarn, Failure_MappedToError)
-{
-  bool fallbackRun = false;
-  bool tryContinued = false;
-
-  // XXX Consider allowing to set a custom error handler, so that we can
-  // actually assert that a warning was emitted.
-  const auto res = [&]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_TRY(QM_OR_ELSE_WARN(OkIf(false), ([&fallbackRun](const NotOk) {
-                             fallbackRun = true;
-                             return mozilla::Result<mozilla::Ok, NotOk>{
-                                 NotOk{}};
-                           })));
-    tryContinued = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isErr());
-  EXPECT_TRUE(fallbackRun);
-  EXPECT_FALSE(tryContinued);
-}
-
-TEST(QuotaCommon_OrElseWarnIf, Success)
-{
-  bool predicateRun = false;
-  bool fallbackRun = false;
-  bool tryContinued = false;
-
-  const auto res = [&]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_TRY(QM_OR_ELSE_WARN_IF(
-        OkIf(true),
-        [&predicateRun](const NotOk) {
-          predicateRun = true;
-          return false;
-        },
-        ([&fallbackRun](const NotOk) {
-          fallbackRun = true;
-          return mozilla::Result<mozilla::Ok, NotOk>{mozilla::Ok{}};
-        })));
-
-    tryContinued = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_FALSE(predicateRun);
-  EXPECT_FALSE(fallbackRun);
-  EXPECT_TRUE(tryContinued);
-}
-
-TEST(QuotaCommon_OrElseWarnIf, Failure_PredicateReturnsFalse)
-{
-  bool predicateRun = false;
-  bool fallbackRun = false;
-  bool tryContinued = false;
-
-  const auto res = [&]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_TRY(QM_OR_ELSE_WARN_IF(
-        OkIf(false),
-        [&predicateRun](const NotOk) {
-          predicateRun = true;
-          return false;
-        },
-        ([&fallbackRun](const NotOk) {
-          fallbackRun = true;
-          return mozilla::Result<mozilla::Ok, NotOk>{mozilla::Ok{}};
-        })));
-
-    tryContinued = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isErr());
-  EXPECT_TRUE(predicateRun);
-  EXPECT_FALSE(fallbackRun);
-  EXPECT_FALSE(tryContinued);
-}
-
-TEST(QuotaCommon_OrElseWarnIf, Failure_PredicateReturnsTrue_MappedToSuccess)
-{
-  bool predicateRun = false;
-  bool fallbackRun = false;
-  bool tryContinued = false;
-
-  const auto res = [&]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_TRY(QM_OR_ELSE_WARN_IF(
-        OkIf(false),
-        [&predicateRun](const NotOk) {
-          predicateRun = true;
-          return true;
-        },
-        ([&fallbackRun](const NotOk) {
-          fallbackRun = true;
-          return mozilla::Result<mozilla::Ok, NotOk>{mozilla::Ok{}};
-        })));
-
-    tryContinued = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_TRUE(predicateRun);
-  EXPECT_TRUE(fallbackRun);
-  EXPECT_TRUE(tryContinued);
-}
-
-TEST(QuotaCommon_OrElseWarnIf, Failure_PredicateReturnsTrue_MappedToError)
-{
-  bool predicateRun = false;
-  bool fallbackRun = false;
-  bool tryContinued = false;
-
-  const auto res = [&]() -> mozilla::Result<mozilla::Ok, NotOk> {
-    QM_TRY(QM_OR_ELSE_WARN_IF(
-        OkIf(false),
-        [&predicateRun](const NotOk) {
-          predicateRun = true;
-          return true;
-        },
-        ([&fallbackRun](const NotOk) {
-          fallbackRun = true;
-          return mozilla::Result<mozilla::Ok, NotOk>{mozilla::NotOk{}};
-        })));
-
-    tryContinued = true;
-    return mozilla::Ok{};
-  }();
-
-  EXPECT_TRUE(res.isErr());
-  EXPECT_TRUE(predicateRun);
-  EXPECT_TRUE(fallbackRun);
-  EXPECT_FALSE(tryContinued);
-}
-
-TEST(QuotaCommon_OkIf, True)
-{
-  auto res = OkIf(true);
-
-  EXPECT_TRUE(res.isOk());
-}
-
-TEST(QuotaCommon_OkIf, False)
-{
-  auto res = OkIf(false);
-
-  EXPECT_TRUE(res.isErr());
-}
-
-TEST(QuotaCommon_OkToOk, Bool_True)
-{
-  auto res = OkToOk<true>(Ok());
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), true);
-}
-
-TEST(QuotaCommon_OkToOk, Bool_False)
-{
-  auto res = OkToOk<false>(Ok());
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), false);
-}
-
-TEST(QuotaCommon_OkToOk, Int_42)
-{
-  auto res = OkToOk<42>(Ok());
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 42);
-}
-
-TEST(QuotaCommon_ErrToOkOrErr, Bool_True)
-{
-  auto res = ErrToOkOrErr<NS_ERROR_FAILURE, true>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), true);
-}
-
-TEST(QuotaCommon_ErrToOkOrErr, Bool_True_Err)
-{
-  auto res = ErrToOkOrErr<NS_ERROR_FAILURE, true>(NS_ERROR_UNEXPECTED);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_ErrToOkOrErr, Bool_False)
-{
-  auto res = ErrToOkOrErr<NS_ERROR_FAILURE, false>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), false);
-}
-
-TEST(QuotaCommon_ErrToOkOrErr, Bool_False_Err)
-{
-  auto res = ErrToOkOrErr<NS_ERROR_FAILURE, false>(NS_ERROR_UNEXPECTED);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_ErrToOkOrErr, Int_42)
-{
-  auto res = ErrToOkOrErr<NS_ERROR_FAILURE, 42>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 42);
-}
-
-TEST(QuotaCommon_ErrToOkOrErr, Int_42_Err)
-{
-  auto res = ErrToOkOrErr<NS_ERROR_FAILURE, 42>(NS_ERROR_UNEXPECTED);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_ErrToOkOrErr, NsCOMPtr_nullptr)
-{
-  auto res = ErrToOkOrErr<NS_ERROR_FAILURE, nullptr, nsCOMPtr<nsISupports>>(
-      NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), nullptr);
-}
-
-TEST(QuotaCommon_ErrToOkOrErr, NsCOMPtr_nullptr_Err)
-{
-  auto res = ErrToOkOrErr<NS_ERROR_FAILURE, nullptr, nsCOMPtr<nsISupports>>(
-      NS_ERROR_UNEXPECTED);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_ErrToDefaultOkOrErr, Ok)
-{
-  auto res = ErrToDefaultOkOrErr<NS_ERROR_FAILURE, Ok>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-}
-
-TEST(QuotaCommon_ErrToDefaultOkOrErr, Ok_Err)
-{
-  auto res = ErrToDefaultOkOrErr<NS_ERROR_FAILURE, Ok>(NS_ERROR_UNEXPECTED);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_ErrToDefaultOkOrErr, NsCOMPtr)
-{
-  auto res = ErrToDefaultOkOrErr<NS_ERROR_FAILURE, nsCOMPtr<nsISupports>>(
-      NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), nullptr);
-}
-
-TEST(QuotaCommon_ErrToDefaultOkOrErr, NsCOMPtr_Err)
-{
-  auto res = ErrToDefaultOkOrErr<NS_ERROR_FAILURE, nsCOMPtr<nsISupports>>(
-      NS_ERROR_UNEXPECTED);
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED);
-}
-
-TEST(QuotaCommon_IsSpecificError, Match)
-{ EXPECT_TRUE(IsSpecificError<NS_ERROR_FAILURE>(NS_ERROR_FAILURE)); }
-
-TEST(QuotaCommon_IsSpecificError, Mismatch)
-{ EXPECT_FALSE(IsSpecificError<NS_ERROR_FAILURE>(NS_ERROR_UNEXPECTED)); }
-
-TEST(QuotaCommon_ErrToOk, Bool_True)
-{
-  auto res = ErrToOk<true>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), true);
-}
-
-TEST(QuotaCommon_ErrToOk, Bool_False)
-{
-  auto res = ErrToOk<false>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), false);
-}
-
-TEST(QuotaCommon_ErrToOk, Int_42)
-{
-  auto res = ErrToOk<42>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 42);
-}
-
-TEST(QuotaCommon_ErrToOk, NsCOMPtr_nullptr)
-{
-  auto res = ErrToOk<nullptr, nsCOMPtr<nsISupports>>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), nullptr);
-}
-
-TEST(QuotaCommon_ErrToDefaultOk, Ok)
-{
-  auto res = ErrToDefaultOk<Ok>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-}
-
-TEST(QuotaCommon_ErrToDefaultOk, NsCOMPtr)
-{
-  auto res = ErrToDefaultOk<nsCOMPtr<nsISupports>>(NS_ERROR_FAILURE);
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), nullptr);
-}
-
-class StringPairParameterized
-    : public ::testing::TestWithParam<std::pair<const char*, const char*>> {};
-
-TEST_P(StringPairParameterized, AnonymizedOriginString) {
-  const auto [in, expectedAnonymized] = GetParam();
-  const auto anonymized = AnonymizedOriginString(nsDependentCString(in));
-  EXPECT_STREQ(anonymized.get(), expectedAnonymized);
-}
-
-INSTANTIATE_TEST_CASE_P(
-    QuotaCommon, StringPairParameterized,
-    ::testing::Values(
-        // XXX Do we really want to anonymize about: origins?
-        std::pair("about:home", "about:aaaa"),
-        std::pair("https://foo.bar.com", "https://aaa.aaa.aaa"),
-        std::pair("https://foo.bar.com:8000", "https://aaa.aaa.aaa:DDDD"),
-        std::pair("file://UNIVERSAL_FILE_ORIGIN",
-                  "file://aaaaaaaaa_aaaa_aaaaaa")));
-
-TEST(QuotaCommon_ToResultGet, Lambda_NoInput)
-{
-  auto res = ToResultGet<int32_t>([](nsresult* aRv) -> int32_t {
-    *aRv = NS_OK;
-    return 42;
-  });
-
-  static_assert(std::is_same_v<decltype(res), Result<int32_t, nsresult>>);
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 42);
-}
-
-TEST(QuotaCommon_ToResultGet, Lambda_NoInput_Err)
-{
-  auto res = ToResultGet<int32_t>([](nsresult* aRv) -> int32_t {
-    *aRv = NS_ERROR_FAILURE;
-    return -1;
-  });
-
-  static_assert(std::is_same_v<decltype(res), Result<int32_t, nsresult>>);
-
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE);
-}
-
-TEST(QuotaCommon_ToResultGet, Lambda_WithInput)
-{
-  auto res = ToResultGet<int32_t>(
-      [](int32_t aValue, nsresult* aRv) -> int32_t {
-        *aRv = NS_OK;
-        return aValue * 2;
-      },
-      42);
-
-  static_assert(std::is_same_v<decltype(res), Result<int32_t, nsresult>>);
-
-  EXPECT_TRUE(res.isOk());
-  EXPECT_EQ(res.unwrap(), 84);
-}
-
-TEST(QuotaCommon_ToResultGet, Lambda_WithInput_Err)
-{
-  auto res = ToResultGet<int32_t>(
-      [](int32_t aValue, nsresult* aRv) -> int32_t {
-        *aRv = NS_ERROR_FAILURE;
-        return -1;
-      },
-      42);
-
-  static_assert(std::is_same_v<decltype(res), Result<int32_t, nsresult>>);
-
-  EXPECT_TRUE(res.isErr());
-  EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE);
-}
-
-// BEGIN COPY FROM mfbt/tests/TestResult.cpp
-struct Failed {};
-
-static GenericErrorResult<Failed> Fail() { return Err(Failed()); }
-
-static Result<Ok, Failed> Task1(bool pass) {
-  if (!pass) {
-    return Fail();  // implicit conversion from GenericErrorResult to Result
-  }
-  return Ok();
-}
-// END COPY FROM mfbt/tests/TestResult.cpp
-
-static Result<bool, Failed> Condition(bool aNoError, bool aResult) {
-  return Task1(aNoError).map([aResult](auto) { return aResult; });
-}
-
-TEST(QuotaCommon_CollectWhileTest, NoFailures)
-{
-  const size_t loopCount = 5;
-  size_t conditionExecutions = 0;
-  size_t bodyExecutions = 0;
-  auto result = CollectWhile(
-      [&conditionExecutions] {
-        ++conditionExecutions;
-        return Condition(true, conditionExecutions <= loopCount);
-      },
-      [&bodyExecutions] {
-        ++bodyExecutions;
-        return Task1(true);
-      });
-  static_assert(std::is_same_v<decltype(result), Result<Ok, Failed>>);
-  MOZ_RELEASE_ASSERT(result.isOk());
-  MOZ_RELEASE_ASSERT(loopCount == bodyExecutions);
-  MOZ_RELEASE_ASSERT(1 + loopCount == conditionExecutions);
-}
-
-TEST(QuotaCommon_CollectWhileTest, BodyFailsImmediately)
-{
-  size_t conditionExecutions = 0;
-  size_t bodyExecutions = 0;
-  auto result = CollectWhile(
-      [&conditionExecutions] {
-        ++conditionExecutions;
-        return Condition(true, true);
-      },
-      [&bodyExecutions] {
-        ++bodyExecutions;
-        return Task1(false);
-      });
-  static_assert(std::is_same_v<decltype(result), Result<Ok, Failed>>);
-  MOZ_RELEASE_ASSERT(result.isErr());
-  MOZ_RELEASE_ASSERT(1 == bodyExecutions);
-  MOZ_RELEASE_ASSERT(1 == conditionExecutions);
-}
-
-TEST(QuotaCommon_CollectWhileTest, BodyFailsOnSecondExecution)
-{
-  size_t conditionExecutions = 0;
-  size_t bodyExecutions = 0;
-  auto result = CollectWhile(
-      [&conditionExecutions] {
-        ++conditionExecutions;
-        return Condition(true, true);
-      },
-      [&bodyExecutions] {
-        ++bodyExecutions;
-        return Task1(bodyExecutions < 2);
-      });
-  static_assert(std::is_same_v<decltype(result), Result<Ok, Failed>>);
-  MOZ_RELEASE_ASSERT(result.isErr());
-  MOZ_RELEASE_ASSERT(2 == bodyExecutions);
-  MOZ_RELEASE_ASSERT(2 == conditionExecutions);
-}
-
-TEST(QuotaCommon_CollectWhileTest, ConditionFailsImmediately)
-{
-  size_t conditionExecutions = 0;
-  size_t bodyExecutions = 0;
-  auto result = CollectWhile(
-      [&conditionExecutions] {
-        ++conditionExecutions;
-        return Condition(false, true);
-      },
-      [&bodyExecutions] {
-        ++bodyExecutions;
-        return Task1(true);
-      });
-  static_assert(std::is_same_v<decltype(result), Result<Ok, Failed>>);
-  MOZ_RELEASE_ASSERT(result.isErr());
-  MOZ_RELEASE_ASSERT(0 == bodyExecutions);
-  MOZ_RELEASE_ASSERT(1 == conditionExecutions);
-}
-
-TEST(QuotaCommon_CollectWhileTest, ConditionFailsOnSecondExecution)
-{
-  size_t conditionExecutions = 0;
-  size_t bodyExecutions = 0;
-  auto result = CollectWhile(
-      [&conditionExecutions] {
-        ++conditionExecutions;
-        return Condition(conditionExecutions < 2, true);
-      },
-      [&bodyExecutions] {
-        ++bodyExecutions;
-        return Task1(true);
-      });
-  static_assert(std::is_same_v<decltype(result), Result<Ok, Failed>>);
-  MOZ_RELEASE_ASSERT(result.isErr());
-  MOZ_RELEASE_ASSERT(1 == bodyExecutions);
-  MOZ_RELEASE_ASSERT(2 == conditionExecutions);
-}
-
-TEST(QuotaCommon_CollectEachInRange, Success)
-{
-  size_t bodyExecutions = 0;
-  const auto result = CollectEachInRange(
-      std::array<int, 5>{{1, 2, 3, 4, 5}},
-      [&bodyExecutions](const int val) -> Result<Ok, nsresult> {
-        ++bodyExecutions;
-        return Ok{};
-      });
-
-  MOZ_RELEASE_ASSERT(result.isOk());
-  MOZ_RELEASE_ASSERT(5 == bodyExecutions);
-}
-
-TEST(QuotaCommon_CollectEachInRange, FailureShortCircuit)
-{
-  size_t bodyExecutions = 0;
-  const auto result = CollectEachInRange(
-      std::array<int, 5>{{1, 2, 3, 4, 5}},
-      [&bodyExecutions](const int val) -> Result<Ok, nsresult> {
-        ++bodyExecutions;
-        return val == 3 ? Err(NS_ERROR_FAILURE) : Result<Ok, nsresult>{Ok{}};
-      });
-
-  MOZ_RELEASE_ASSERT(result.isErr());
-  MOZ_RELEASE_ASSERT(NS_ERROR_FAILURE == result.inspectErr());
-  MOZ_RELEASE_ASSERT(3 == bodyExecutions);
-}
-
-TEST(QuotaCommon_ReduceEach, Success)
-{
-  const auto result = ReduceEach(
-      [i = int{0}]() mutable -> Result<int, Failed> {
-        if (i < 5) {
-          return ++i;
-        }
-        return 0;
-      },
-      0, [](int val, int add) -> Result<int, Failed> { return val + add; });
-  static_assert(std::is_same_v<decltype(result), const Result<int, Failed>>);
-
-  MOZ_RELEASE_ASSERT(result.isOk());
-  MOZ_RELEASE_ASSERT(15 == result.inspect());
-}
-
-TEST(QuotaCommon_ReduceEach, StepError)
-{
-  const auto result = ReduceEach(
-      [i = int{0}]() mutable -> Result<int, Failed> {
-        if (i < 5) {
-          return ++i;
-        }
-        return 0;
-      },
-      0,
-      [](int val, int add) -> Result<int, Failed> {
-        if (val > 2) {
-          return Err(Failed{});
-        }
-        return val + add;
-      });
-  static_assert(std::is_same_v<decltype(result), const Result<int, Failed>>);
-
-  MOZ_RELEASE_ASSERT(result.isErr());
-}
-
-TEST(QuotaCommon_ReduceEach, GeneratorError)
-{
-  size_t generatorExecutions = 0;
-  const auto result = ReduceEach(
-      [i = int{0}, &generatorExecutions]() mutable -> Result<int, Failed> {
-        ++generatorExecutions;
-        if (i < 1) {
-          return ++i;
-        }
-        return Err(Failed{});
-      },
-      0,
-      [](int val, int add) -> Result<int, Failed> {
-        if (val > 2) {
-          return Err(Failed{});
-        }
-        return val + add;
-      });
-  static_assert(std::is_same_v<decltype(result), const Result<int, Failed>>);
-
-  MOZ_RELEASE_ASSERT(result.isErr());
-  MOZ_RELEASE_ASSERT(2 == generatorExecutions);
-}
-
-TEST(QuotaCommon_Reduce, Success)
-{
-  const auto range = std::vector{0, 1, 2, 3, 4, 5};
-  const auto result = Reduce(
-      range, 0, [](int val, Maybe<const int&> add) -> Result<int, Failed> {
-        return val + add.ref();
-      });
-  static_assert(std::is_same_v<decltype(result), const Result<int, Failed>>);
-
-  MOZ_RELEASE_ASSERT(result.isOk());
-  MOZ_RELEASE_ASSERT(15 == result.inspect());
-}
-
-TEST(QuotaCommon_ScopedLogExtraInfo, AddAndRemove)
+TEST(DOM_Quota_ScopedLogExtraInfo, AddAndRemove)
 {
   static constexpr auto text = "foo"_ns;
 
   {
     const auto extraInfo =
         ScopedLogExtraInfo{ScopedLogExtraInfo::kTagQuery, text};
 
 #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
@@ -1833,17 +27,17 @@ TEST(QuotaCommon_ScopedLogExtraInfo, Add
 
 #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
   const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
 
   EXPECT_EQ(0u, extraInfoMap.count(ScopedLogExtraInfo::kTagQuery));
 #endif
 }
 
-TEST(QuotaCommon_ScopedLogExtraInfo, Nested)
+TEST(DOM_Quota_ScopedLogExtraInfo, Nested)
 {
   static constexpr auto text = "foo"_ns;
   static constexpr auto nestedText = "bar"_ns;
 
   {
     const auto extraInfo =
         ScopedLogExtraInfo{ScopedLogExtraInfo::kTagQuery, text};
 
@@ -1864,176 +58,8 @@ TEST(QuotaCommon_ScopedLogExtraInfo, Nes
   }
 
 #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED
   const auto& extraInfoMap = ScopedLogExtraInfo::GetExtraInfoMap();
 
   EXPECT_EQ(0u, extraInfoMap.count(ScopedLogExtraInfo::kTagQuery));
 #endif
 }
-
-TEST(QuotaCommon_CallWithDelayedRetriesIfAccessDenied, NoFailures)
-{
-  uint32_t tries = 0;
-
-  auto res = CallWithDelayedRetriesIfAccessDenied(
-      [&tries]() -> Result<Ok, nsresult> {
-        ++tries;
-        return Ok{};
-      },
-      10, 2);
-
-  EXPECT_EQ(tries, 1u);
-  EXPECT_TRUE(res.isOk());
-}
-
-TEST(QuotaCommon_CallWithDelayedRetriesIfAccessDenied, PermanentFailures)
-{
-  uint32_t tries = 0;
-
-  auto res = CallWithDelayedRetriesIfAccessDenied(
-      [&tries]() -> Result<Ok, nsresult> {
-        ++tries;
-        return Err(NS_ERROR_FILE_IS_LOCKED);
-      },
-      10, 2);
-
-  EXPECT_EQ(tries, 11u);
-  EXPECT_TRUE(res.isErr());
-}
-
-TEST(QuotaCommon_CallWithDelayedRetriesIfAccessDenied, FailuresAndSuccess)
-{
-  uint32_t tries = 0;
-
-  auto res = CallWithDelayedRetriesIfAccessDenied(
-      [&tries]() -> Result<Ok, nsresult> {
-        if (++tries == 5) {
-          return Ok{};
-        }
-        return Err(NS_ERROR_FILE_ACCESS_DENIED);
-      },
-      10, 2);
-
-  EXPECT_EQ(tries, 5u);
-  EXPECT_TRUE(res.isOk());
-}
-
-TEST(QuotaCommon_MakeSourceFileRelativePath, ThisSourceFile)
-{
-  static constexpr auto thisSourceFileRelativePath =
-      "dom/quota/test/gtest/TestQuotaCommon.cpp"_ns;
-
-  const nsCString sourceFileRelativePath{
-      mozilla::dom::quota::detail::MakeSourceFileRelativePath(
-          nsLiteralCString(__FILE__))};
-
-  EXPECT_STREQ(sourceFileRelativePath.get(), thisSourceFileRelativePath.get());
-}
-
-static nsCString MakeTreePath(const nsACString& aBasePath,
-                              const nsACString& aRelativePath) {
-  nsCString path{aBasePath};
-
-  path.Append("/");
-  path.Append(aRelativePath);
-
-  return path;
-}
-
-static nsCString MakeSourceTreePath(const nsACString& aRelativePath) {
-  return MakeTreePath(mozilla::dom::quota::detail::GetSourceTreeBase(),
-                      aRelativePath);
-}
-
-static nsCString MakeObjdirDistIncludeTreePath(
-    const nsACString& aRelativePath) {
-  return MakeTreePath(
-      mozilla::dom::quota::detail::GetObjdirDistIncludeTreeBase(),
-      aRelativePath);
-}
-
-TEST(QuotaCommon_MakeSourceFileRelativePath, DomQuotaSourceFile)
-{
-  static constexpr auto domQuotaSourceFileRelativePath =
-      "dom/quota/ActorsParent.cpp"_ns;
-
-  const nsCString sourceFileRelativePath{
-      mozilla::dom::quota::detail::MakeSourceFileRelativePath(
-          MakeSourceTreePath(domQuotaSourceFileRelativePath))};
-
-  EXPECT_STREQ(sourceFileRelativePath.get(),
-               domQuotaSourceFileRelativePath.get());
-}
-
-TEST(QuotaCommon_MakeSourceFileRelativePath, DomQuotaSourceFile_Exported)
-{
-  static constexpr auto mozillaDomQuotaSourceFileRelativePath =
-      "mozilla/dom/quota/QuotaCommon.h"_ns;
-
-  static constexpr auto domQuotaSourceFileRelativePath =
-      "dom/quota/QuotaCommon.h"_ns;
-
-  const nsCString sourceFileRelativePath{
-      mozilla::dom::quota::detail::MakeSourceFileRelativePath(
-          MakeObjdirDistIncludeTreePath(
-              mozillaDomQuotaSourceFileRelativePath))};
-
-  EXPECT_STREQ(sourceFileRelativePath.get(),
-               domQuotaSourceFileRelativePath.get());
-}
-
-TEST(QuotaCommon_MakeSourceFileRelativePath, DomIndexedDBSourceFile)
-{
-  static constexpr auto domIndexedDBSourceFileRelativePath =
-      "dom/indexedDB/ActorsParent.cpp"_ns;
-
-  const nsCString sourceFileRelativePath{
-      mozilla::dom::quota::detail::MakeSourceFileRelativePath(
-          MakeSourceTreePath(domIndexedDBSourceFileRelativePath))};
-
-  EXPECT_STREQ(sourceFileRelativePath.get(),
-               domIndexedDBSourceFileRelativePath.get());
-}
-
-TEST(QuotaCommon_MakeSourceFileRelativePath,
-     DomLocalstorageSourceFile_Exported_Mapped)
-{
-  static constexpr auto mozillaDomSourceFileRelativePath =
-      "mozilla/dom/LocalStorageCommon.h"_ns;
-
-  static constexpr auto domLocalstorageSourceFileRelativePath =
-      "dom/localstorage/LocalStorageCommon.h"_ns;
-
-  const nsCString sourceFileRelativePath{
-      mozilla::dom::quota::detail::MakeSourceFileRelativePath(
-          MakeObjdirDistIncludeTreePath(mozillaDomSourceFileRelativePath))};
-
-  EXPECT_STREQ(sourceFileRelativePath.get(),
-               domLocalstorageSourceFileRelativePath.get());
-}
-
-TEST(QuotaCommon_MakeSourceFileRelativePath, NonDomSourceFile)
-{
-  static constexpr auto nonDomSourceFileRelativePath =
-      "storage/mozStorageService.cpp"_ns;
-
-  const nsCString sourceFileRelativePath{
-      mozilla::dom::quota::detail::MakeSourceFileRelativePath(
-          MakeSourceTreePath(nonDomSourceFileRelativePath))};
-
-  EXPECT_STREQ(sourceFileRelativePath.get(),
-               nonDomSourceFileRelativePath.get());
-}
-
-TEST(QuotaCommon_MakeSourceFileRelativePath, OtherSourceFile)
-{
-  constexpr auto otherSourceFilePath = "/foo/bar/Test.cpp"_ns;
-  const nsCString sourceFileRelativePath{
-      mozilla::dom::quota::detail::MakeSourceFileRelativePath(
-          otherSourceFilePath)};
-
-  EXPECT_STREQ(sourceFileRelativePath.get(), "Test.cpp");
-}
-
-#ifdef __clang__
-#  pragma clang diagnostic pop
-#endif
--- a/dom/quota/test/gtest/moz.build
+++ b/dom/quota/test/gtest/moz.build
@@ -7,16 +7,17 @@
 UNIFIED_SOURCES = [
     "TestCheckedUnsafePtr.cpp",
     "TestEncryptedStream.cpp",
     "TestFlatten.cpp",
     "TestPersistenceType.cpp",
     "TestQMResult.cpp",
     "TestQuotaCommon.cpp",
     "TestQuotaManager.cpp",
+    "TestScopedLogExtraInfo.cpp",
     "TestUsageInfo.cpp",
 ]
 
 include("/ipc/chromium/chromium-config.mozbuild")
 
 FINAL_LIBRARY = "xul-gtest"
 
 LOCAL_INCLUDES += [
--- a/dom/script/ScriptLoadRequest.h
+++ b/dom/script/ScriptLoadRequest.h
@@ -96,20 +96,18 @@ class ScriptLoadRequest
   // PreloaderBase
   static void PrioritizeAsPreload(nsIChannel* aChannel);
   virtual void PrioritizeAsPreload() override;
 
   bool IsModuleRequest() const { return mKind == ScriptKind::eModule; }
 
   ModuleLoadRequest* AsModuleRequest();
 
-#ifdef MOZ_GECKO_PROFILER
   TimeStamp mOffThreadParseStartTime;
   TimeStamp mOffThreadParseStopTime;
-#endif
 
   void FireScriptAvailable(nsresult aResult) {
     bool isInlineClassicScript = mIsInline && !IsModuleRequest();
     GetScriptElement()->ScriptAvailable(aResult, GetScriptElement(),
                                         isInlineClassicScript, mURI, mLineNo);
   }
   void FireScriptEvaluated(nsresult aResult) {
     GetScriptElement()->ScriptEvaluated(aResult, GetScriptElement(), mIsInline);
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -2411,17 +2411,16 @@ NotifyOffThreadScriptLoadCompletedRunnab
     NS_ReleaseOnMainThread(
         "NotifyOffThreadScriptLoadCompletedRunnable::mLoader",
         mLoader.forget());
   }
 }
 
 static void GetProfilerLabelForRequest(ScriptLoadRequest* aRequest,
                                        nsACString& aOutString) {
-#ifdef MOZ_GECKO_PROFILER
   if (!profiler_is_active()) {
     aOutString.Append("<script> element");
     return;
   }
   aOutString.Append("<script");
   if (aRequest->IsAsyncScript()) {
     aOutString.Append(" async");
   } else if (aRequest->IsDeferredScript()) {
@@ -2447,33 +2446,29 @@ static void GetProfilerLabelForRequest(S
       aOutString.Append("> inline (dynamically created) in ");
     }
     aOutString.Append(url);
   } else {
     aOutString.Append(" src=\"");
     aOutString.Append(url);
     aOutString.Append("\">");
   }
-#else
-  aOutString.Append("<script> element");
-#endif
 }
 
 NS_IMETHODIMP
 NotifyOffThreadScriptLoadCompletedRunnable::Run() {
   MOZ_ASSERT(NS_IsMainThread());
 
   // We want these to be dropped on the main thread, once we return from this
   // function.
   RefPtr<ScriptLoadRequest> request = std::move(mRequest);
 
   // Runnable pointer should have been cleared in the offthread callback.
   MOZ_ASSERT(!request->mRunnable);
 
-#ifdef MOZ_GECKO_PROFILER
   if (profiler_is_active()) {
     ProfilerString8View scriptSourceString;
     if (request->IsTextSource()) {
       scriptSourceString = "ScriptCompileOffThread";
     } else {
       MOZ_ASSERT(request->IsBytecode());
       scriptSourceString = "BytecodeDecodeOffThread";
     }
@@ -2481,17 +2476,16 @@ NotifyOffThreadScriptLoadCompletedRunnab
     nsAutoCString profilerLabelString;
     GetProfilerLabelForRequest(request, profilerLabelString);
     PROFILER_MARKER_TEXT(
         scriptSourceString, JS,
         MarkerTiming::Interval(request->mOffThreadParseStartTime,
                                request->mOffThreadParseStopTime),
         profilerLabelString);
   }
-#endif
 
   RefPtr<ScriptLoader> loader = std::move(mLoader);
 
   // Request was already cancelled at some earlier point.
   if (!request->mOffThreadToken) {
     return NS_OK;
   }
 
@@ -2499,20 +2493,18 @@ NotifyOffThreadScriptLoadCompletedRunnab
 }
 
 static void OffThreadScriptLoaderCallback(JS::OffThreadToken* aToken,
                                           void* aCallbackData) {
   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> aRunnable = dont_AddRef(
       static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData));
   MOZ_ASSERT(aRunnable.get() == aRunnable->GetScriptLoadRequest()->mRunnable);
 
-#ifdef MOZ_GECKO_PROFILER
   aRunnable->GetScriptLoadRequest()->mOffThreadParseStopTime =
       TimeStamp::NowUnfuzzed();
-#endif
 
   LogRunnable::Run run(aRunnable);
 
   aRunnable->SetToken(aToken);
 
   // If mRunnable was cleared then request was canceled so do nothing.
   if (!aRunnable->GetScriptLoadRequest()->mRunnable.exchange(nullptr)) {
     return;
@@ -2578,19 +2570,17 @@ nsresult ScriptLoader::AttemptAsyncScrip
 
   RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> runnable =
       new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
 
   // Emulate dispatch.  CompileOffThreadModule will call
   // OffThreadScriptLoaderCallback were we will emulate run.
   LogRunnable::LogDispatch(runnable);
 
-#ifdef MOZ_GECKO_PROFILER
   aRequest->mOffThreadParseStartTime = TimeStamp::NowUnfuzzed();
-#endif
 
   // Save the runnable so it can be properly cleared during cancellation.
   aRequest->mRunnable = runnable.get();
   auto signalOOM =
       mozilla::MakeScopeExit([&aRequest]() { aRequest->mRunnable = nullptr; });
 
   if (aRequest->IsModuleRequest()) {
     MOZ_ASSERT(aRequest->IsTextSource());
@@ -3130,20 +3120,18 @@ nsresult ScriptLoader::EvaluateScript(Sc
     context = scriptGlobal->GetScriptContext();
     if (!context) {
       return NS_ERROR_FAILURE;
     }
 
     globalObject = scriptGlobal;
   }
 
-#ifdef MOZ_GECKO_PROFILER
   nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
   nsIDocShell* docShell = window ? window->GetDocShell() : nullptr;
-#endif
   nsAutoCString profilerLabelString;
   GetProfilerLabelForRequest(aRequest, profilerLabelString);
 
   // Update our current script
   // This must be destroyed after destroying nsAutoMicroTask, see:
   // https://bugzilla.mozilla.org/show_bug.cgi?id=1620505#c4
   nsIScriptElement* currentScript =
       aRequest->IsModuleRequest() ? nullptr : aRequest->GetScriptElement();
--- a/gfx/layers/ProfilerScreenshots.cpp
+++ b/gfx/layers/ProfilerScreenshots.cpp
@@ -25,17 +25,16 @@ ProfilerScreenshots::~ProfilerScreenshot
 bool ProfilerScreenshots::IsEnabled() {
   return profiler_feature_active(ProfilerFeature::Screenshots);
 }
 
 void ProfilerScreenshots::SubmitScreenshot(
     uintptr_t aWindowIdentifier, const gfx::IntSize& aOriginalSize,
     const IntSize& aScaledSize, const TimeStamp& aTimeStamp,
     const std::function<bool(DataSourceSurface*)>& aPopulateSurface) {
-#ifdef MOZ_GECKO_PROFILER
   RefPtr<DataSourceSurface> backingSurface = TakeNextSurface();
   if (!backingSurface) {
     return;
   }
 
   MOZ_RELEASE_ASSERT(aScaledSize <= backingSurface->GetSize());
 
   bool succeeded = aPopulateSurface(backingSurface);
@@ -104,17 +103,16 @@ void ProfilerScreenshots::SubmitScreensh
                  MarkerTiming::InstantAt(timeStamp)},
                 ScreenshotMarker{}, dataURL, originalSize, windowIdentifier);
           }
         }
 
         // Return backingSurface back to the surface pool.
         self->ReturnSurface(backingSurface);
       }));
-#endif
 }
 
 already_AddRefed<DataSourceSurface> ProfilerScreenshots::TakeNextSurface() {
   MutexAutoLock mon(mMutex);
   if (!mAvailableSurfaces.IsEmpty()) {
     RefPtr<DataSourceSurface> surf = mAvailableSurfaces[0];
     mAvailableSurfaces.RemoveElementAt(0);
     return surf.forget();
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -79,17 +79,16 @@ static void DrawLayerInfo(const RenderTa
 
   IntPoint topLeft = visibleRegion.GetBounds().ToUnknownRect().TopLeft();
   aManager->GetTextRenderer()->RenderText(
       aManager->GetCompositor(), ss.str().c_str(), topLeft,
       aLayer->GetEffectiveTransform(), 16, maxWidth);
 }
 
 static void PrintUniformityInfo(Layer* aLayer) {
-#if defined(MOZ_GECKO_PROFILER)
   if (!profiler_thread_is_being_profiled()) {
     return;
   }
 
   // Don't want to print a log for smaller layers
   if (aLayer->GetLocalVisibleRegion().GetBounds().Width() < 300 ||
       aLayer->GetLocalVisibleRegion().GetBounds().Height() < 300) {
     return;
@@ -127,17 +126,16 @@ static void PrintUniformityInfo(Layer* a
       schema.AddKeyLabelFormat("y", "Y", MS::Format::integer);
       return schema;
     }
   };
 
   profiler_add_marker("LayerTranslation", geckoprofiler::category::GRAPHICS, {},
                       LayerTranslationMarker{},
                       WrapProfileBufferRawPointer(aLayer), translation);
-#endif
 }
 
 static Maybe<gfx::Polygon> SelectLayerGeometry(
     const Maybe<gfx::Polygon>& aParentGeometry,
     const Maybe<gfx::Polygon>& aChildGeometry) {
   // Both the parent and the child layer were split.
   if (aParentGeometry && aChildGeometry) {
     return Some(aParentGeometry->ClipPolygon(*aChildGeometry));
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -2094,17 +2094,16 @@ already_AddRefed<IAPZCTreeManager> Compo
   }
   LayerTreeState* lts = &cit->second;
 
   RefPtr<IAPZCTreeManager> apzctm =
       lts->mParent ? lts->mParent->mApzcTreeManager.get() : nullptr;
   return apzctm.forget();
 }
 
-#if defined(MOZ_GECKO_PROFILER)
 static void InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (profiler_thread_is_being_profiled()) {
     // Tracks when a vsync occurs according to the HardwareComposer.
     struct VsyncMarker {
       static constexpr mozilla::Span<const char> MarkerTypeName() {
         return mozilla::MakeStringSpan("VsyncTimestamp");
       }
@@ -2117,29 +2116,26 @@ static void InsertVsyncProfilerMarker(Ti
         return schema;
       }
     };
     profiler_add_marker("VsyncTimestamp", geckoprofiler::category::GRAPHICS,
                         MarkerTiming::InstantAt(aVsyncTimestamp),
                         VsyncMarker{});
   }
 }
-#endif
 
 /*static */
 void CompositorBridgeParent::PostInsertVsyncProfilerMarker(
     TimeStamp aVsyncTimestamp) {
-#if defined(MOZ_GECKO_PROFILER)
   // Called in the vsync thread
   if (profiler_is_active() && CompositorThreadHolder::IsActive()) {
     CompositorThread()->Dispatch(
         NewRunnableFunction("InsertVsyncProfilerMarkerRunnable",
                             InsertVsyncProfilerMarker, aVsyncTimestamp));
   }
-#endif
 }
 
 widget::PCompositorWidgetParent*
 CompositorBridgeParent::AllocPCompositorWidgetParent(
     const CompositorWidgetInitData& aInitData) {
 #if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
   if (mWidget) {
     // Should not create two widgets on the same compositor.
@@ -2528,17 +2524,16 @@ int32_t RecordContentFrameTime(
     const TimeStamp& aTxnStart, const VsyncId& aCompositeId,
     const TimeStamp& aCompositeEnd, const TimeDuration& aFullPaintTime,
     const TimeDuration& aVsyncRate, bool aContainsSVGGroup,
     bool aRecordUploadStats, wr::RendererStats* aStats /* = nullptr */) {
   double latencyMs = (aCompositeEnd - aTxnStart).ToMilliseconds();
   double latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
   int32_t fracLatencyNorm = lround(latencyNorm * 100.0);
 
-#ifdef MOZ_GECKO_PROFILER
   if (profiler_can_accept_markers()) {
     struct ContentFrameMarker {
       static constexpr Span<const char> MarkerTypeName() {
         return MakeStringSpan("CONTENT_FRAME_TIME");
       }
       static void StreamJSONMarkerData(
           baseprofiler::SpliceableJSONWriter& aWriter) {}
       static MarkerSchema MarkerTypeDisplay() {
@@ -2548,17 +2543,16 @@ int32_t RecordContentFrameTime(
         return schema;
       }
     };
 
     profiler_add_marker("CONTENT_FRAME_TIME", geckoprofiler::category::GRAPHICS,
                         MarkerTiming::Interval(aTxnStart, aCompositeEnd),
                         ContentFrameMarker{});
   }
-#endif
 
   Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm);
 
   if (!(aTxnId == VsyncId()) && aVsyncStart) {
     latencyMs = (aCompositeEnd - aVsyncStart).ToMilliseconds();
     latencyNorm = latencyMs / aVsyncRate.ToMilliseconds();
     fracLatencyNorm = lround(latencyNorm * 100.0);
     int32_t result = fracLatencyNorm;
--- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
@@ -33,20 +33,18 @@
 #include "mozilla/mozalloc.h"  // for operator new, etc
 #include "nsDebug.h"           // for NS_ASSERTION, etc
 #include "nsTArray.h"          // for nsTArray
 #include "nsXULAppAPI.h"       // for XRE_GetIOMessageLoop
 #include "mozilla/Unused.h"
 #include "mozilla/StaticPrefs_dom.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
-#ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/BaseProfilerMarkerTypes.h"
-#  include "GeckoProfiler.h"
-#endif
+#include "mozilla/BaseProfilerMarkerTypes.h"
+#include "GeckoProfiler.h"
 
 namespace mozilla {
 
 namespace layers {
 
 // defined in CompositorBridgeParent.cpp
 typedef std::map<LayersId, CompositorBridgeParent::LayerTreeState> LayerTreeMap;
 extern LayerTreeMap sIndirectLayerTrees;
@@ -364,24 +362,22 @@ void ContentCompositorBridgeParent::Shad
   if (aLayerTree->ShouldParentObserveEpoch()) {
     // Note that we send this through the window compositor, since this needs
     // to reach the widget owning the tab.
     Unused << state->mParent->SendObserveLayersUpdate(
         id, aLayerTree->GetChildEpoch(), true);
   }
 
   auto endTime = TimeStamp::Now();
-#ifdef MOZ_GECKO_PROFILER
   if (profiler_can_accept_markers()) {
     profiler_add_marker(
         "CONTENT_FULL_PAINT_TIME", geckoprofiler::category::GRAPHICS,
         MarkerTiming::Interval(aInfo.transactionStart(), endTime),
         baseprofiler::markers::ContentBuildMarker{});
   }
-#endif
   Telemetry::Accumulate(
       Telemetry::CONTENT_FULL_PAINT_TIME,
       static_cast<uint32_t>(
           (endTime - aInfo.transactionStart()).ToMilliseconds()));
 
   RegisterPayloads(aLayerTree, aInfo.payload());
 
   aLayerTree->SetPendingTransactionId(
--- a/gfx/tests/reftest/reftest.list
+++ b/gfx/tests/reftest/reftest.list
@@ -24,9 +24,9 @@ fuzzy-if(!useDrawSnapshot&&webrender,2-7
 == bug1523410-translate-scale-snap.html bug1523410-translate-scale-snap-ref.html
 == 1523080.html 1523080-ref.html
 == 1616444-same-color-different-paths.html 1616444-same-color-different-paths-ref.html
 skip-if(!asyncPan||!webrender||Android) fuzzy-if(winWidget,54-94,2713-3419) fuzzy-if(cocoaWidget,24-24,1190-1200) pref(apz.allow_zooming,true) == picture-caching-on-async-zoom.html picture-caching-on-async-zoom.html?ref
 pref(apz.allow_zooming,true) fails-if(useDrawSnapshot) == 1662062-1-no-blurry.html 1662062-1-ref.html
 # Bug 1715676: nsBulletFrame has been removed and the new rendering does not use PushRoundedRect that this test is for:
 # == 1681610.html 1681610-ref.html
 skip-if(!webrender||geckoview) fuzzy-if(!useDrawSnapshot,0-255,0-667) fuzzy-if(useDrawSnapshot,0-255,0-3601) == 1687157-1.html 1687157-1-ref.html
-skip-if(!webrender) fuzzy-if(webrender,64-99,512-520) == 1696439-1.html 1696439-1-ref.html
+skip-if(!webrender) fuzzy-if(webrender,64-99,512-523) == 1696439-1.html 1696439-1-ref.html
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -2243,16 +2243,19 @@ gfxFont* gfxFontGroup::GetDefaultFont() 
     MOZ_CRASH_UNSAFE(msg);
   }
 
   return mDefaultFont.get();
 }
 
 gfxFont* gfxFontGroup::GetFirstValidFont(uint32_t aCh,
                                          StyleGenericFontFamily* aGeneric) {
+  // Ensure cached font instances are valid.
+  CheckForUpdatedPlatformList();
+
   uint32_t count = mFonts.Length();
   bool loading = false;
   for (uint32_t i = 0; i < count; ++i) {
     FamilyFace& ff = mFonts[i];
     if (ff.IsInvalid()) {
       continue;
     }
 
--- a/gfx/webrender_bindings/RenderCompositorEGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorEGL.cpp
@@ -197,18 +197,28 @@ bool RenderCompositorEGL::Resume() {
 
 bool RenderCompositorEGL::IsPaused() { return mEGLSurface == EGL_NO_SURFACE; }
 
 gl::GLContext* RenderCompositorEGL::gl() const {
   return RenderThread::Get()->SingletonGL();
 }
 
 bool RenderCompositorEGL::MakeCurrent() {
-  gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
-  return gl()->MakeCurrent();
+  const auto& gle = gl::GLContextEGL::Cast(gl());
+
+  gle->SetEGLSurfaceOverride(mEGLSurface);
+  bool ok = gl()->MakeCurrent();
+  if (!gl()->IsGLES() && ok && mEGLSurface != EGL_NO_SURFACE) {
+    // If we successfully made a surface current, set the draw buffer
+    // appropriately. It's not well-defined by the EGL spec whether
+    // eglMakeCurrent should do this automatically. See bug 1646135.
+    gl()->fDrawBuffer(gl()->IsDoubleBuffered() ? LOCAL_GL_BACK
+                                               : LOCAL_GL_FRONT);
+  }
+  return ok;
 }
 
 void RenderCompositorEGL::DestroyEGLSurface() {
   const auto& gle = gl::GLContextEGL::Cast(gl());
   const auto& egl = gle->mEgl;
 
   // Release EGLSurface of back buffer before calling ResizeBuffers().
   if (mEGLSurface) {
--- a/gfx/wr/webrender/src/prim_store/text_run.rs
+++ b/gfx/wr/webrender/src/prim_store/text_run.rs
@@ -391,17 +391,17 @@ impl TextRunPrimitive {
     ) -> RasterSpace {
         let prim_spatial_node = &spatial_tree.spatial_nodes[prim_spatial_node_index.0 as usize];
         if prim_spatial_node.is_ancestor_or_self_zooming {
             let scale_factors = spatial_tree
                 .get_relative_transform(prim_spatial_node_index, ROOT_SPATIAL_NODE_INDEX)
                 .scale_factors();
 
             // Round the scale up to the nearest power of 2, but don't exceed 8.
-            let scale = scale_factors.0.max(scale_factors.1).min(8.0);
+            let scale = scale_factors.0.max(scale_factors.1).min(8.0).max(1.0);
             let rounded_up = 2.0f32.powf(scale.log2().ceil());
 
             RasterSpace::Local(rounded_up)
         } else {
             self.requested_raster_space
         }
     }
 
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -5,36 +5,33 @@
  * 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 ipc_glue_MessageChannel_h
 #define ipc_glue_MessageChannel_h 1
 
 #include "ipc/EnumSerializer.h"
 #include "mozilla/Atomics.h"
+#include "mozilla/BaseProfilerMarkers.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Vector.h"
 #if defined(OS_WIN)
 #  include "mozilla/ipc/Neutering.h"
 #endif  // defined(OS_WIN)
 
 #include <functional>
 #include <map>
 #include <stack>
 #include <vector>
 
 #include "MessageLink.h"  // for HasResultCodes
 #include "mozilla/ipc/Transport.h"
 #include "mozilla/ipc/ScopedPort.h"
 
-#ifdef MOZ_GECKO_PROFILER
-#  include "mozilla/BaseProfilerMarkers.h"
-#endif
-
 class MessageLoop;
 
 namespace IPC {
 template <typename T>
 struct ParamTraits;
 }
 
 namespace mozilla {
@@ -865,17 +862,16 @@ namespace IPC {
 template <>
 struct ParamTraits<mozilla::ipc::ResponseRejectReason>
     : public ContiguousEnumSerializer<
           mozilla::ipc::ResponseRejectReason,
           mozilla::ipc::ResponseRejectReason::SendError,
           mozilla::ipc::ResponseRejectReason::EndGuard_> {};
 }  // namespace IPC
 
-#ifdef MOZ_GECKO_PROFILER
 namespace geckoprofiler::markers {
 
 struct IPCMarker {
   static constexpr mozilla::Span<const char> MarkerTypeName() {
     return mozilla::MakeStringSpan("IPC");
   }
   static void StreamJSONMarkerData(
       mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
@@ -933,11 +929,10 @@ struct IPCMarker {
       default:
         MOZ_ASSERT_UNREACHABLE("Invalid IPC phase");
         return mozilla::MakeStringSpan("<invalid IPC phase>");
     }
   }
 };
 
 }  // namespace geckoprofiler::markers
-#endif
 
 #endif  // ifndef ipc_glue_MessageChannel_h
--- a/js/src/jit-test/lib/wasm-binary.js
+++ b/js/src/jit-test/lib/wasm-binary.js
@@ -286,19 +286,26 @@ function moduleWithSections(sectionArray
 function sigSection(sigs) {
     var body = [];
     body.push(...varU32(sigs.length));
     for (let sig of sigs) {
         body.push(...varU32(FuncCode));
         body.push(...varU32(sig.args.length));
         for (let arg of sig.args)
             body.push(...varU32(arg));
-        body.push(...varU32(sig.ret == VoidCode ? 0 : 1));
-        if (sig.ret != VoidCode)
+        if (sig.ret == VoidCode) {
+            body.push(...varU32(0));
+        } else if (typeof sig.ret == "number") {
+            body.push(...varU32(1));
             body.push(...varU32(sig.ret));
+        } else {
+            body.push(...varU32(sig.ret.length));
+            for (let r of sig.ret)
+                body.push(...varU32(r));
+        }
     }
     return { name: typeId, body };
 }
 
 function declSection(decls) {
     var body = [];
     body.push(...varU32(decls.length));
     for (let decl of decls)
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -102,19 +102,17 @@
 #include "nsLayoutUtils.h"
 #include "nsViewportInfo.h"
 #include "nsCSSRendering.h"
 // for |#ifdef DEBUG| code
 #include "prenv.h"
 #include "nsDisplayList.h"
 #include "nsRegion.h"
 #include "nsAutoLayoutPhase.h"
-#ifdef MOZ_GECKO_PROFILER
-#  include "AutoProfilerStyleMarker.h"
-#endif
+#include "AutoProfilerStyleMarker.h"
 #ifdef MOZ_REFLOW_PERF
 #  include "nsFontMetrics.h"
 #endif
 #include "MobileViewportManager.h"
 #include "OverflowChangedTracker.h"
 #include "PositionedEventTargeting.h"
 
 #include "nsIReflowCallback.h"
@@ -4168,47 +4166,43 @@ void PresShell::DoFlushPendingNotificati
       if (aFlush.mFlushAnimations && mPresContext->EffectCompositor()) {
         mPresContext->EffectCompositor()->PostRestyleForThrottledAnimations();
       }
     }
 
     // The FlushResampleRequests() above flushed style changes.
     if (MOZ_LIKELY(!mIsDestroying)) {
       nsAutoScriptBlocker scriptBlocker;
-#ifdef MOZ_GECKO_PROFILER
       Maybe<uint64_t> innerWindowID;
       if (auto* window = mDocument->GetInnerWindow()) {
         innerWindowID = Some(window->WindowID());
       }
       AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause),
                                                 innerWindowID);
-#endif
       PerfStats::AutoMetricRecording<PerfStats::Metric::Styling> autoRecording;
       LAYOUT_TELEMETRY_RECORD_BASE(Restyle);
 
       mPresContext->RestyleManager()->ProcessPendingRestyles();
     }
 
     // Now those constructors or events might have posted restyle
     // events.  At the same time, we still need up-to-date style data.
     // In particular, reflow depends on style being completely up to
     // date.  If it's not, then style reparenting, which can
     // happen during reflow, might suddenly pick up the new rules and
     // we'll end up with frames whose style doesn't match the frame
     // type.
     if (MOZ_LIKELY(!mIsDestroying)) {
       nsAutoScriptBlocker scriptBlocker;
-#ifdef MOZ_GECKO_PROFILER
       Maybe<uint64_t> innerWindowID;
       if (auto* window = mDocument->GetInnerWindow()) {
         innerWindowID = Some(window->WindowID());
       }
       AutoProfilerStyleMarker tracingStyleFlush(std::move(mStyleCause),
                                                 innerWindowID);
-#endif
       PerfStats::AutoMetricRecording<PerfStats::Metric::Styling> autoRecording;
       LAYOUT_TELEMETRY_RECORD_BASE(Restyle);
 
       mPresContext->RestyleManager()->ProcessPendingRestyles();
       // Clear mNeedStyleFlush here agagin to make this flag work properly for
       // optimization since the flag might have set in ProcessPendingRestyles().
       mNeedStyleFlush = false;
     }
@@ -9461,26 +9455,24 @@ bool PresShell::DoReflow(nsIFrame* targe
   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
   bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
 
   if (isTimelineRecording) {
     timelines->AddMarkerForDocShell(docShell, "Reflow",
                                     MarkerTracingType::START);
   }
 
-#ifdef MOZ_GECKO_PROFILER
   Maybe<uint64_t> innerWindowID;
   if (auto* window = mDocument->GetInnerWindow()) {
     innerWindowID = Some(window->WindowID());
   }
   AutoProfilerTracing tracingLayoutFlush(
       "Paint", "Reflow", geckoprofiler::category::LAYOUT,
       std::move(mReflowCause), innerWindowID);
   mReflowCause = nullptr;
-#endif
 
   FlushPendingScrollAnchorSelections();
 
   if (mReflowContinueTimer) {
     mReflowContinueTimer->Cancel();
     mReflowContinueTimer = nullptr;
   }
 
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1209,21 +1209,19 @@ TimeStamp nsRefreshDriver::MostRecentRef
 }
 
 void nsRefreshDriver::AddRefreshObserver(nsARefreshObserver* aObserver,
                                          FlushType aFlushType,
                                          const char* aObserverDescription) {
   ObserverArray& array = ArrayFor(aFlushType);
   array.AppendElement(ObserverData{
       aObserver, aObserverDescription, TimeStamp::Now(),
-#ifdef MOZ_GECKO_PROFILER
       mPresContext
           ? MarkerInnerWindowIdFromDocShell(mPresContext->GetDocShell())
           : MarkerInnerWindowId::NoId(),
-#endif
       profiler_capture_backtrace(), aFlushType});
   EnsureTimerStarted();
 }
 
 bool nsRefreshDriver::RemoveRefreshObserver(nsARefreshObserver* aObserver,
                                             FlushType aFlushType) {
   ObserverArray& array = ArrayFor(aFlushType);
   auto index = array.IndexOf(aObserver);
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -434,19 +434,17 @@ class nsRefreshDriver final : public moz
     RequestTable mEntries;
   };
   typedef nsClassHashtable<nsUint32HashKey, ImageStartData> ImageStartTable;
 
   struct ObserverData {
     nsARefreshObserver* mObserver;
     const char* mDescription;
     mozilla::TimeStamp mRegisterTime;
-#ifdef MOZ_GECKO_PROFILER
     mozilla::MarkerInnerWindowId mInnerWindowId;
-#endif
     mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> mCause;
     mozilla::FlushType mFlushType;
 
     bool operator==(nsARefreshObserver* aObserver) const {
       return mObserver == aObserver;
     }
     operator RefPtr<nsARefreshObserver>() { return mObserver; }
   };
--- a/layout/generic/MathMLTextRunFactory.cpp
+++ b/layout/generic/MathMLTextRunFactory.cpp
@@ -511,16 +511,19 @@ void MathMLTextRunFactory::RebuildTextRu
         font.fontFeatureSettings.AppendElement(settingDTLS);
       }
     }
   }
 
   uint8_t mathVar = NS_MATHML_MATHVARIANT_NONE;
   bool doMathvariantStyling = true;
 
+  // Ensure it will be safe to call FindFontForChar in the loop below.
+  fontGroup->CheckForUpdatedPlatformList();
+
   for (uint32_t i = 0; i < length; ++i) {
     int extraChars = 0;
     mathVar = styles[i]->mMathVariant;
 
     if (singleCharMI && mathVar == NS_MATHML_MATHVARIANT_NONE) {
       // If the user has explicitly set a non-default value for fontstyle or
       // fontweight, the italic mathvariant behaviour of <mi> is disabled
       // This overrides the initial values specified in fontStyle, to avoid
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -69,18 +69,18 @@ fuzzy-if(Android,0-6,0-8) fuzzy-if(webre
 skip-if(!asyncPan) == fixed-pos-scrolled-clip-5.html fixed-pos-scrolled-clip-5-ref.html
 skip-if(!asyncPan) == position-sticky-bug1434250.html position-sticky-bug1434250-ref.html
 fuzzy-if(Android,0-8,0-4) fuzzy-if(webrender&&gtkWidget,16-25,12-32) fuzzy-if(webrender&&cocoaWidget,13-16,20-44) skip-if(!asyncPan) == position-sticky-scrolled-clip-1.html position-sticky-scrolled-clip-1-ref.html # Bug 1604338
 fuzzy-if(Android,0-6,0-4) skip == position-sticky-scrolled-clip-2.html position-sticky-scrolled-clip-2-ref.html # bug ?????? - incorrectly applying clip to sticky contents
 fuzzy-if(Android,0-8,0-27) fuzzy-if(webrender&&cocoaWidget,9-11,20-44) skip-if(!asyncPan) == curtain-effect-1.html curtain-effect-1-ref.html
 fuzzy-if(Android,0-7,0-4) fuzzy-if(webrender&&gtkWidget,10-15,12-32) fuzzy-if(webrender&&cocoaWidget,5-9,20-42) skip-if(!asyncPan) == transformed-1.html transformed-1-ref.html # Bug 1604338
 fuzzy-if(Android&&!webrender,2-6,4-4) fuzzy-if(Android&&webrender,6-7,4-4) fuzzy-if(webrender&&gtkWidget,3-5,12-28) fuzzy-if(webrender&&cocoaWidget,5-6,18-38) skip-if(!asyncPan) == position-sticky-transformed-in-scrollframe-1.html position-sticky-transformed-in-scrollframe-1-ref.html # Bug 1604338
 fuzzy-if(Android&&!webrender,3-3,4-4) fuzzy-if(Android&&webrender,10-10,4-4) fuzzy-if(webrender&&gtkWidget,13-20,12-32) fuzzy-if(webrender&&cocoaWidget,12-16,20-44) skip-if(!asyncPan) == position-sticky-transformed-in-scrollframe-2.html position-sticky-transformed-in-scrollframe-2-ref.html # Bug 1604338
-fuzzy-if(Android&&!webrender,3-3,4-4) fuzzy-if(Android&&webrender,12-13,4-4) fuzzy-if(webrender&&gtkWidget,16-27,14-32) fuzzy-if(webrender&&cocoaWidget,13-16,20-44) skip-if(!asyncPan) == position-sticky-in-transformed-scrollframe-1.html position-sticky-in-transformed-scrollframe-ref.html # Bug 1604338
-fuzzy-if(Android&&!webrender,3-3,4-4) fuzzy-if(Android&&webrender,12-13,4-4) fuzzy-if(webrender&&gtkWidget,16-27,14-32) fuzzy-if(webrender&&cocoaWidget,13-16,20-44) skip-if(!asyncPan) == position-sticky-in-transformed-scrollframe-2.html position-sticky-in-transformed-scrollframe-ref.html # Bug 1604338
+fuzzy-if(Android&&!webrender,3-3,4-4) fuzzy-if(Android&&webrender,12-13,4-24) fuzzy-if(webrender&&gtkWidget,16-27,14-32) fuzzy-if(webrender&&cocoaWidget,13-16,20-44) skip-if(!asyncPan) == position-sticky-in-transformed-scrollframe-1.html position-sticky-in-transformed-scrollframe-ref.html # Bug 1604338
+fuzzy-if(Android&&!webrender,3-3,4-4) fuzzy-if(Android&&webrender,12-13,4-24) fuzzy-if(webrender&&gtkWidget,16-27,14-32) fuzzy-if(webrender&&cocoaWidget,13-16,20-44) skip-if(!asyncPan) == position-sticky-in-transformed-scrollframe-2.html position-sticky-in-transformed-scrollframe-ref.html # Bug 1604338
 
 # for the following tests, we want to disable the low-precision buffer
 # as it will expand the displayport beyond what the test specifies in
 # its reftest-displayport attributes, and interfere with where we expect
 # checkerboarding to occur
 defaults pref(layers.low-precision-buffer,false)
 skip-if(!asyncPan) == checkerboard-1.html checkerboard-1-ref.html
 skip-if(!asyncPan) == checkerboard-2.html checkerboard-2-ref.html
--- a/mfbt/LinkedList.h
+++ b/mfbt/LinkedList.h
@@ -309,33 +309,33 @@ class LinkedListElement {
   }
 
   /*
    * Insert aElem after this element, but don't check that this element is in
    * the list.  This is called by LinkedList::insertFront().
    */
   void setNextUnsafe(RawType aElem) {
     LinkedListElement* listElem = static_cast<LinkedListElement*>(aElem);
-    MOZ_ASSERT(!listElem->isInList());
+    MOZ_RELEASE_ASSERT(!listElem->isInList());
 
     listElem->mNext = this->mNext;
     listElem->mPrev = this;
     this->mNext->mPrev = listElem;
     this->mNext = listElem;
 
     Traits::enterList(aElem);
   }
 
   /*
    * Insert aElem before this element, but don't check that this element is in
    * the list.  This is called by LinkedList::insertBack().
    */
   void setPreviousUnsafe(RawType aElem) {
     LinkedListElement<T>* listElem = static_cast<LinkedListElement<T>*>(aElem);
-    MOZ_ASSERT(!listElem->isInList());
+    MOZ_RELEASE_ASSERT(!listElem->isInList());
 
     listElem->mNext = this;
     listElem->mPrev = this->mPrev;
     this->mPrev->mNext = listElem;
     this->mPrev = listElem;
 
     Traits::enterList(aElem);
   }
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -4355,39 +4355,36 @@ static nsresult pref_ReadDefaultPrefs(co
     if (NS_FAILED(rv)) {
       NS_WARNING("Error parsing preferences.");
     }
   }
 
   return NS_OK;
 }
 
-#ifdef MOZ_GECKO_PROFILER
 static nsCString PrefValueToString(const bool* b) {
   return nsCString(*b ? "true" : "false");
 }
 static nsCString PrefValueToString(const int* i) {
   return nsPrintfCString("%d", *i);
 }
 static nsCString PrefValueToString(const uint32_t* u) {
   return nsPrintfCString("%d", *u);
 }
 static nsCString PrefValueToString(const float* f) {
   return nsPrintfCString("%f", *f);
 }
 static nsCString PrefValueToString(const nsACString& s) { return nsCString(s); }
-#endif
 
 // These preference getter wrappers allow us to look up the value for static
 // preferences based on their native types, rather than manually mapping them to
 // the appropriate Preferences::Get* functions.
 // We define these methods in a struct which is made friend of Preferences in
 // order to access private members.
 struct Internals {
-#ifdef MOZ_GECKO_PROFILER
   struct PreferenceReadMarker {
     static constexpr Span<const char> MarkerTypeName() {
       return MakeStringSpan("PreferenceRead");
     }
     static void StreamJSONMarkerData(
         baseprofiler::SpliceableJSONWriter& aWriter,
         const ProfilerString8View& aPrefName,
         const Maybe<PrefValueKind>& aPrefKind, PrefType aPrefType,
@@ -4428,58 +4425,53 @@ struct Internals {
         case PrefType::String:
           return "String";
         default:
           MOZ_ASSERT_UNREACHABLE("Unknown preference type.");
           return "Unknown";
       }
     }
   };
-#endif  // MOZ_GECKO_PROFILER
 
   template <typename T>
   static nsresult GetPrefValue(const char* aPrefName, T&& aResult,
                                PrefValueKind aKind) {
     nsresult rv = NS_ERROR_UNEXPECTED;
     NS_ENSURE_TRUE(Preferences::InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
     if (Maybe<PrefWrapper> pref = pref_Lookup(aPrefName)) {
       rv = pref->GetValue(aKind, std::forward<T>(aResult));
 
-#ifdef MOZ_GECKO_PROFILER
       if (profiler_feature_active(ProfilerFeature::PreferenceReads)) {
         profiler_add_marker(
             "PreferenceRead", baseprofiler::category::OTHER_PreferenceRead, {},
             PreferenceReadMarker{},
             ProfilerString8View::WrapNullTerminatedString(aPrefName),
             Some(aKind), pref->Type(), PrefValueToString(aResult));
       }
-#endif
     }
 
     return rv;
   }
 
   template <typename T>
   static nsresult GetSharedPrefValue(const char* aName, T* aResult) {
     nsresult rv = NS_ERROR_UNEXPECTED;
 
     if (Maybe<PrefWrapper> pref = pref_SharedLookup(aName)) {
       rv = pref->GetValue(PrefValueKind::User, aResult);
 
-#ifdef MOZ_GECKO_PROFILER
       if (profiler_feature_active(ProfilerFeature::PreferenceReads)) {
         profiler_add_marker(
             "PreferenceRead", baseprofiler::category::OTHER_PreferenceRead, {},
             PreferenceReadMarker{},
             ProfilerString8View::WrapNullTerminatedString(aName),
             Nothing() /* indicates Shared */, pref->Type(),
             PrefValueToString(aResult));
       }
-#endif
     }
 
     return rv;
   }
 
   template <typename T>
   static T GetPref(const char* aPrefName, T aFallback,
                    PrefValueKind aKind = PrefValueKind::User) {
--- a/mozglue/baseprofiler/moz.build
+++ b/mozglue/baseprofiler/moz.build
@@ -8,19 +8,17 @@
 # that cannot work in mozglue (because they are totally dependent on libxul-
 # specific code).
 # All exported headers now prefixed with "Base" to avoid #include name clashes.
 
 if CONFIG["MOZ_GECKO_PROFILER"]:
     DEFINES["IMPL_MFBT"] = True
     EXPORTS += [
         "public/BaseProfilerSharedLibraries.h",
-        "public/BaseProfilingCategory.h",
         "public/BaseProfilingStack.h",
-        "public/ProfilingCategoryList.h",
     ]
     UNIFIED_SOURCES += [
         "core/PageInformation.cpp",
         "core/platform.cpp",
         "core/ProfileBuffer.cpp",
         "core/ProfileBufferEntry.cpp",
         "core/ProfiledThreadData.cpp",
         "core/ProfileJSONWriter.cpp",
@@ -67,16 +65,18 @@ if CONFIG["MOZ_GECKO_PROFILER"]:
     if CONFIG["OS_TARGET"] == "Android":
         DEFINES["ANDROID_NDK_MAJOR_VERSION"] = CONFIG["ANDROID_NDK_MAJOR_VERSION"]
         DEFINES["ANDROID_NDK_MINOR_VERSION"] = CONFIG["ANDROID_NDK_MINOR_VERSION"]
 
     FINAL_LIBRARY = "mozglue"
 
 EXPORTS += [
     "public/BaseProfiler.h",
+    "public/BaseProfilingCategory.h",
+    "public/ProfilingCategoryList.h",
 ]
 
 EXPORTS.mozilla += [
     "public/BaseProfileJSONWriter.h",
     "public/BaseProfilerCounts.h",
     "public/BaseProfilerDetail.h",
     "public/BaseProfilerLabels.h",
     "public/BaseProfilerMarkers.h",
--- a/mozglue/baseprofiler/public/BaseProfiler.h
+++ b/mozglue/baseprofiler/public/BaseProfiler.h
@@ -45,19 +45,16 @@
 #  define AUTO_BASE_PROFILER_REGISTER_THREAD(name)
 
 #  define AUTO_BASE_PROFILER_THREAD_SLEEP
 #  define AUTO_BASE_PROFILER_THREAD_WAKE
 
 // Function stubs for when MOZ_GECKO_PROFILER is not defined.
 
 namespace mozilla {
-// This won't be used, it's just there to allow the empty definition of
-// `profiler_capture_backtrace`.
-class ProfileChunkedBuffer {};
 
 namespace baseprofiler {
 // This won't be used, it's just there to allow the empty definition of
 // `profiler_get_backtrace`.
 struct ProfilerBacktrace {};
 using UniqueProfilerBacktrace = UniquePtr<ProfilerBacktrace>;
 
 // Get/Capture-backtrace functions can return nullptr or false, the result
--- a/mozglue/baseprofiler/public/BaseProfilerDetail.h
+++ b/mozglue/baseprofiler/public/BaseProfilerDetail.h
@@ -8,25 +8,25 @@
 
 #ifndef BaseProfilerDetail_h
 #define BaseProfilerDetail_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/PlatformMutex.h"
 
-#ifndef MOZ_GECKO_PROFILER
-#  error Do not #include this header when MOZ_GECKO_PROFILER is not #defined.
-#endif
-
 namespace mozilla {
 namespace baseprofiler {
 
+#ifdef MOZ_GECKO_PROFILER
 // Implemented in platform.cpp
 MFBT_API int profiler_current_thread_id();
+#else
+inline int profiler_current_thread_id() { return 0; }
+#endif  // MOZ_GECKO_PROFILER
 
 namespace detail {
 
 // Thin shell around mozglue PlatformMutex, for Base Profiler internal use.
 class BaseProfilerMutex : private ::mozilla::detail::MutexImpl {
  public:
   BaseProfilerMutex() : ::mozilla::detail::MutexImpl() {}
   explicit BaseProfilerMutex(const char* aName)
--- a/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h
+++ b/mozglue/baseprofiler/public/BaseProfilerMarkerTypes.h
@@ -20,18 +20,16 @@
 
 // !!!                       /!\ WORK IN PROGRESS /!\                       !!!
 // This file contains draft marker definitions, but most are not used yet.
 // Further work is needed to complete these definitions, and use them to convert
 // existing PROFILER_ADD_MARKER calls. See meta bug 1661394.
 
 #include "mozilla/BaseProfilerMarkers.h"
 
-#ifdef MOZ_GECKO_PROFILER
-
 namespace mozilla::baseprofiler::markers {
 
 struct MediaSampleMarker {
   static constexpr Span<const char> MarkerTypeName() {
     return MakeStringSpan("MediaSample");
   }
   static void StreamJSONMarkerData(SpliceableJSONWriter& aWriter,
                                    int64_t aSampleStartTimeUs,
@@ -59,11 +57,9 @@ struct ContentBuildMarker {
     using MS = MarkerSchema;
     MS schema{MS::Location::markerChart, MS::Location::markerTable};
     return schema;
   }
 };
 
 }  // namespace mozilla::baseprofiler::markers
 
-#endif  // MOZ_GECKO_PROFILER
-
 #endif  // BaseProfilerMarkerTypes_h
--- a/mozglue/baseprofiler/public/BaseProfilerMarkers.h
+++ b/mozglue/baseprofiler/public/BaseProfilerMarkers.h
@@ -27,38 +27,27 @@
 // - Otherwise #include "ProfilerMarkers.h" instead, and use
 //   `profiler_add_marker(...)`.
 // See these functions for more details.
 
 #ifndef BaseProfilerMarkers_h
 #define BaseProfilerMarkers_h
 
 #include "mozilla/BaseProfilerMarkersDetail.h"
-
-#ifndef MOZ_GECKO_PROFILER
-
-#  define BASE_PROFILER_MARKER_UNTYPED(markerName, categoryName, ...)
-#  define BASE_PROFILER_MARKER(markerName, categoryName, options, MarkerType, \
-                               ...)
-#  define BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options, text)
-#  define AUTO_BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options, \
-                                         text)
+#include "mozilla/BaseProfilerLabels.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Unused.h"
 
-#else  // ndef MOZ_GECKO_PROFILER
-
-#  include "mozilla/BaseProfilerLabels.h"
-#  include "mozilla/TimeStamp.h"
-#  include "mozilla/Unused.h"
-
-#  include <functional>
-#  include <string>
-#  include <utility>
+#include <functional>
+#include <string>
+#include <utility>
 
 namespace mozilla::baseprofiler {
 
+#ifdef MOZ_GECKO_PROFILER
 // Forward-declaration. TODO: Move to more common header, see bug 1681416.
 MFBT_API bool profiler_capture_backtrace_into(
     ProfileChunkedBuffer& aChunkedBuffer, StackCaptureOptions aCaptureOptions);
 
 // Add a marker to a given buffer. `AddMarker()` and related macros should be
 // used in most cases, see below for more information about them and the
 // parameters; This function may be useful when markers need to be recorded in a
 // local buffer outside of the main profiler buffer.
@@ -77,68 +66,72 @@ ProfileBufferBlockIndex AddMarkerToBuffe
 
 // Add a marker (without payload) to a given buffer.
 inline ProfileBufferBlockIndex AddMarkerToBuffer(
     ProfileChunkedBuffer& aBuffer, const ProfilerString8View& aName,
     const MarkerCategory& aCategory, MarkerOptions&& aOptions = {}) {
   return AddMarkerToBuffer(aBuffer, aName, aCategory, std::move(aOptions),
                            markers::NoPayload{});
 }
+#endif  // MOZ_GECKO_PROFILER
 
 // Add a marker to the Base Profiler buffer.
 // - aName: Main name of this marker.
 // - aCategory: Category for this marker.
 // - aOptions: Optional settings (such as timing, inner window id,
 //   backtrace...), see `MarkerOptions` for details.
 // - aMarkerType: Empty object that specifies the type of marker.
 // - aPayloadArguments: Arguments expected by this marker type's
 // ` StreamJSONMarkerData` function.
 template <typename MarkerType, typename... PayloadArguments>
 ProfileBufferBlockIndex AddMarker(
     const ProfilerString8View& aName, const MarkerCategory& aCategory,
     MarkerOptions&& aOptions, MarkerType aMarkerType,
     const PayloadArguments&... aPayloadArguments) {
+#ifndef MOZ_GECKO_PROFILER
+  return {};
+#else
   if (!baseprofiler::profiler_can_accept_markers()) {
     return {};
   }
   return ::mozilla::baseprofiler::AddMarkerToBuffer(
       base_profiler_markers_detail::CachedBaseCoreBuffer(), aName, aCategory,
       std::move(aOptions), aMarkerType, aPayloadArguments...);
+#endif
 }
 
 // Add a marker (without payload) to the Base Profiler buffer.
 inline ProfileBufferBlockIndex AddMarker(const ProfilerString8View& aName,
                                          const MarkerCategory& aCategory,
                                          MarkerOptions&& aOptions = {}) {
   return AddMarker(aName, aCategory, std::move(aOptions), markers::NoPayload{});
 }
 
 }  // namespace mozilla::baseprofiler
 
 // Same as `AddMarker()` (without payload). This macro is safe to use even if
 // MOZ_GECKO_PROFILER is not #defined.
-#  define BASE_PROFILER_MARKER_UNTYPED(markerName, categoryName, ...)  \
-    do {                                                               \
-      AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_UNTYPED);               \
-      ::mozilla::baseprofiler::AddMarker(                              \
-          markerName, ::mozilla::baseprofiler::category::categoryName, \
-          ##__VA_ARGS__);                                              \
-    } while (false)
+#define BASE_PROFILER_MARKER_UNTYPED(markerName, categoryName, ...)  \
+  do {                                                               \
+    AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_UNTYPED);               \
+    ::mozilla::baseprofiler::AddMarker(                              \
+        markerName, ::mozilla::baseprofiler::category::categoryName, \
+        ##__VA_ARGS__);                                              \
+  } while (false)
 
 // Same as `AddMarker()` (with payload). This macro is safe to use even if
 // MOZ_GECKO_PROFILER is not #defined.
-#  define BASE_PROFILER_MARKER(markerName, categoryName, options, MarkerType, \
-                               ...)                                           \
-    do {                                                                      \
-      AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_with_##MarkerType);            \
-      ::mozilla::baseprofiler::AddMarker(                                     \
-          markerName, ::mozilla::baseprofiler::category::categoryName,        \
-          options, ::mozilla::baseprofiler::markers::MarkerType{},            \
-          ##__VA_ARGS__);                                                     \
-    } while (false)
+#define BASE_PROFILER_MARKER(markerName, categoryName, options, MarkerType,   \
+                             ...)                                             \
+  do {                                                                        \
+    AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_with_##MarkerType);              \
+    ::mozilla::baseprofiler::AddMarker(                                       \
+        markerName, ::mozilla::baseprofiler::category::categoryName, options, \
+        ::mozilla::baseprofiler::markers::MarkerType{}, ##__VA_ARGS__);       \
+  } while (false)
 
 namespace mozilla::baseprofiler::markers {
 // Most common marker type. Others are in BaseProfilerMarkerTypes.h.
 struct TextMarker {
   static constexpr Span<const char> MarkerTypeName() {
     return MakeStringSpan("Text");
   }
   static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
@@ -172,23 +165,23 @@ struct Tracing {
     schema.AddKeyLabelFormat("category", "Type", MS::Format::string);
     return schema;
   }
 };
 }  // namespace mozilla::baseprofiler::markers
 
 // Add a text marker. This macro is safe to use even if MOZ_GECKO_PROFILER is
 // not #defined.
-#  define BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options, text) \
-    do {                                                                     \
-      AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_TEXT);                        \
-      ::mozilla::baseprofiler::AddMarker(                                    \
-          markerName, ::mozilla::baseprofiler::category::categoryName,       \
-          options, ::mozilla::baseprofiler::markers::TextMarker{}, text);    \
-    } while (false)
+#define BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options, text)    \
+  do {                                                                        \
+    AUTO_PROFILER_STATS(BASE_PROFILER_MARKER_TEXT);                           \
+    ::mozilla::baseprofiler::AddMarker(                                       \
+        markerName, ::mozilla::baseprofiler::category::categoryName, options, \
+        ::mozilla::baseprofiler::markers::TextMarker{}, text);                \
+  } while (false)
 
 namespace mozilla::baseprofiler {
 
 // RAII object that adds a BASE_PROFILER_MARKER_TEXT when destroyed; the
 // marker's timing will be the interval from construction (unless an instant or
 // start time is already specified in the provided options) until destruction.
 class MOZ_RAII AutoProfilerTextMarker {
  public:
@@ -215,33 +208,33 @@ class MOZ_RAII AutoProfilerTextMarker {
 
  protected:
   const char* mMarkerName;
   MarkerCategory mCategory;
   MarkerOptions mOptions;
   std::string mText;
 };
 
+#ifdef MOZ_GECKO_PROFILER
 extern template MFBT_API ProfileBufferBlockIndex
 AddMarker(const ProfilerString8View&, const MarkerCategory&, MarkerOptions&&,
           markers::TextMarker, const std::string&);
 
 extern template MFBT_API ProfileBufferBlockIndex
 AddMarkerToBuffer(ProfileChunkedBuffer&, const ProfilerString8View&,
                   const MarkerCategory&, MarkerOptions&&, markers::NoPayload);
 
 extern template MFBT_API ProfileBufferBlockIndex AddMarkerToBuffer(
     ProfileChunkedBuffer&, const ProfilerString8View&, const MarkerCategory&,
     MarkerOptions&&, markers::TextMarker, const std::string&);
+#endif  // MOZ_GECKO_PROFILER
 
 }  // namespace mozilla::baseprofiler
 
 // Creates an AutoProfilerTextMarker RAII object.  This macro is safe to use
 // even if MOZ_GECKO_PROFILER is not #defined.
-#  define AUTO_BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options,   \
-                                         text)                                \
-    ::mozilla::baseprofiler::AutoProfilerTextMarker BASE_PROFILER_RAII(       \
-        markerName, ::mozilla::baseprofiler::category::categoryName, options, \
-        text)
-
-#endif  // nfed MOZ_GECKO_PROFILER else
+#define AUTO_BASE_PROFILER_MARKER_TEXT(markerName, categoryName, options,   \
+                                       text)                                \
+  ::mozilla::baseprofiler::AutoProfilerTextMarker BASE_PROFILER_RAII(       \
+      markerName, ::mozilla::baseprofiler::category::categoryName, options, \
+      text)
 
 #endif  // BaseProfilerMarkers_h
--- a/mozglue/baseprofiler/public/BaseProfilerMarkersDetail.h
+++ b/mozglue/baseprofiler/public/BaseProfilerMarkersDetail.h
@@ -8,28 +8,26 @@
 #define BaseProfilerMarkersDetail_h
 
 #ifndef BaseProfilerMarkers_h
 #  error "This header should only be #included by BaseProfilerMarkers.h"
 #endif
 
 #include "mozilla/BaseProfilerMarkersPrerequisites.h"
 
-#ifdef MOZ_GECKO_PROFILER
-
 //                        ~~ HERE BE DRAGONS ~~
 //
 // Everything below is internal implementation detail, you shouldn't need to
 // look at it unless working on the profiler code.
 
-#  include "mozilla/BaseProfileJSONWriter.h"
-#  include "mozilla/ProfileBufferEntryKinds.h"
+#include "mozilla/BaseProfileJSONWriter.h"
+#include "mozilla/ProfileBufferEntryKinds.h"
 
-#  include <limits>
-#  include <tuple>
+#include <limits>
+#include <tuple>
 
 namespace mozilla::baseprofiler {
 // Implemented in platform.cpp
 MFBT_API ProfileChunkedBuffer& profiler_get_core_buffer();
 }  // namespace mozilla::baseprofiler
 
 namespace mozilla::base_profiler_markers_detail {
 
@@ -668,11 +666,9 @@ struct ProfileBufferEntryReader::Deseria
     MarkerOptions options;
     ReadInto(aER, options);
     return options;
   }
 };
 
 }  // namespace mozilla
 
-#endif  // MOZ_GECKO_PROFILER
-
 #endif  // BaseProfilerMarkersDetail_h
--- a/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h
+++ b/mozglue/baseprofiler/public/BaseProfilerMarkersPrerequisites.h
@@ -19,32 +19,30 @@ enum class StackCaptureOptions {
   NoStack,    // No stack captured.
   Full,       // Capture a full stack, including label frames, JS frames and
               // native frames.
   NonNative,  // Capture a stack without native frames for reduced overhead.
 };
 
 }
 
-#ifdef MOZ_GECKO_PROFILER
+#include "BaseProfilingCategory.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/ProfileChunkedBuffer.h"
+#include "mozilla/BaseProfilerState.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Variant.h"
 
-#  include "BaseProfilingCategory.h"
-#  include "mozilla/Maybe.h"
-#  include "mozilla/ProfileChunkedBuffer.h"
-#  include "mozilla/BaseProfilerState.h"
-#  include "mozilla/TimeStamp.h"
-#  include "mozilla/UniquePtr.h"
-#  include "mozilla/Variant.h"
-
-#  include <initializer_list>
-#  include <string_view>
-#  include <string>
-#  include <type_traits>
-#  include <utility>
-#  include <vector>
+#include <initializer_list>
+#include <string_view>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
 
 namespace mozilla {
 
 // Return a NotNull<const CHAR*> pointing at the literal empty string `""`.
 template <typename CHAR>
 constexpr const CHAR* LiteralEmptyStringPointer() {
   static_assert(std::is_same_v<CHAR, char> || std::is_same_v<CHAR, char16_t>,
                 "Only char and char16_t are supported in Firefox");
@@ -243,26 +241,26 @@ class MarkerCategory {
 };
 
 namespace baseprofiler::category {
 
 // Each category pair name constructs a MarkerCategory.
 // E.g.: mozilla::baseprofiler::category::OTHER_Profiling
 // Profiler macros will take the category name alone without namespace.
 // E.g.: `PROFILER_MARKER_UNTYPED("name", OTHER_Profiling)`
-#  define CATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color)
-#  define CATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString) \
-    static constexpr MarkerCategory name{ProfilingCategoryPair::name};
-#  define CATEGORY_ENUM_END_CATEGORY
+#define CATEGORY_ENUM_BEGIN_CATEGORY(name, labelAsString, color)
+#define CATEGORY_ENUM_SUBCATEGORY(supercategory, name, labelAsString) \
+  static constexpr MarkerCategory name{ProfilingCategoryPair::name};
+#define CATEGORY_ENUM_END_CATEGORY
 MOZ_PROFILING_CATEGORY_LIST(CATEGORY_ENUM_BEGIN_CATEGORY,
                             CATEGORY_ENUM_SUBCATEGORY,
                             CATEGORY_ENUM_END_CATEGORY)
-#  undef CATEGORY_ENUM_BEGIN_CATEGORY
-#  undef CATEGORY_ENUM_SUBCATEGORY
-#  undef CATEGORY_ENUM_END_CATEGORY
+#undef CATEGORY_ENUM_BEGIN_CATEGORY
+#undef CATEGORY_ENUM_SUBCATEGORY
+#undef CATEGORY_ENUM_END_CATEGORY
 
 // Import `MarkerCategory` into this namespace. This will allow using this type
 // dynamically in macros that prepend `::mozilla::baseprofiler::category::` to
 // the given category, e.g.:
 // `PROFILER_MARKER_UNTYPED("name", MarkerCategory(...))`
 using MarkerCategory = ::mozilla::MarkerCategory;
 
 }  // namespace baseprofiler::category
@@ -530,34 +528,34 @@ class MarkerStack {
  private:
   explicit MarkerStack(StackCaptureOptions aCaptureOptions)
       : mCaptureOptions(aCaptureOptions) {
     AssertInvariants();
   }
 
   // This should be called after every constructor and non-const function.
   void AssertInvariants() const {
-#  ifdef DEBUG
+#ifdef DEBUG
     if (mCaptureOptions != StackCaptureOptions::NoStack) {
       MOZ_ASSERT(!mOptionalChunkedBufferStorage,
                  "We should not hold a buffer when capture is requested");
       MOZ_ASSERT(!mChunkedBuffer,
                  "We should not point at a buffer when capture is requested");
     } else {
       if (mOptionalChunkedBufferStorage) {
         MOZ_ASSERT(mChunkedBuffer == mOptionalChunkedBufferStorage.get(),
                    "Non-null mOptionalChunkedBufferStorage must be pointed-at "
                    "by mChunkedBuffer");
       }
       if (mChunkedBuffer) {
         MOZ_ASSERT(!mChunkedBuffer->IsEmpty(),
                    "Non-null mChunkedBuffer must not be empty");
       }
     }
-#  endif  // DEBUG
+#endif  // DEBUG
   }
 
   StackCaptureOptions mCaptureOptions = StackCaptureOptions::NoStack;
 
   // Optional storage for the backtrace, in case it was captured before the
   // add-marker call.
   UniquePtr<ProfileChunkedBuffer> mOptionalChunkedBufferStorage;
 
@@ -619,36 +617,36 @@ class MarkerOptions {
   // Each option may be added in a chain by e.g.:
   // `options.Set(MarkerThreadId(123)).Set(MarkerTiming::IntervalEnd())`.
   // When passed to an add-marker function, it must be an rvalue, either created
   // on the spot, or `std::move`d from storage, e.g.:
   // `PROFILER_MARKER_UNTYPED("...", std::move(options).Set(...))`;
   //
   // Options can be read by their name (without "Marker"), e.g.: `o.ThreadId()`.
   // Add "Ref" for a non-const reference, e.g.: `o.ThreadIdRef() = ...;`
-#  define FUNCTIONS_ON_MEMBER(NAME)                      \
-    MarkerOptions& Set(Marker##NAME&& a##NAME)& {        \
-      m##NAME = std::move(a##NAME);                      \
-      return *this;                                      \
-    }                                                    \
-                                                         \
-    MarkerOptions&& Set(Marker##NAME&& a##NAME)&& {      \
-      m##NAME = std::move(a##NAME);                      \
-      return std::move(*this);                           \
-    }                                                    \
-                                                         \
-    const Marker##NAME& NAME() const { return m##NAME; } \
-                                                         \
-    Marker##NAME& NAME##Ref() { return m##NAME; }
+#define FUNCTIONS_ON_MEMBER(NAME)                      \
+  MarkerOptions& Set(Marker##NAME&& a##NAME)& {        \
+    m##NAME = std::move(a##NAME);                      \
+    return *this;                                      \
+  }                                                    \
+                                                       \
+  MarkerOptions&& Set(Marker##NAME&& a##NAME)&& {      \
+    m##NAME = std::move(a##NAME);                      \
+    return std::move(*this);                           \
+  }                                                    \
+                                                       \
+  const Marker##NAME& NAME() const { return m##NAME; } \
+                                                       \
+  Marker##NAME& NAME##Ref() { return m##NAME; }
 
   FUNCTIONS_ON_MEMBER(ThreadId);
   FUNCTIONS_ON_MEMBER(Timing);
   FUNCTIONS_ON_MEMBER(Stack);
   FUNCTIONS_ON_MEMBER(InnerWindowId);
-#  undef FUNCTIONS_ON_MEMBER
+#undef FUNCTIONS_ON_MEMBER
 
  private:
   friend ProfileBufferEntryReader::Deserializer<MarkerOptions>;
 
   MarkerThreadId mThreadId;
   MarkerTiming mTiming;
   MarkerStack mStack;
   MarkerInnerWindowId mInnerWindowId;
@@ -758,27 +756,27 @@ class MarkerSchema {
   // Caller must specify location(s) or SpecialFrontendLocation above.
   MarkerSchema() = delete;
 
   // Optional labels in the marker chart, the chart tooltip, and the marker
   // table. If not provided, the marker "name" will be used. The given string
   // can contain element keys in braces to include data elements streamed by
   // `StreamJSONMarkerData()`. E.g.: "This is {text}"
 
-#  define LABEL_SETTER(name)                       \
-    MarkerSchema& Set##name(std::string a##name) { \
-      m##name = std::move(a##name);                \
-      return *this;                                \
-    }
+#define LABEL_SETTER(name)                       \
+  MarkerSchema& Set##name(std::string a##name) { \
+    m##name = std::move(a##name);                \
+    return *this;                                \
+  }
 
   LABEL_SETTER(ChartLabel)
   LABEL_SETTER(TooltipLabel)
   LABEL_SETTER(TableLabel)
 
-#  undef LABEL_SETTER
+#undef LABEL_SETTER
 
   MarkerSchema& SetAllLabels(std::string aText) {
     // Here we set the same text in each label.
     // TODO: Move to a single "label" field once the front-end allows it.
     SetChartLabel(aText);
     SetTooltipLabel(aText);
     SetTableLabel(std::move(aText));
     return *this;
@@ -863,11 +861,9 @@ class MarkerSchema {
   using DataRow = mozilla::Variant<DynamicData, StaticData>;
   using DataRowVector = std::vector<DataRow>;
 
   DataRowVector mData;
 };
 
 }  // namespace mozilla
 
-#endif  // MOZ_GECKO_PROFILER
-
 #endif  // BaseProfilerMarkersPrerequisites_h
--- a/mozglue/baseprofiler/public/BaseProfilerState.h
+++ b/mozglue/baseprofiler/public/BaseProfilerState.h
@@ -21,16 +21,25 @@
 // some generic process and thread information.
 // It is safe to include unconditionally, but uses of structs and functions must
 // be guarded by `#ifdef MOZ_GECKO_PROFILER`.
 
 #ifndef MOZ_GECKO_PROFILER
 
 #  define AUTO_PROFILER_STATS(name)
 
+namespace mozilla {
+
+namespace baseprofiler {
+
+inline int profiler_main_thread_id() { return 0; }
+
+}  // namespace baseprofiler
+}  // namespace mozilla
+
 #else  // !MOZ_GECKO_PROFILER
 
 #  include "mozilla/Atomics.h"
 #  include "mozilla/Maybe.h"
 
 #  include <stdint.h>
 #  include <string>
 
--- a/mozglue/baseprofiler/public/BaseProfilingCategory.h
+++ b/mozglue/baseprofiler/public/BaseProfilingCategory.h
@@ -2,20 +2,16 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 BaseProfilingCategory_h
 #define BaseProfilingCategory_h
 
-#ifndef MOZ_GECKO_PROFILER
-#  error Do not #include this header when MOZ_GECKO_PROFILER is not #defined.
-#endif
-
 #include "mozilla/Types.h"
 
 #include <cstdint>
 
 #include "ProfilingCategoryList.h"
 
 namespace mozilla {
 namespace baseprofiler {
--- a/mozglue/build/AsanOptions.cpp
+++ b/mozglue/build/AsanOptions.cpp
@@ -105,16 +105,18 @@ extern "C" const char* __lsan_default_su
          // it doesn't suppress it.
 
          // Bug 1078015 - If the process terminates during a PR_Sleep, LSAN
          // detects a leak
          "leak:PR_Sleep\n"
 
          // Bug 1363976 - Stylo holds some global data alive forever.
          "leak:style::global_style_data\n"
+         "leak:style::sharing::SHARING_CACHE_KEY\n"
+         "leak:style::bloom::BLOOM_KEY\n"
 
          //
          // Many leaks only affect some test suites.  The suite annotations are
          // not checked.
          //
 
          // Bug 979928 - WebRTC leaks in different mochitest suites.
          "leak:NR_reg_init\n"
--- a/mozglue/misc/PreXULSkeletonUI.cpp
+++ b/mozglue/misc/PreXULSkeletonUI.cpp
@@ -1836,19 +1836,17 @@ static Result<Ok, PreXULSkeletonUIError>
 
 static Result<Ok, PreXULSkeletonUIError> WriteRegBool(
     HKEY regKey, const std::wstring& valueName, bool value) {
   return WriteRegUint(regKey, valueName, value ? 1 : 0);
 }
 
 static Result<Ok, PreXULSkeletonUIError> CreateAndStorePreXULSkeletonUIImpl(
     HINSTANCE hInstance, int argc, char** argv) {
-#ifdef MOZ_GECKO_PROFILER
   const TimeStamp skeletonStart = TimeStamp::NowUnfuzzed();
-#endif
 
   if (!IsWin10OrLater()) {
     return Err(PreXULSkeletonUIError::Ineligible);
   }
 
   HKEY regKey;
   MOZ_TRY_VAR(regKey, OpenPreXULSkeletonUIRegKey());
   AutoCloseRegKey closeKey(regKey);
--- a/mozglue/tests/TestBaseProfiler.cpp
+++ b/mozglue/tests/TestBaseProfiler.cpp
@@ -4374,52 +4374,45 @@ void TestProfiler() {
   // These don't need to make sense, we just want to know that they're defined
   // and don't do anything.
 
 #  ifndef AUTO_BASE_PROFILER_INIT
 #    error AUTO_BASE_PROFILER_INIT not #defined
 #  endif  // AUTO_BASE_PROFILER_INIT
   AUTO_BASE_PROFILER_INIT;
 
-  // This wouldn't build if the macro did output its arguments.
 #  ifndef AUTO_BASE_PROFILER_MARKER_TEXT
 #    error AUTO_BASE_PROFILER_MARKER_TEXT not #defined
 #  endif  // AUTO_BASE_PROFILER_MARKER_TEXT
-  AUTO_BASE_PROFILER_MARKER_TEXT(catch, catch, catch, catch);
 
 #  ifndef AUTO_BASE_PROFILER_LABEL
 #    error AUTO_BASE_PROFILER_LABEL not #defined
 #  endif  // AUTO_BASE_PROFILER_LABEL
-  AUTO_BASE_PROFILER_LABEL(catch, catch);
 
 #  ifndef AUTO_BASE_PROFILER_THREAD_SLEEP
 #    error AUTO_BASE_PROFILER_THREAD_SLEEP not #defined
 #  endif  // AUTO_BASE_PROFILER_THREAD_SLEEP
   AUTO_BASE_PROFILER_THREAD_SLEEP;
 
 #  ifndef BASE_PROFILER_MARKER_UNTYPED
 #    error BASE_PROFILER_MARKER_UNTYPED not #defined
 #  endif  // BASE_PROFILER_MARKER_UNTYPED
-  BASE_PROFILER_MARKER_UNTYPED(catch, catch);
-  BASE_PROFILER_MARKER_UNTYPED(catch, catch, catch);
 
 #  ifndef BASE_PROFILER_MARKER
 #    error BASE_PROFILER_MARKER not #defined
 #  endif  // BASE_PROFILER_MARKER
-  BASE_PROFILER_MARKER(catch, catch, catch, catch);
-  BASE_PROFILER_MARKER(catch, catch, catch, catch, catch);
 
 #  ifndef BASE_PROFILER_MARKER_TEXT
 #    error BASE_PROFILER_MARKER_TEXT not #defined
 #  endif  // BASE_PROFILER_MARKER_TEXT
-  BASE_PROFILER_MARKER_TEXT(catch, catch, catch, catch);
 
   MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_get_backtrace(),
                      "profiler_get_backtrace should return nullptr");
-  mozilla::ProfileChunkedBuffer buffer;
+  mozilla::ProfileChunkedBuffer buffer(
+      mozilla::ProfileChunkedBuffer::ThreadSafety::WithoutMutex);
   MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_capture_backtrace_into(
                          buffer, mozilla::StackCaptureOptions::Full),
                      "profiler_capture_backtrace_into should return false");
   MOZ_RELEASE_ASSERT(!mozilla::baseprofiler::profiler_capture_backtrace(),
                      "profiler_capture_backtrace should return nullptr");
 }
 
 // Testing that macros are still #defined (but do nothing) when
--- a/netwerk/base/CaptivePortalService.cpp
+++ b/netwerk/base/CaptivePortalService.cpp
@@ -14,18 +14,16 @@
 static constexpr auto kInterfaceName = u"captive-portal-inteface"_ns;
 
 static const char kOpenCaptivePortalLoginEvent[] = "captive-portal-login";
 static const char kAbortCaptivePortalLoginEvent[] =
     "captive-portal-login-abort";
 static const char kCaptivePortalLoginSuccessEvent[] =
     "captive-portal-login-success";
 
-static const uint32_t kDefaultInterval = 60 * 1000;  // check every 60 seconds
-
 namespace mozilla {
 namespace net {
 
 static LazyLogModule gCaptivePortalLog("CaptivePortalService");
 #undef LOG
 #define LOG(args) MOZ_LOG(gCaptivePortalLog, mozilla::LogLevel::Debug, args)
 
 NS_IMPL_ISUPPORTS(CaptivePortalService, nsICaptivePortalService, nsIObserver,
@@ -40,27 +38,17 @@ already_AddRefed<nsICaptivePortalService
     return do_AddRef(gCPService);
   }
 
   gCPService = new CaptivePortalService();
   ClearOnShutdown(&gCPService);
   return do_AddRef(gCPService);
 }
 
-CaptivePortalService::CaptivePortalService()
-    : mState(UNKNOWN),
-      mStarted(false),
-      mInitialized(false),
-      mRequestInProgress(false),
-      mEverBeenCaptive(false),
-      mDelay(kDefaultInterval),
-      mSlackCount(0),
-      mMinInterval(kDefaultInterval),
-      mMaxInterval(25 * kDefaultInterval),
-      mBackoffFactor(5.0) {
+CaptivePortalService::CaptivePortalService() {
   mLastChecked = TimeStamp::Now();
 }
 
 CaptivePortalService::~CaptivePortalService() {
   LOG(("CaptivePortalService::~CaptivePortalService isParentProcess:%d\n",
        XRE_GetProcessType() == GeckoProcessType_Default));
 }
 
@@ -308,30 +296,30 @@ CaptivePortalService::Observe(nsISupport
     // Doesn't do anything if called in the content process.
     return NS_OK;
   }
 
   LOG(("CaptivePortalService::Observe() topic=%s\n", aTopic));
   if (!strcmp(aTopic, kOpenCaptivePortalLoginEvent)) {
     // A redirect or altered content has been detected.
     // The user needs to log in. We are in a captive portal.
-    mState = LOCKED_PORTAL;
+    StateTransition(LOCKED_PORTAL);
     mLastChecked = TimeStamp::Now();
     mEverBeenCaptive = true;
   } else if (!strcmp(aTopic, kCaptivePortalLoginSuccessEvent)) {
     // The user has successfully logged in. We have connectivity.
-    mState = UNLOCKED_PORTAL;
+    StateTransition(UNLOCKED_PORTAL);
     mLastChecked = TimeStamp::Now();
     mSlackCount = 0;
     mDelay = mMinInterval;
 
     RearmTimer();
   } else if (!strcmp(aTopic, kAbortCaptivePortalLoginEvent)) {
     // The login has been aborted
-    mState = UNKNOWN;
+    StateTransition(UNKNOWN);
     mLastChecked = TimeStamp::Now();
     mSlackCount = 0;
   }
 
   // Send notification so that the captive portal state is mirrored in the
   // content process.
   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   if (observerService) {
@@ -375,22 +363,38 @@ CaptivePortalService::Complete(bool succ
   mLastChecked = TimeStamp::Now();
 
   // Note: this callback gets called when:
   // 1. the request is completed, and content is valid (success == true)
   // 2. when the request is aborted or times out (success == false)
 
   if (success) {
     if (mEverBeenCaptive) {
-      mState = UNLOCKED_PORTAL;
+      StateTransition(UNLOCKED_PORTAL);
       NotifyConnectivityAvailable(true);
     } else {
-      mState = NOT_CAPTIVE;
+      StateTransition(NOT_CAPTIVE);
       NotifyConnectivityAvailable(false);
     }
   }
 
   mRequestInProgress = false;
   return NS_OK;
 }
 
+void CaptivePortalService::StateTransition(int32_t aNewState) {
+  int32_t oldState = mState;
+  mState = aNewState;
+
+  if ((oldState == UNKNOWN && mState == NOT_CAPTIVE) ||
+      (oldState == LOCKED_PORTAL && mState == UNLOCKED_PORTAL)) {
+    nsCOMPtr<nsIObserverService> observerService =
+        services::GetObserverService();
+    if (observerService) {
+      nsCOMPtr<nsICaptivePortalService> cps(this);
+      observerService->NotifyObservers(
+          cps, NS_CAPTIVE_PORTAL_CONNECTIVITY_CHANGED, nullptr);
+    }
+  }
+}
+
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/base/CaptivePortalService.h
+++ b/netwerk/base/CaptivePortalService.h
@@ -37,37 +37,41 @@ class CaptivePortalService : public nsIC
 
   static already_AddRefed<nsICaptivePortalService> GetSingleton();
 
   // This method is only called in the content process, in order to mirror
   // the captive portal state in the parent process.
   void SetStateInChild(int32_t aState);
 
  private:
+  static const uint32_t kDefaultInterval = 60 * 1000;  // check every 60 seconds
+
   CaptivePortalService();
   virtual ~CaptivePortalService();
   nsresult PerformCheck();
   nsresult RearmTimer();
   void NotifyConnectivityAvailable(bool aCaptive);
 
   nsCOMPtr<nsICaptivePortalDetector> mCaptivePortalDetector;
-  int32_t mState;
+  int32_t mState{UNKNOWN};
 
   nsCOMPtr<nsITimer> mTimer;
-  bool mStarted;
-  bool mInitialized;
-  bool mRequestInProgress;
-  bool mEverBeenCaptive;
+  bool mStarted{false};
+  bool mInitialized{false};
+  bool mRequestInProgress{false};
+  bool mEverBeenCaptive{false};
 
-  uint32_t mDelay;
-  int32_t mSlackCount;
+  uint32_t mDelay{kDefaultInterval};
+  int32_t mSlackCount{0};
 
-  uint32_t mMinInterval;
-  uint32_t mMaxInterval;
-  float mBackoffFactor;
+  uint32_t mMinInterval{kDefaultInterval};
+  uint32_t mMaxInterval{25 * kDefaultInterval};
+  float mBackoffFactor{5.0};
+
+  void StateTransition(int32_t aNewState);
 
   // This holds a timestamp when the last time when the captive portal check
   // has changed state.
   mozilla::TimeStamp mLastChecked;
 };
 
 }  // namespace net
 }  // namespace mozilla
--- a/netwerk/base/nsICaptivePortalService.idl
+++ b/netwerk/base/nsICaptivePortalService.idl
@@ -68,9 +68,16 @@ interface nsICaptivePortalService : nsIS
  * This notification will be emitted when the captive portal service has
  * determined that we can connect to the internet.
  * The service will pass either "captive" if there is an unlocked captive portal
  * present, or "clear" if no captive portal was detected.
  * Note: this notification only gets sent in the parent process.
  */
 #define NS_CAPTIVE_PORTAL_CONNECTIVITY "network:captive-portal-connectivity"
 
+/**
+ * Similar to NS_CAPTIVE_PORTAL_CONNECTIVITY but only gets dispatched when
+ * the connectivity changes from UNKNOWN to NOT_CAPTIVE or from LOCKED_PORTAL
+ * to UNLOCKED_PORTAL.
+ */
+#define NS_CAPTIVE_PORTAL_CONNECTIVITY_CHANGED "network:captive-portal-connectivity-changed"
+
 %}
--- a/netwerk/protocol/http/Http3Session.cpp
+++ b/netwerk/protocol/http/Http3Session.cpp
@@ -1737,16 +1737,19 @@ void Http3Session::CloseConnectionTeleme
     case CloseError::Tag::PeerError:
       key = "peer_transport"_ns;
       value = GetTransportErrorCodeForTelemetry(key, aError.peer_error._0);
       break;
     case CloseError::Tag::AppError:
       key = "app"_ns;
       value = GetAppErrorCodeForTelemetry(aError.app_error._0);
       break;
+    case CloseError::Tag::EchRetry:
+      key = "transport_crypto_alert"_ns;
+      value = 121;
   }
 
   key.Append(aClosing ? "_closing"_ns : "_closed"_ns);
 
   Telemetry::Accumulate(Telemetry::HTTP3_CONNECTION_CLOSE_CODE_3, key, value);
 
   Http3Stats stats{};
   mHttp3Connection->GetStats(&stats);
--- a/netwerk/socket/neqo_glue/Cargo.toml
+++ b/netwerk/socket/neqo_glue/Cargo.toml
@@ -3,24 +3,24 @@ name = "neqo_glue"
 version = "0.1.0"
 authors = ["Dragana Damjanovic <dd.mozilla@gmail.com>"]
 edition = "2018"
 
 [lib]
 name = "neqo_glue"
 
 [dependencies]
-neqo-http3 = { tag = "v0.4.25", git = "https://github.com/mozilla/neqo" }
-neqo-transport = { tag = "v0.4.25", git = "https://github.com/mozilla/neqo" }
-neqo-common = { tag = "v0.4.25", git = "https://github.com/mozilla/neqo" }
-neqo-qpack = { tag = "v0.4.25", git = "https://github.com/mozilla/neqo" }
+neqo-http3 = { tag = "v0.4.26", git = "https://github.com/mozilla/neqo" }
+neqo-transport = { tag = "v0.4.26", git = "https://github.com/mozilla/neqo" }
+neqo-common = { tag = "v0.4.26", git = "https://github.com/mozilla/neqo" }
+neqo-qpack = { tag = "v0.4.26", git = "https://github.com/mozilla/neqo" }
 nserror = { path = "../../../xpcom/rust/nserror" }
 nsstring = { path = "../../../xpcom/rust/nsstring" }
 xpcom = { path = "../../../xpcom/rust/xpcom" }
 thin-vec = { version = "0.2.1", features = ["gecko-ffi"] }
 log = "0.4.0"
 qlog = "0.4.0"
 
 [dependencies.neqo-crypto]
-tag = "v0.4.25"
+tag = "v0.4.26"
 git = "https://github.com/mozilla/neqo"
 default-features = false
 features = ["gecko"]
--- a/netwerk/socket/neqo_glue/src/lib.rs
+++ b/netwerk/socket/neqo_glue/src/lib.rs
@@ -403,38 +403,42 @@ fn crypto_error_code(err: neqo_crypto::E
         neqo_crypto::Error::MixedHandshakeMethod => 8,
         neqo_crypto::Error::NoDataAvailable => 9,
         neqo_crypto::Error::NssError { .. } => 10,
         neqo_crypto::Error::OverrunError => 11,
         neqo_crypto::Error::SelfEncryptFailure => 12,
         neqo_crypto::Error::TimeTravelError => 13,
         neqo_crypto::Error::UnsupportedCipher => 14,
         neqo_crypto::Error::UnsupportedVersion => 15,
+        neqo_crypto::Error::StringError => 16,
+        neqo_crypto::Error::EchRetry(_) => 17,
     }
 }
 
 // This is only used for telemetry. Therefore we only return error code
 // numbers and do not label them. Recording telemetry is easier with a
 // number.
 #[repr(C)]
 pub enum CloseError {
     TransportInternalError(u16),
     TransportInternalErrorOther(u16),
     TransportError(u64),
     CryptoError(u64),
     CryptoAlert(u8),
     PeerAppError(u64),
     PeerError(u64),
     AppError(u64),
+    EchRetry,
 }
 
 impl From<TransportError> for CloseError {
     fn from(error: TransportError) -> CloseError {
         match error {
             TransportError::InternalError(c) => CloseError::TransportInternalError(c),
+            TransportError::CryptoError(neqo_crypto::Error::EchRetry(_)) => CloseError::EchRetry,
             TransportError::CryptoError(c) => CloseError::CryptoError(crypto_error_code(c)),
             TransportError::CryptoAlert(c) => CloseError::CryptoAlert(c),
             TransportError::PeerApplicationError(c) => CloseError::PeerAppError(c),
             TransportError::PeerError(c) => CloseError::PeerError(c),
             TransportError::NoError
             | TransportError::IdleTimeout
             | TransportError::ConnectionRefused
             | TransportError::FlowControlError
@@ -443,16 +447,17 @@ impl From<TransportError> for CloseError
             | TransportError::FinalSizeError
             | TransportError::FrameEncodingError
             | TransportError::TransportParameterError
             | TransportError::ProtocolViolation
             | TransportError::InvalidToken
             | TransportError::KeysExhausted
             | TransportError::ApplicationError
             | TransportError::NoAvailablePath => CloseError::TransportError(error.code()),
+            TransportError::EchRetry(_) => CloseError::EchRetry,
             TransportError::AckedUnsentPacket => CloseError::TransportInternalErrorOther(0),
             TransportError::ConnectionIdLimitExceeded => CloseError::TransportInternalErrorOther(1),
             TransportError::ConnectionIdsExhausted => CloseError::TransportInternalErrorOther(2),
             TransportError::ConnectionState => CloseError::TransportInternalErrorOther(3),
             TransportError::DecodingFrame => CloseError::TransportInternalErrorOther(4),
             TransportError::DecryptError => CloseError::TransportInternalErrorOther(5),
             TransportError::HandshakeFailed => CloseError::TransportInternalErrorOther(6),
             TransportError::IntegerOverflow => CloseError::TransportInternalErrorOther(7),
@@ -570,16 +575,17 @@ pub enum Http3Event {
         error: CloseError,
     },
     ConnectionClosed {
         error: CloseError,
     },
     ResumptionToken {
         expire_in: u64, // microseconds
     },
+    EchFallbackAuthenticationNeeded,
     NoEvent,
 }
 
 fn convert_h3_to_h1_headers(
     headers: Vec<(String, String)>,
     ret_headers: &mut ThinVec<u8>,
 ) -> nsresult {
     if headers.iter().filter(|(k, _)| k == ":status").count() != 1 {
@@ -698,24 +704,50 @@ pub extern "C" fn neqo_http3conn_event(
                     }
                 } else {
                     Http3Event::NoEvent
                 }
             }
             Http3ClientEvent::GoawayReceived => Http3Event::GoawayReceived,
             Http3ClientEvent::StateChange(state) => match state {
                 Http3State::Connected => Http3Event::ConnectionConnected,
-                Http3State::Closing(error_code) => Http3Event::ConnectionClosing {
-                    error: error_code.into(),
+                Http3State::Closing(error_code) => {
+                    match error_code {
+                        neqo_transport::ConnectionError::Transport(
+                            TransportError::CryptoError(neqo_crypto::Error::EchRetry(ref c)),
+                        )
+                        | neqo_transport::ConnectionError::Transport(TransportError::EchRetry(
+                            ref c,
+                        )) => {
+                            data.extend_from_slice(c.as_ref());
+                        }
+                        _ => {}
+                    }
+                    Http3Event::ConnectionClosing { error: error_code.into() }
                 },
-                Http3State::Closed(error_code) => Http3Event::ConnectionClosed {
-                    error: error_code.into(),
+                Http3State::Closed(error_code) => {
+                    match error_code {
+                        neqo_transport::ConnectionError::Transport(
+                            TransportError::CryptoError(neqo_crypto::Error::EchRetry(ref c)),
+                        )
+                        | neqo_transport::ConnectionError::Transport(TransportError::EchRetry(
+                            ref c,
+                        )) => {
+                            data.extend_from_slice(c.as_ref());
+                        }
+                        _ => {}
+                    }
+                    Http3Event::ConnectionClosed { error: error_code.into() }
                 },
                 _ => Http3Event::NoEvent,
             },
+            Http3ClientEvent::EchFallbackAuthenticationNeeded { public_name } => {
+                data.extend_from_slice(public_name.as_ref());
+                Http3Event::EchFallbackAuthenticationNeeded
+            }
         };
 
         if !matches!(fe, Http3Event::NoEvent) {
             *ret_event = fe;
             return NS_OK;
         }
     }
 
--- a/netwerk/test/http3server/Cargo.toml
+++ b/netwerk/test/http3server/Cargo.toml
@@ -1,25 +1,25 @@
 [package]
 name = "http3server"
 version = "0.1.1"
 authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"]
 edition = "2018"
 
 [dependencies]
-neqo-transport = { tag = "v0.4.25", git = "https://github.com/mozilla/neqo" }
-neqo-common = { tag = "v0.4.25", git = "https://github.com/mozilla/neqo" }
-neqo-http3 = { tag = "v0.4.25", git = "https://github.com/mozilla/neqo" }
-neqo-qpack = { tag = "v0.4.25", git = "https://github.com/mozilla/neqo" }
+neqo-transport = { tag = "v0.4.26", git = "https://github.com/mozilla/neqo" }
+neqo-common = { tag = "v0.4.26", git = "https://github.com/mozilla/neqo" }
+neqo-http3 = { tag = "v0.4.26", git = "https://github.com/mozilla/neqo" }
+neqo-qpack = { tag = "v0.4.26", git = "https://github.com/mozilla/neqo" }
 mio = "0.6.17"
 mio-extras = "2.0.5"
 log = "0.4.0"
 
 [dependencies.neqo-crypto]
-tag = "v0.4.25"
+tag = "v0.4.26"
 git = "https://github.com/mozilla/neqo"
 default-features = false
 features = ["gecko"]
 
 # Make sure to use bindgen's runtime-loading of libclang, as it allows for a wider range of clang versions to be used
 [build-dependencies]
 bindgen = {version = "0.56", default-features = false, features = ["runtime"] }
 
--- a/netwerk/test/unit/test_captive_portal_service.js
+++ b/netwerk/test/unit/test_captive_portal_service.js
@@ -1,22 +1,24 @@
 "use strict";
 
 const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
 
 let httpserver = null;
 XPCOMUtils.defineLazyGetter(this, "cpURI", function() {
-  return "http://localhost:" + httpserver.identity.primaryPort + "/captive.txt";
+  return (
+    "http://localhost:" + httpserver.identity.primaryPort + "/captive.html"
+  );
 });
 
 const SUCCESS_STRING =
   '<meta http-equiv="refresh" content="0;url=https://support.mozilla.org/kb/captive-portal"/>';
 let cpResponse = SUCCESS_STRING;
 function contentHandler(metadata, response) {
-  response.setHeader("Content-Type", "text/plain");
+  response.setHeader("Content-Type", "text/html");
   response.bodyOutputStream.write(cpResponse, cpResponse.length);
 }
 
 const PREF_CAPTIVE_ENABLED = "network.captive-portal-service.enabled";
 const PREF_CAPTIVE_TESTMODE = "network.captive-portal-service.testMode";
 const PREF_CAPTIVE_ENDPOINT = "captivedetect.canonicalURL";
 const PREF_CAPTIVE_MINTIME = "network.captive-portal-service.minInterval";
 const PREF_CAPTIVE_MAXTIME = "network.captive-portal-service.maxInterval";
@@ -51,17 +53,17 @@ function observerPromise(topic) {
       },
     };
     Services.obs.addObserver(observer, topic);
   });
 }
 
 add_task(function setup() {
   httpserver = new HttpServer();
-  httpserver.registerPathHandler("/captive.txt", contentHandler);
+  httpserver.registerPathHandler("/captive.html", contentHandler);
   httpserver.start(-1);
 
   Services.prefs.setCharPref(PREF_CAPTIVE_ENDPOINT, cpURI);
   Services.prefs.setIntPref(PREF_CAPTIVE_MINTIME, 50);
   Services.prefs.setIntPref(PREF_CAPTIVE_MAXTIME, 100);
   Services.prefs.setBoolPref(PREF_CAPTIVE_TESTMODE, true);
   Services.prefs.setBoolPref(PREF_DNS_NATIVE_IS_LOCALHOST, true);
 });
@@ -95,20 +97,20 @@ add_task(async function test_simple() {
 
 // This test redirects to another URL which returns the same content.
 // It should still be interpreted as a captive portal.
 add_task(async function test_redirect_success() {
   Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, false);
   equal(cps.state, Ci.nsICaptivePortalService.UNKNOWN);
 
   httpserver.registerPathHandler("/succ.txt", (metadata, response) => {
-    response.setHeader("Content-Type", "text/plain");
+    response.setHeader("Content-Type", "text/html");
     response.bodyOutputStream.write(cpResponse, cpResponse.length);
   });
-  httpserver.registerPathHandler("/captive.txt", (metadata, response) => {
+  httpserver.registerPathHandler("/captive.html", (metadata, response) => {
     response.setStatusLine(metadata.httpVersion, 307, "Moved Temporarily");
     response.setHeader(
       "Location",
       `http://localhost:${httpserver.identity.primaryPort}/succ.txt`
     );
   });
 
   let notification = observerPromise("captive-portal-login").then(
@@ -130,21 +132,21 @@ add_task(async function test_redirect_su
 
 // This redirects to another URI with a different content.
 // We check that it triggers a captive portal login
 add_task(async function test_redirect_bad() {
   Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, false);
   equal(cps.state, Ci.nsICaptivePortalService.UNKNOWN);
 
   httpserver.registerPathHandler("/bad.txt", (metadata, response) => {
-    response.setHeader("Content-Type", "text/plain");
+    response.setHeader("Content-Type", "text/html");
     response.bodyOutputStream.write("bad", "bad".length);
   });
 
-  httpserver.registerPathHandler("/captive.txt", (metadata, response) => {
+  httpserver.registerPathHandler("/captive.html", (metadata, response) => {
     response.setStatusLine(metadata.httpVersion, 307, "Moved Temporarily");
     response.setHeader(
       "Location",
       `http://localhost:${httpserver.identity.primaryPort}/bad.txt`
     );
   });
 
   let notification = observerPromise("captive-portal-login");
@@ -160,17 +162,17 @@ add_task(async function test_redirect_ba
 
 // This redirects to the same URI.
 // We check that it triggers a captive portal login
 add_task(async function test_redirect_loop() {
   Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, false);
   equal(cps.state, Ci.nsICaptivePortalService.UNKNOWN);
 
   // This is actually a redirect loop
-  httpserver.registerPathHandler("/captive.txt", (metadata, response) => {
+  httpserver.registerPathHandler("/captive.html", (metadata, response) => {
     response.setStatusLine(metadata.httpVersion, 307, "Moved Temporarily");
     response.setHeader("Location", cpURI);
   });
 
   let notification = observerPromise("captive-portal-login");
   Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, true);
 
   await notification;
@@ -185,24 +187,84 @@ add_task(async function test_redirect_ht
 
   let h2Port = Cc["@mozilla.org/process/environment;1"]
     .getService(Ci.nsIEnvironment)
     .get("MOZHTTP2_PORT");
   Assert.notEqual(h2Port, null);
   Assert.notEqual(h2Port, "");
 
   // Any kind of redirection should trigger the captive portal login.
-  httpserver.registerPathHandler("/captive.txt", (metadata, response) => {
+  httpserver.registerPathHandler("/captive.html", (metadata, response) => {
     response.setStatusLine(metadata.httpVersion, 307, "Moved Temporarily");
     response.setHeader("Location", `https://foo.example.com:${h2Port}/exit`);
   });
 
   let notification = observerPromise("captive-portal-login");
   Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, true);
 
   await notification;
   equal(
     cps.state,
     Ci.nsICaptivePortalService.LOCKED_PORTAL,
     "Should be locked after redirect to https"
   );
   Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, false);
 });
+
+add_task(async function test_changed_notification() {
+  Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, false);
+  equal(cps.state, Ci.nsICaptivePortalService.UNKNOWN);
+
+  httpserver.registerPathHandler("/captive.html", contentHandler);
+  cpResponse = SUCCESS_STRING;
+
+  let changedNotificationCount = 0;
+  let observer = {
+    QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+    observe(aSubject, aTopic, aData) {
+      changedNotificationCount += 1;
+    },
+  };
+  Services.obs.addObserver(
+    observer,
+    "network:captive-portal-connectivity-changed"
+  );
+
+  let notification = observerPromise(
+    "network:captive-portal-connectivity-changed"
+  );
+  Services.prefs.setBoolPref(PREF_CAPTIVE_ENABLED, true);
+  await notification;
+  equal(changedNotificationCount, 1);
+  equal(cps.state, Ci.nsICaptivePortalService.NOT_CAPTIVE);
+
+  notification = observerPromise("network:captive-portal-connectivity");
+  cps.recheckCaptivePortal();
+  await notification;
+  equal(changedNotificationCount, 1);
+  equal(cps.state, Ci.nsICaptivePortalService.NOT_CAPTIVE);
+
+  notification = observerPromise("captive-portal-login");
+  cpResponse = "you are captive";
+  cps.recheckCaptivePortal();
+  await notification;
+  equal(changedNotificationCount, 1);
+  equal(cps.state, Ci.nsICaptivePortalService.LOCKED_PORTAL);
+
+  notification = observerPromise("captive-portal-login-success");
+  cpResponse = SUCCESS_STRING;
+  cps.recheckCaptivePortal();
+  await notification;
+  equal(changedNotificationCount, 2);
+  equal(cps.state, Ci.nsICaptivePortalService.UNLOCKED_PORTAL);
+
+  notification = observerPromise("captive-portal-login");
+  cpResponse = "you are captive";
+  cps.recheckCaptivePortal();
+  await notification;
+  equal(changedNotificationCount, 2);
+  equal(cps.state, Ci.nsICaptivePortalService.LOCKED_PORTAL);
+
+  Services.obs.removeObserver(
+    observer,
+    "network:captive-portal-connectivity-changed"
+  );
+});
--- a/security/manager/ssl/nsCertOverrideService.cpp
+++ b/security/manager/ssl/nsCertOverrideService.cpp
@@ -436,27 +436,16 @@ nsresult nsCertOverrideService::Write(co
         this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
         u"nsCertOverrideService writing data"_ns);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
-static nsresult GetCertSha256Fingerprint(nsIX509Cert* aCert,
-                                         nsCString& aResult) {
-  nsAutoString fpStrUTF16;
-  nsresult rv = aCert->GetSha256Fingerprint(fpStrUTF16);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  aResult.Assign(NS_ConvertUTF16toUTF8(fpStrUTF16));
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 nsCertOverrideService::RememberValidityOverride(
     const nsACString& aHostName, int32_t aPort,
     const OriginAttributes& aOriginAttributes, nsIX509Cert* aCert,
     uint32_t aOverrideBits, bool aTemporary) {
   NS_ENSURE_ARG_POINTER(aCert);
   if (aHostName.IsEmpty() || !IsAscii(aHostName)) {
     return NS_ERROR_INVALID_ARG;
--- a/security/manager/ssl/nsClientAuthRemember.cpp
+++ b/security/manager/ssl/nsClientAuthRemember.cpp
@@ -147,49 +147,47 @@ nsClientAuthRememberService::DeleteDecis
   }
   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(NS_NSSCOMPONENT_CID));
   if (!nssComponent) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   return nssComponent->ClearSSLExternalAndInternalSessionCache();
 }
 
-static nsresult GetCertSha256Fingerprint(CERTCertificate* aNssCert,
-                                         nsCString& aResult) {
-  nsCOMPtr<nsIX509Cert> cert(nsNSSCertificate::Create(aNssCert));
-  nsAutoString fpStrUTF16;
-  nsresult rv = cert->GetSha256Fingerprint(fpStrUTF16);
-  if (NS_FAILED(rv)) {
-    return rv;
+NS_IMETHODIMP
+nsClientAuthRememberService::RememberDecisionScriptable(
+    const nsACString& aHostName, JS::Handle<JS::Value> aOriginAttributes,
+    nsIX509Cert* aServerCert, nsIX509Cert* aClientCert, JSContext* aCx) {
+  OriginAttributes attrs;
+  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+    return NS_ERROR_INVALID_ARG;
   }
-  aResult.Assign(NS_ConvertUTF16toUTF8(fpStrUTF16));
-  return NS_OK;
+  return RememberDecision(aHostName, attrs, aServerCert, aClientCert);
 }
 
 NS_IMETHODIMP
 nsClientAuthRememberService::RememberDecision(
     const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
-    CERTCertificate* aServerCert, CERTCertificate* aClientCert) {
+    nsIX509Cert* aServerCert, nsIX509Cert* aClientCert) {
   // aClientCert == nullptr means: remember that user does not want to use a
   // cert
   NS_ENSURE_ARG_POINTER(aServerCert);
   if (aHostName.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsAutoCString fpStr;
   nsresult rv = GetCertSha256Fingerprint(aServerCert, fpStr);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (aClientCert) {
-    RefPtr<nsNSSCertificate> pipCert(new nsNSSCertificate(aClientCert));
     nsAutoCString dbkey;
-    rv = pipCert->GetDbKey(dbkey);
+    rv = aClientCert->GetDbKey(dbkey);
     if (NS_SUCCEEDED(rv)) {
       AddEntryToList(aHostName, aOriginAttributes, fpStr, dbkey);
     }
   } else {
     AddEntryToList(aHostName, aOriginAttributes, fpStr,
                    nsClientAuthRemember::SentinelValue);
   }
 
@@ -250,17 +248,17 @@ nsresult CheckForPreferredCertificate(co
   }
   return cert->GetDbKey(aCertDBKey);
 }
 #endif
 
 NS_IMETHODIMP
 nsClientAuthRememberService::HasRememberedDecision(
     const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
-    CERTCertificate* aCert, nsACString& aCertDBKey, bool* aRetVal) {
+    nsIX509Cert* aCert, nsACString& aCertDBKey, bool* aRetVal) {
   if (aHostName.IsEmpty()) return NS_ERROR_INVALID_ARG;
 
   NS_ENSURE_ARG_POINTER(aCert);
   NS_ENSURE_ARG_POINTER(aRetVal);
   *aRetVal = false;
   aCertDBKey.Truncate();
 
   nsAutoCString fpStr;
@@ -291,16 +289,27 @@ nsClientAuthRememberService::HasRemember
     *aRetVal = true;
     return NS_OK;
   }
 #endif
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsClientAuthRememberService::HasRememberedDecisionScriptable(
+    const nsACString& aHostName, JS::Handle<JS::Value> aOriginAttributes,
+    nsIX509Cert* aCert, nsACString& aCertDBKey, JSContext* aCx, bool* aRetVal) {
+  OriginAttributes attrs;
+  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  return HasRememberedDecision(aHostName, attrs, aCert, aCertDBKey, aRetVal);
+}
+
 nsresult nsClientAuthRememberService::AddEntryToList(
     const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
     const nsACString& aFingerprint, const nsACString& aDBKey) {
   nsAutoCString entryKey;
   GetEntryKey(aHostName, aOriginAttributes, aFingerprint, entryKey);
   DataStorageType storageType = GetDataStorageType(aOriginAttributes);
 
   nsCString tmpDbKey(aDBKey);
--- a/security/manager/ssl/nsIClientAuthRememberService.idl
+++ b/security/manager/ssl/nsIClientAuthRememberService.idl
@@ -2,21 +2,21 @@
  *
  * 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 "nsISupports.idl"
 
 %{C++
-#include "cert.h"
 #define NS_CLIENTAUTHREMEMBERSERVICE_CONTRACTID "@mozilla.org/security/clientAuthRememberService;1"
 %}
 
-[ptr] native CERTCertificatePtr(CERTCertificate);
+interface nsIX509Cert;
+
 [ref] native const_OriginAttributesRef(const mozilla::OriginAttributes);
 
 [scriptable, uuid(e92825af-7e81-4b5c-b412-8e1dd36d14fe)]
 interface nsIClientAuthRememberRecord : nsISupports
 {
 
   readonly attribute ACString asciiHost;
 
@@ -35,28 +35,40 @@ interface nsIClientAuthRememberService :
 
   [must_use]
   void forgetRememberedDecision(in ACString key);
 
 
   [must_use]
   Array<nsIClientAuthRememberRecord> getDecisions();
 
+
   [must_use, noscript]
   void rememberDecision(in ACString aHostName,
                         in const_OriginAttributesRef aOriginAttributes,
-                        in CERTCertificatePtr aServerCert,
-                        in CERTCertificatePtr aClientCert);
+                        in nsIX509Cert aServerCert,
+                        in nsIX509Cert aClientCert);
+
+  [implicit_jscontext]
+  void rememberDecisionScriptable(in ACString aHostName,
+                                  in jsval originAttributes,
+                                  in nsIX509Cert aServerCert,
+                                  in nsIX509Cert aClientCert);
 
   [must_use, noscript]
   bool hasRememberedDecision(in ACString aHostName,
                              in const_OriginAttributesRef aOriginAttributes,
-                             in CERTCertificatePtr aServerCert,
+                             in nsIX509Cert aServerCert,
                              out ACString aCertDBKey);
 
+  [implicit_jscontext]
+  bool hasRememberedDecisionScriptable(in ACString aHostName,
+                                       in jsval originAttributes,
+                                       in nsIX509Cert aServerCert,
+                                       out ACString aCertDBKey);
+
   [must_use]
   void clearRememberedDecisions();
 
   [implicit_jscontext]
   void deleteDecisionsByHost(in ACString aHostName,
                              in jsval aOriginAttributes);
-
 };
--- a/security/manager/ssl/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/nsNSSCertHelper.cpp
@@ -93,8 +93,18 @@ void LossyUTF8ToUTF16(const char* str, u
   auto span = Span(str, len);
   if (IsUtf8(span)) {
     CopyUTF8toUTF16(span, result);
   } else {
     // Actually Latin1 despite ASCII in the legacy name
     CopyASCIItoUTF16(span, result);
   }
 }
+
+nsresult GetCertSha256Fingerprint(nsIX509Cert* aCert, nsCString& aResult) {
+  nsAutoString fpStrUTF16;
+  nsresult rv = aCert->GetSha256Fingerprint(fpStrUTF16);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  aResult.Assign(NS_ConvertUTF16toUTF8(fpStrUTF16));
+  return NS_OK;
+}
--- a/security/manager/ssl/nsNSSCertHelper.h
+++ b/security/manager/ssl/nsNSSCertHelper.h
@@ -23,9 +23,11 @@ void LossyUTF8ToUTF16(const char* str, u
 
 // Must be used on the main thread only.
 nsresult GetPIPNSSBundleString(const char* stringName, nsAString& result);
 nsresult GetPIPNSSBundleString(const char* stringName, nsACString& result);
 nsresult PIPBundleFormatStringFromName(const char* stringName,
                                        const nsTArray<nsString>& params,
                                        nsAString& result);
 
+nsresult GetCertSha256Fingerprint(nsIX509Cert* aCert, nsCString& aResult);
+
 #endif  // nsNSSCertHelper_h
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -2358,19 +2358,19 @@ void ClientAuthDataRunnable::RunOnTarget
 
   if (mInfo.ProviderTlsFlags() == 0) {
     cars = do_GetService(NS_CLIENTAUTHREMEMBERSERVICE_CONTRACTID);
   }
 
   if (cars) {
     nsCString rememberedDBKey;
     bool found;
-    nsresult rv =
-        cars->HasRememberedDecision(hostname, mInfo.OriginAttributesRef(),
-                                    mServerCert, rememberedDBKey, &found);
+    nsCOMPtr<nsIX509Cert> cert(nsNSSCertificate::Create(mServerCert));
+    nsresult rv = cars->HasRememberedDecision(
+        hostname, mInfo.OriginAttributesRef(), cert, rememberedDBKey, &found);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
     if (found) {
       // An empty dbKey indicates that the user chose not to use a certificate
       // and chose to remember this decision
       if (rememberedDBKey.IsEmpty()) {
         return;
@@ -2458,19 +2458,23 @@ void ClientAuthDataRunnable::RunOnTarget
     if (NS_WARN_IF(!mSelectedCertificate)) {
       return;
     }
     mSelectedKey.reset(
         PK11_FindKeyByAnyCert(mSelectedCertificate.get(), nullptr));
   }
 
   if (cars && wantRemember) {
-    rv = cars->RememberDecision(
-        hostname, mInfo.OriginAttributesRef(), mServerCert,
-        certChosen ? mSelectedCertificate.get() : nullptr);
+    nsCOMPtr<nsIX509Cert> serverCert(nsNSSCertificate::Create(mServerCert));
+    nsCOMPtr<nsIX509Cert> clientCert;
+    if (certChosen) {
+      clientCert = nsNSSCertificate::Create(mSelectedCertificate.get());
+    }
+    rv = cars->RememberDecision(hostname, mInfo.OriginAttributesRef(),
+                                serverCert, clientCert);
     Unused << NS_WARN_IF(NS_FAILED(rv));
   }
 }
 
 mozilla::pkix::Result RemoteClientAuthDataRunnable::BuildChainForCertificate(
     CERTCertificate*, UniqueCERTCertList& builtChain) {
   builtChain.reset(CERT_NewCertList());
   if (!builtChain) {
--- a/security/sandbox/common/test/SandboxTestingChildTests.h
+++ b/security/sandbox/common/test/SandboxTestingChildTests.h
@@ -15,16 +15,17 @@
 #    include <sys/prctl.h>
 #    include <sys/ioctl.h>
 #    include <termios.h>
 #    include <sys/resource.h>
 #    include <sys/time.h>
 #    include <sys/utsname.h>
 #    include <sched.h>
 #    include <sys/syscall.h>
+#    include <sys/un.h>
 #  endif  // XP_LINUX
 #  include <sys/socket.h>
 #  include <sys/stat.h>
 #  include <sys/types.h>
 #  include <time.h>
 #  include <unistd.h>
 #endif
 
@@ -41,26 +42,78 @@ void RunTestsContent(SandboxTestingChild
                    [&] { return fstatat(AT_FDCWD, kAllowedPath, &st, 0); });
   child->ErrnoTest("fstatat_as_lstat"_ns, true, [&] {
     return fstatat(AT_FDCWD, kAllowedPath, &st, AT_SYMLINK_NOFOLLOW);
   });
 
 #  ifdef XP_LINUX
   child->ErrnoTest("fstatat_as_fstat"_ns, true,
                    [&] { return fstatat(0, "", &st, AT_EMPTY_PATH); });
-#  endif  // XP_LINUX
 
   const struct timespec usec = {0, 1000};
   child->ErrnoTest("nanosleep"_ns, true,
                    [&] { return nanosleep(&usec, nullptr); });
 
   struct timespec res = {0, 0};
   child->ErrnoTest("clock_getres"_ns, true,
                    [&] { return clock_getres(CLOCK_REALTIME, &res); });
 
+  // An abstract socket that does not starts with '/', so we don't want it to
+  // work.
+  // Checking ENETUNREACH should be thrown by SandboxBrokerClient::Connect()
+  // when it detects it does not starts with a '/'
+  child->ErrnoValueTest("connect_abstract_blocked"_ns, false, ENETUNREACH, [&] {
+    int sockfd;
+    struct sockaddr_un addr;
+    char str[] = "\0xyz";  // Abstract socket requires first byte to be NULL
+    size_t str_size = 4;
+
+    memset(&addr, 0, sizeof(struct sockaddr_un));
+    addr.sun_family = AF_UNIX;
+    memcpy(&addr.sun_path, str, str_size);
+
+    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sockfd == -1) {
+      return -1;
+    }
+
+    int con_st = connect(sockfd, (struct sockaddr*)&addr,
+                         sizeof(sa_family_t) + str_size);
+    return con_st;
+  });
+
+  // An abstract socket that does starts with /, so we do want it to work.
+  // Checking ECONNREFUSED because this is what the broker should get when
+  // trying to establish the connect call for us.
+  child->ErrnoValueTest("connect_abstract_permit"_ns, false, ECONNREFUSED, [&] {
+    int sockfd;
+    struct sockaddr_un addr;
+    // we re-use actual X path, because this is what is allowed within
+    // SandboxBrokerPolicyFactory::InitContentPolicy()
+    // We can't just use any random path allowed, but one with CONNECT allowed.
+
+    // Abstract socket requires first byte to be NULL
+    char str[] = "\0/tmp/.X11-unix/X";
+    size_t str_size = 17;
+
+    memset(&addr, 0, sizeof(struct sockaddr_un));
+    addr.sun_family = AF_UNIX;
+    memcpy(&addr.sun_path, str, str_size);
+
+    sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sockfd == -1) {
+      return -1;
+    }
+
+    int con_st = connect(sockfd, (struct sockaddr*)&addr,
+                         sizeof(sa_family_t) + str_size);
+    return con_st;
+  });
+#  endif  // XP_LINUX
+
 #else   // XP_UNIX
   child->ReportNoTests();
 #endif  // XP_UNIX
 }
 
 void RunTestsSocket(SandboxTestingChild* child) {
   MOZ_ASSERT(child, "No SandboxTestingChild*?");
 
--- a/security/sandbox/linux/SandboxBrokerClient.cpp
+++ b/security/sandbox/linux/SandboxBrokerClient.cpp
@@ -214,41 +214,61 @@ int SandboxBrokerClient::Rmdir(const cha
 int SandboxBrokerClient::Readlink(const char* aPath, void* aBuff,
                                   size_t aSize) {
   Request req = {SANDBOX_FILE_READLINK, 0, aSize};
   return DoCall(&req, aPath, nullptr, aBuff, false);
 }
 
 int SandboxBrokerClient::Connect(const sockaddr_un* aAddr, size_t aLen,
                                  int aType) {
-  static const size_t maxLen = sizeof(aAddr->sun_path);
+  static constexpr size_t maxLen = sizeof(aAddr->sun_path);
   const char* path = aAddr->sun_path;
   const auto addrEnd = reinterpret_cast<const char*>(aAddr) + aLen;
   // Ensure that the length isn't impossibly small.
   if (addrEnd <= path) {
     return -EINVAL;
   }
   // Unix domain only
   if (aAddr->sun_family != AF_UNIX) {
     return -EAFNOSUPPORT;
   }
   // How much of sun_path may be accessed?
   auto bufLen = static_cast<size_t>(addrEnd - path);
   if (bufLen > maxLen) {
     bufLen = maxLen;
   }
+
+  // Try to handle abstract addresses where the address (the part
+  // after the leading null byte) resembles a pathname: a leading
+  // slash and no embedded nulls.
+  //
+  // `DoCall` expects null-terminated strings, but in this case the
+  // "path" is terminated by the sockaddr length (without a null), so
+  // we need to make a copy.
+  if (bufLen >= 2 && path[0] == '\0' && path[1] == '/' &&
+      !memchr(path + 1, '\0', bufLen - 1)) {
+    char tmpBuf[maxLen];
+    MOZ_RELEASE_ASSERT(bufLen - 1 < maxLen);
+    memcpy(tmpBuf, path + 1, bufLen - 1);
+    tmpBuf[bufLen - 1] = '\0';
+
+    const Request req = {SANDBOX_SOCKET_CONNECT_ABSTRACT, aType, 0};
+    return DoCall(&req, tmpBuf, nullptr, nullptr, true);
+  }
+
   // Require null-termination.  (Linux doesn't require it, but
   // applications usually null-terminate for portability, and not
   // handling unterminated strings means we don't have to copy the path.)
   const size_t pathLen = strnlen(path, bufLen);
   if (pathLen == bufLen) {
     return -ENAMETOOLONG;
   }
-  // Abstract addresses aren't handled (yet?).
+
+  // Abstract addresses are handled only in some specific case, error in others
   if (pathLen == 0) {
-    return -ECONNREFUSED;
+    return -ENETUNREACH;
   }
 
   const Request req = {SANDBOX_SOCKET_CONNECT, aType, 0};
   return DoCall(&req, path, nullptr, nullptr, true);
 }
 
 }  // namespace mozilla
--- a/security/sandbox/linux/broker/SandboxBroker.cpp
+++ b/security/sandbox/linux/broker/SandboxBroker.cpp
@@ -463,47 +463,66 @@ static int DoLink(const char* aPath, con
     return link(aPath, aPath2);
   }
   if (aOper == SandboxBrokerCommon::Operation::SANDBOX_FILE_SYMLINK) {
     return symlink(aPath, aPath2);
   }
   MOZ_CRASH("SandboxBroker: Unknown link operation");
 }
 
-static int DoConnect(const char* aPath, size_t aLen, int aType) {
+static int DoConnect(const char* aPath, size_t aLen, int aType,
+                     bool aIsAbstract) {
   // Deny SOCK_DGRAM for the same reason it's denied for socketpair.
   if (aType != SOCK_STREAM && aType != SOCK_SEQPACKET) {
     errno = EACCES;
     return -1;
   }
   // Ensure that the address is a pathname.  (An empty string
   // resulting from an abstract address probably shouldn't have made
   // it past the policy check, but check explicitly just in case.)
   if (aPath[0] == '\0') {
-    errno = ECONNREFUSED;
+    errno = ENETUNREACH;
     return -1;
   }
 
   // Try to copy the name into a normal-sized sockaddr_un, with
-  // null-termination:
+  // null-termination. Specifically, from man page:
+  //
+  // When the address of an abstract socket is returned, the returned addrlen is
+  // greater than sizeof(sa_family_t) (i.e., greater than 2), and the name of
+  // the socket is contained in the first (addrlen - sizeof(sa_family_t)) bytes
+  // of sun_path.
+  //
+  // As mentionned in `SandboxBrokerClient::Connect()`, `DoCall` expects a
+  // null-terminated string while abstract socket are not. So we receive a copy
+  // here and we have to put things back correctly as a real abstract socket to
+  // perform the brokered `connect()` call.
   struct sockaddr_un sun;
   memset(&sun, 0, sizeof(sun));
   sun.sun_family = AF_UNIX;
-  if (aLen + 1 > sizeof(sun.sun_path)) {
+  char* sunPath = sun.sun_path;
+  size_t sunLen = sizeof(sun.sun_path);
+  size_t addrLen = sizeof(sun);
+  if (aIsAbstract) {
+    *sunPath++ = '\0';
+    sunLen--;
+    addrLen = offsetof(struct sockaddr_un, sun_path) + aLen + 1;
+  }
+  if (aLen + 1 > sunLen) {
     errno = ENAMETOOLONG;
     return -1;
   }
-  memcpy(&sun.sun_path, aPath, aLen);
+  memcpy(sunPath, aPath, aLen);
 
   // Finally, the actual socket connection.
   const int fd = socket(AF_UNIX, aType | SOCK_CLOEXEC, 0);
   if (fd < 0) {
     return -1;
   }
-  if (connect(fd, reinterpret_cast<struct sockaddr*>(&sun), sizeof(sun)) < 0) {
+  if (connect(fd, reinterpret_cast<struct sockaddr*>(&sun), addrLen) < 0) {
     close(fd);
     return -1;
   }
   return fd;
 }
 
 size_t SandboxBroker::RealPath(char* aPath, size_t aBufSize, size_t aPathLen) {
   char* result = realpath(aPath, nullptr);
@@ -961,18 +980,20 @@ void SandboxBroker::ThreadMain(void) {
               resp.mError = -errno;
             }
           } else {
             AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
           }
           break;
 
         case SANDBOX_SOCKET_CONNECT:
+        case SANDBOX_SOCKET_CONNECT_ABSTRACT:
           if (permissive || (perms & MAY_CONNECT) != 0) {
-            openedFd = DoConnect(pathBuf, pathLen, req.mFlags);
+            openedFd = DoConnect(pathBuf, pathLen, req.mFlags,
+                                 req.mOp == SANDBOX_SOCKET_CONNECT_ABSTRACT);
             if (openedFd >= 0) {
               resp.mError = 0;
             } else {
               resp.mError = -errno;
             }
           } else {
             AuditDenial(req.mOp, req.mFlags, perms, pathBuf);
           }
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.cpp
@@ -26,18 +26,30 @@
 // can change.
 #    error "No MSG_CMSG_CLOEXEC?"
 #  endif  // XP_LINUX
 #endif    // MSG_CMSG_CLOEXEC
 
 namespace mozilla {
 
 const char* SandboxBrokerCommon::OperationDescription[] = {
-    "open",  "access", "stat",  "chmod",  "link",     "symlink",
-    "mkdir", "rename", "rmdir", "unlink", "readlink", "connect"};
+    "open",
+    "access",
+    "stat",
+    "chmod",
+    "link",
+    "symlink",
+    "mkdir",
+    "rename",
+    "rmdir",
+    "unlink",
+    "readlink",
+    "connect",
+    "connect-abstract",
+};
 
 /* static */
 ssize_t SandboxBrokerCommon::RecvWithFd(int aFd, const iovec* aIO,
                                         size_t aNumIO, int* aPassedFdPtr) {
   struct msghdr msg = {};
   msg.msg_iov = const_cast<iovec*>(aIO);
   msg.msg_iovlen = aNumIO;
 
--- a/security/sandbox/linux/broker/SandboxBrokerCommon.h
+++ b/security/sandbox/linux/broker/SandboxBrokerCommon.h
@@ -33,16 +33,17 @@ class SandboxBrokerCommon {
     SANDBOX_FILE_LINK,
     SANDBOX_FILE_SYMLINK,
     SANDBOX_FILE_MKDIR,
     SANDBOX_FILE_RENAME,
     SANDBOX_FILE_RMDIR,
     SANDBOX_FILE_UNLINK,
     SANDBOX_FILE_READLINK,
     SANDBOX_SOCKET_CONNECT,
+    SANDBOX_SOCKET_CONNECT_ABSTRACT,
   };
   // String versions of the above
   static const char* OperationDescription[];