Merge autoland to mozilla-central. a=merge
authorButkovits Atila <abutkovits@mozilla.com>
Wed, 12 Jan 2022 23:13:24 +0200
changeset 604410 38711fbec2b1e046cc8261e5f5a36833c00cacca
parent 604296 d163f7c81ef38ebf3d1edaff9e961c925472225f (current diff)
parent 604409 c4da31624b83b2056aac1b5d67129448dfb16dba (diff)
child 604423 c0ea810672ec101da680ca3c9c483a97b00dc13b
push id39143
push userabutkovits@mozilla.com
push dateWed, 12 Jan 2022 21:30:02 +0000
treeherdermozilla-central@38711fbec2b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone98.0a1
first release with
nightly linux32
38711fbec2b1 / 98.0a1 / 20220112213002 / files
nightly linux64
38711fbec2b1 / 98.0a1 / 20220112213002 / files
nightly mac
38711fbec2b1 / 98.0a1 / 20220112213002 / files
nightly win32
38711fbec2b1 / 98.0a1 / 20220112213002 / files
nightly win64
38711fbec2b1 / 98.0a1 / 20220112213002 / 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
testing/web-platform/meta/css/css-text-decor/inheritance.html.ini
widget/gtk/nsNativeThemeGTK.cpp
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -77,30 +77,30 @@ git = "https://github.com/hsivonen/chard
 replace-with = "vendored-sources"
 rev = "ed8a4c6f900a90d4dbc1d64b856e61490a1c3570"
 
 [source."https://github.com/hsivonen/chardetng"]
 git = "https://github.com/hsivonen/chardetng"
 replace-with = "vendored-sources"
 rev = "3484d3e3ebdc8931493aa5df4d7ee9360a90e76b"
 
-[source."https://github.com/grovesNL/glow"]
-git = "https://github.com/grovesNL/glow"
-replace-with = "vendored-sources"
-rev = "5851ca6"
-
 [source."https://github.com/gfx-rs/wgpu"]
 git = "https://github.com/gfx-rs/wgpu"
 replace-with = "vendored-sources"
-rev = "1e593a6"
+rev = "6bc896f"
 
 [source."https://github.com/gfx-rs/naga"]
 git = "https://github.com/gfx-rs/naga"
 replace-with = "vendored-sources"
-rev = "8ffd6ba"
+rev = "c0b7ac7"
+
+[source."https://github.com/gfx-rs/metal-rs"]
+git = "https://github.com/gfx-rs/metal-rs"
+replace-with = "vendored-sources"
+rev = "140c8f4"
 
 [source."https://github.com/chris-zen/coremidi.git"]
 git = "https://github.com/chris-zen/coremidi.git"
 replace-with = "vendored-sources"
 rev = "fc68464b5445caf111e41f643a2e69ccce0b4f83"
 
 [source.crates-io]
 replace-with = "vendored-sources"
--- a/.gitignore
+++ b/.gitignore
@@ -176,8 +176,11 @@ lextab.py
 testing/raptor/.raptor-venv
 testing/raptor/raptor-venv
 testing/raptor/raptor/tests/json/
 testing/raptor/webext/raptor/auto_gen_test_config.js
 
 # Ignore ICU4X experimentation data files.
 # See intl/ICU4X.md for more details.
 config/external/icu4x
+
+# Ignore the index files generated by clangd.
+.cache/clangd/index/
--- a/.hgignore
+++ b/.hgignore
@@ -223,8 +223,11 @@ toolkit/components/certviewer/content/no
 toolkit/components/certviewer/content/package-lock.json
 
 # Ignore Rust/Cargo output from running `cargo` directly for image_builder docker image
 ^taskcluster/docker/image_builder/build-image/target
 
 # Ignore ICU4X experimentation data files.
 # See intl/ICU4X.md for more details.
 ^config/external/icu4x
+
+# Ignore the index files generated by clangd.
+^.cache/clangd/index/
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -119,19 +119,19 @@ version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "ash"
-version = "0.33.3+1.2.191"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219"
+version = "0.35.0+1.2.203"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a7638ce84f8c84d6fd6faa63aa267574d345181ba591c0eeb5550d4c30cd600"
 dependencies = [
  "libloading 0.7.2",
 ]
 
 [[package]]
 name = "ashmem"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2137,18 +2137,19 @@ dependencies = [
 [[package]]
 name = "glob"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 
 [[package]]
 name = "glow"
-version = "0.11.1"
-source = "git+https://github.com/grovesNL/glow?rev=5851ca6#5851ca65e6b6f4f2c59683245c07de1843c85452"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919"
 dependencies = [
  "js-sys",
  "slotmap",
  "wasm-bindgen",
  "web-sys",
 ]
 
 [[package]]
@@ -3049,18 +3050,17 @@ source = "registry+https://github.com/ru
 checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "metal"
 version = "0.23.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0514f491f4cc03632ab399ee01e2c1c1b12d3e1cf2d667c1ff5f87d6dcd2084"
+source = "git+https://github.com/gfx-rs/metal-rs?rev=140c8f4#140c8f4e39001ae154f153ffc767da6c0c9d7f06"
 dependencies = [
  "bitflags",
  "block",
  "core-graphics-types",
  "foreign-types",
  "log",
  "objc",
 ]
@@ -3368,18 +3368,18 @@ dependencies = [
 [[package]]
 name = "murmurhash3"
 version = "0.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
 
 [[package]]
 name = "naga"
-version = "0.7.1"
-source = "git+https://github.com/gfx-rs/naga?rev=8ffd6ba#8ffd6ba929b4b93c9564f08fe8bb34b23fa72a6f"
+version = "0.8.0"
+source = "git+https://github.com/gfx-rs/naga?rev=c0b7ac7#c0b7ac7f542cc42ccac6f2ec3fc1fb01309cf4d7"
 dependencies = [
  "bit-set",
  "bitflags",
  "codespan-reporting",
  "hexf-parse",
  "indexmap",
  "log",
  "num-traits",
@@ -5747,18 +5747,18 @@ source = "registry+https://github.com/ru
 checksum = "98db6ff463a94d727ee7c1188bab33146468add6dfb94df30a1f4a3495a700d9"
 dependencies = [
  "log",
  "url",
 ]
 
 [[package]]
 name = "wgpu-core"
-version = "0.11.0"
-source = "git+https://github.com/gfx-rs/wgpu?rev=1e593a6#1e593a6bd3e60796d00604d803c77bfb41ef3232"
+version = "0.12.0"
+source = "git+https://github.com/gfx-rs/wgpu?rev=6bc896f#6bc896fe8cc6d74e09934afc7f257c0c60aac59f"
 dependencies = [
  "arrayvec 0.7.2",
  "bitflags",
  "cfg_aliases",
  "codespan-reporting",
  "copyless",
  "fxhash",
  "log",
@@ -5770,18 +5770,18 @@ dependencies = [
  "smallvec",
  "thiserror",
  "wgpu-hal",
  "wgpu-types",
 ]
 
 [[package]]
 name = "wgpu-hal"
-version = "0.11.2"
-source = "git+https://github.com/gfx-rs/wgpu?rev=1e593a6#1e593a6bd3e60796d00604d803c77bfb41ef3232"
+version = "0.12.0"
+source = "git+https://github.com/gfx-rs/wgpu?rev=6bc896f#6bc896fe8cc6d74e09934afc7f257c0c60aac59f"
 dependencies = [
  "arrayvec 0.7.2",
  "ash",
  "bit-set",
  "bitflags",
  "block",
  "core-graphics-types",
  "d3d12",
@@ -5807,18 +5807,18 @@ dependencies = [
  "wasm-bindgen",
  "web-sys",
  "wgpu-types",
  "winapi",
 ]
 
 [[package]]
 name = "wgpu-types"
-version = "0.11.0"
-source = "git+https://github.com/gfx-rs/wgpu?rev=1e593a6#1e593a6bd3e60796d00604d803c77bfb41ef3232"
+version = "0.12.0"
+source = "git+https://github.com/gfx-rs/wgpu?rev=6bc896f#6bc896fe8cc6d74e09934afc7f257c0c60aac59f"
 dependencies = [
  "bitflags",
  "bitflags_serde_shim",
  "serde",
 ]
 
 [[package]]
 name = "wgpu_bindings"
--- a/browser/base/content/test/general/browser_documentnavigation.js
+++ b/browser/base/content/test/general/browser_documentnavigation.js
@@ -281,17 +281,18 @@ add_task(async function() {
 
   SidebarUI.toggle("viewBookmarksSidebar");
 });
 
 // Navigate when the downloads panel is open
 add_task(async function test_download_focus() {
   await pushPrefs(
     ["accessibility.tabfocus", 7],
-    ["browser.download.autohideButton", false]
+    ["browser.download.autohideButton", false],
+    ["security.dialog_enable_delay", 0]
   );
   await promiseButtonShown("downloads-button");
 
   let popupShownPromise = BrowserTestUtils.waitForEvent(
     document,
     "popupshown",
     true
   );
--- a/browser/components/downloads/DownloadsViewUI.jsm
+++ b/browser/components/downloads/DownloadsViewUI.jsm
@@ -220,16 +220,21 @@ var DownloadsViewUI = {
     contextMenu.querySelector(".downloadCommandsSeparator").hidden =
       contextMenu.querySelector(".downloadUnblockMenuItem").hidden &&
       contextMenu.querySelector(".downloadShowMenuItem").hidden;
 
     let download = element._shell.download;
     let mimeInfo = DownloadsCommon.getMimeInfo(download);
     let { preferredAction, useSystemDefault } = mimeInfo ? mimeInfo : {};
 
+    // Hide the "Delete" item if there's no file data to delete.
+    contextMenu.querySelector(".downloadDeleteFileMenuItem").hidden = !(
+      download.target?.exists || download.target?.partFileExists
+    );
+
     // Hide the "use system viewer" and "always use system viewer" items
     // if the feature is disabled or this download doesn't support it:
     let useSystemViewerItem = contextMenu.querySelector(
       ".downloadUseSystemDefaultMenuItem"
     );
     let alwaysUseSystemViewerItem = contextMenu.querySelector(
       ".downloadAlwaysUseSystemDefaultMenuItem"
     );
@@ -967,17 +972,19 @@ DownloadsViewUI.DownloadElementShell.pro
       case "downloadsCmd_open":
       case "downloadsCmd_open:current":
       case "downloadsCmd_open:tab":
       case "downloadsCmd_open:tabshifted":
       case "downloadsCmd_open:window":
       case "downloadsCmd_alwaysOpenSimilarFiles":
         // This property is false if the download did not succeed.
         return this.download.target.exists;
+
       case "downloadsCmd_show":
+      case "downloadsCmd_deleteFile":
         let { target } = this.download;
         return target.exists || target.partFileExists;
 
       case "downloadsCmd_delete":
       case "cmd_delete":
         // We don't want in-progress downloads to be removed accidentally.
         return this.download.stopped;
       case "downloadsCmd_openInSystemViewer":
@@ -1060,16 +1067,31 @@ DownloadsViewUI.DownloadElementShell.pro
     // ownership.
     this.cmd_delete();
   },
 
   cmd_delete() {
     DownloadsCommon.deleteDownload(this.download).catch(Cu.reportError);
   },
 
+  async downloadsCmd_deleteFile() {
+    let { download } = this;
+    let { path } = download.target;
+    let { succeeded } = download;
+    // Remove the download from the session and history downloads, delete part files.
+    await DownloadsCommon.deleteDownload(download);
+    // Delete final files.
+    if (succeeded) {
+      // Temp files are made "read-only" by DownloadIntegration.downloadDone, so reset the permission bits to read/write.
+      // This won't be necessary after 1733587 since Downloads won't ever be temporary.
+      await IOUtils.setPermissions(path, 0o660);
+      await IOUtils.remove(path, { ignoreAbsent: true });
+    }
+  },
+
   downloadsCmd_openInSystemViewer() {
     // For this interaction only, pass a flag to override the preferredAction for this
     // mime-type and open using the system viewer
     DownloadsCommon.openDownload(this.download, {
       useSystemDefault: true,
     }).catch(Cu.reportError);
   },
 
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -1125,16 +1125,20 @@ class DownloadsViewItem extends Download
 
         if (!this.download.target.partFilePath) {
           return false;
         }
 
         let partFile = new FileUtils.File(this.download.target.partFilePath);
         return partFile.exists();
       }
+      case "downloadsCmd_deleteFile": {
+        let { target } = this.download;
+        return target.exists || target.partFileExists;
+      }
       case "cmd_delete":
       case "downloadsCmd_copyLocation":
       case "downloadsCmd_doDefault":
         return true;
       case "downloadsCmd_showBlockedInfo":
         return this.download.hasBlockedData;
     }
     return DownloadsViewUI.DownloadElementShell.prototype.isCommandEnabled.call(
@@ -1215,16 +1219,32 @@ class DownloadsViewItem extends Download
     // We explicitly close the panel here to give the user the feedback that
     // their click has been received, and we're handling the action.
     // Otherwise, we'd have to wait for the operating system file manager
     // window to open before the panel closed. This also helps to prevent the
     // user from opening the containing folder several times.
     DownloadsPanel.hidePanel();
   }
 
+  async downloadsCmd_deleteFile() {
+    await super.downloadsCmd_deleteFile();
+    // Protects against an unusual edge case where the user:
+    // 1) downloads a file with Firefox; 2) deletes the file from outside of Firefox, e.g., a file manager;
+    // 3) downloads the same file from the same source; 4) opens the downloads panel and uses the menuitem to delete one of those 2 files;
+    // Under those conditions, Firefox will make 2 view items even though there's only 1 file.
+    // Using this method will only delete the view item it was called on, because this instance is not aware of other view items with identical targets.
+    // So the remaining view item needs to be refreshed to hide the "Delete" option.
+    // That example only concerns 2 duplicate view items but you can have an arbitrary number, so iterate over all items...
+    for (let viewItem of DownloadsView._visibleViewItems.values()) {
+      viewItem.download.refresh().catch(Cu.reportError);
+    }
+    // Don't use DownloadsPanel.hidePanel for this method because it will remove
+    // the view item from the list, which is already sufficient feedback.
+  }
+
   downloadsCmd_showBlockedInfo() {
     DownloadsBlockedSubview.toggle(
       this.element,
       ...this.rawBlockedTitleAndDetails
     );
   }
 
   downloadsCmd_openReferrer() {
--- a/browser/components/downloads/content/downloadsCommands.inc.xhtml
+++ b/browser/components/downloads/content/downloadsCommands.inc.xhtml
@@ -20,9 +20,10 @@
   <command id="downloadsCmd_open:window"/>
   <command id="downloadsCmd_show"/>
   <command id="downloadsCmd_retry"/>
   <command id="downloadsCmd_openReferrer"/>
   <command id="downloadsCmd_clearDownloads"/>
   <command id="downloadsCmd_openInSystemViewer"/>
   <command id="downloadsCmd_alwaysOpenInSystemViewer"/>
   <command id="downloadsCmd_alwaysOpenSimilarFiles"/>
+  <command id="downloadsCmd_deleteFile"/>
 </commandset>
--- a/browser/components/downloads/content/downloadsContextMenu.inc.xhtml
+++ b/browser/components/downloads/content/downloadsContextMenu.inc.xhtml
@@ -32,14 +32,17 @@
 
   <menuitem command="downloadsCmd_openReferrer"
             data-l10n-id="downloads-cmd-go-to-download-page"/>
   <menuitem command="cmd_copy"
             data-l10n-id="downloads-cmd-copy-download-link"/>
 
   <menuseparator/>
 
+  <menuitem command="downloadsCmd_deleteFile"
+            class="downloadDeleteFileMenuItem"
+            data-l10n-id="downloads-cmd-delete-file"/>
   <menuitem command="cmd_delete"
             class="downloadRemoveFromHistoryMenuItem"
             data-l10n-id="downloads-cmd-remove-from-history"/>
   <menuitem command="downloadsCmd_clearDownloads"
             data-l10n-id="downloads-cmd-clear-downloads"/>
 </menupopup>
--- a/browser/components/downloads/content/downloadsPanel.inc.xhtml
+++ b/browser/components/downloads/content/downloadsPanel.inc.xhtml
@@ -41,16 +41,18 @@
   <command id="downloadsCmd_clearList"
            oncommand="goDoCommand('downloadsCmd_clearList')"/>
   <command id="downloadsCmd_openInSystemViewer"
            oncommand="goDoCommand('downloadsCmd_openInSystemViewer')"/>
   <command id="downloadsCmd_alwaysOpenInSystemViewer"
            oncommand="goDoCommand('downloadsCmd_alwaysOpenInSystemViewer')"/>
   <command id="downloadsCmd_alwaysOpenSimilarFiles"
            oncommand="goDoCommand('downloadsCmd_alwaysOpenSimilarFiles')"/>
+  <command id="downloadsCmd_deleteFile"
+           oncommand="goDoCommand('downloadsCmd_deleteFile')"/>
 </commandset>
 
 <!-- For accessibility to screen readers, we use a label on the panel instead
   of the anchor because the panel can also be displayed without an anchor. -->
 <panel id="downloadsPanel"
        data-l10n-id="downloads-panel"
        class="panel-no-padding"
        role="group"
@@ -97,16 +99,19 @@
 
     <menuitem command="downloadsCmd_openReferrer"
               data-l10n-id="downloads-cmd-go-to-download-page"/>
     <menuitem command="downloadsCmd_copyLocation"
               data-l10n-id="downloads-cmd-copy-download-link"/>
 
     <menuseparator/>
 
+    <menuitem command="downloadsCmd_deleteFile"
+              class="downloadDeleteFileMenuItem"
+              data-l10n-id="downloads-cmd-delete-file"/>
     <menuitem command="cmd_delete"
               class="downloadRemoveFromHistoryMenuItem"
               data-l10n-id="downloads-cmd-remove-from-history"/>
     <menuitem command="downloadsCmd_clearList"
               data-l10n-id="downloads-cmd-clear-list"/>
     <menuitem command="downloadsCmd_clearDownloads"
               hidden="true"
               data-l10n-id="downloads-cmd-clear-downloads"/>
--- a/browser/components/downloads/test/browser/browser.ini
+++ b/browser/components/downloads/test/browser/browser.ini
@@ -46,8 +46,9 @@ support-files =
   foo.txt^headers^
 [browser_downloads_panel_height.js]
 [browser_downloads_panel_opens.js]
 [browser_downloads_autohide.js]
 [browser_go_to_download_page.js]
 [browser_pdfjs_preview.js]
 [browser_downloads_pauseResume.js]
 [browser_downloads_panel_focus.js]
+[browser_downloads_context_menu_delete_file.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/downloads/test/browser/browser_downloads_context_menu_delete_file.js
@@ -0,0 +1,91 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let gDownloadDir;
+const TestFiles = {};
+let downloads = [];
+
+async function createDownloadFile() {
+  if (!gDownloadDir) {
+    gDownloadDir = await setDownloadDir();
+  }
+  info("Created download directory: " + gDownloadDir);
+  TestFiles.txt = await createDownloadedFile(
+    PathUtils.join(gDownloadDir, "downloaded.txt"),
+    "Test file"
+  );
+  info("Created downloaded text file at:" + TestFiles.txt.path);
+  info("Setting path for download file");
+  let target = FileUtils.getFile("TmpD", ["downloaded.txt"]);
+  target.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+  downloads.push({
+    state: DownloadsCommon.DOWNLOAD_FINISHED,
+    contentType: "text/plain",
+    target,
+  });
+}
+
+async function prepareDownloadFiles(downloadList) {
+  // prepare downloads
+  await task_addDownloads(downloads);
+  let [download] = await downloadList.getAll();
+  info("Download succeeded? " + download.succeeded);
+  info("Download target exists? " + download.target.exists);
+}
+
+add_task(async function test_download_deleteFile() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["browser.download.improvements_to_download_panel", true]],
+  });
+
+  // remove download files, empty out collections
+  let downloadList = await Downloads.getList(Downloads.ALL);
+  let downloadCount = (await downloadList.getAll()).length;
+  is(downloadCount, 0, "At the start of the test, there should be 0 downloads");
+  await task_resetState();
+  await createDownloadFile();
+  await prepareDownloadFiles(downloadList);
+  await task_openPanel();
+  await TestUtils.waitForCondition(() => {
+    let downloadsListBox = document.getElementById("downloadsListBox");
+    downloadsListBox.removeAttribute("disabled");
+    return downloadsListBox.childElementCount == downloads.length;
+  });
+
+  info("trigger the context menu");
+  let itemTarget = document.querySelector(
+    "#downloadsListBox richlistitem .downloadMainArea"
+  );
+
+  let contextMenu = await openContextMenu(itemTarget);
+  let deleteFileItem = contextMenu.querySelector(
+    '[command="downloadsCmd_deleteFile"]'
+  );
+
+  ok(
+    !BrowserTestUtils.is_hidden(deleteFileItem),
+    "deleteFileItem should be visible"
+  );
+
+  let target = FileUtils.getFile("TmpD", ["downloaded.txt"]);
+  ok(target.exists(), "downloaded.txt should exist");
+  info(`file path: ${target.path}`);
+  let hiddenPromise = BrowserTestUtils.waitForEvent(
+    DownloadsPanel.panel,
+    "popuphidden"
+  );
+
+  deleteFileItem.click();
+
+  await TestUtils.waitForCondition(() => !target.exists());
+
+  await TestUtils.waitForCondition(() => {
+    let downloadsListBox = document.getElementById("downloadsListBox");
+    downloadsListBox.removeAttribute("disabled");
+    return downloadsListBox.childElementCount == 0;
+  });
+  DownloadsPanel.hidePanel();
+  await hiddenPromise;
+});
--- a/browser/components/downloads/test/browser/browser_downloads_panel_context_menu.js
+++ b/browser/components/downloads/test/browser/browser_downloads_panel_context_menu.js
@@ -12,16 +12,17 @@ const MENU_ITEMS = {
   openInSystemViewer: '[command="downloadsCmd_openInSystemViewer"]',
   alwaysOpenInSystemViewer: '[command="downloadsCmd_alwaysOpenInSystemViewer"]',
   alwaysOpenSimilarFiles: '[command="downloadsCmd_alwaysOpenSimilarFiles"]',
   show: '[command="downloadsCmd_show"]',
   commandsSeparator: "menuseparator,.downloadCommandsSeparator",
   openReferrer: '[command="downloadsCmd_openReferrer"]',
   copyLocation: '[command="downloadsCmd_copyLocation"]',
   separator: "menuseparator",
+  deleteFile: '[command="downloadsCmd_deleteFile"]',
   delete: '[command="cmd_delete"]',
   clearList: '[command="downloadsCmd_clearList"]',
   clearDownloads: '[command="downloadsCmd_clearDownloads"]',
 };
 
 const TestCasesDefaultMimetypes = [
   {
     name: "Completed PDF download with improvements pref disabled",
@@ -37,16 +38,17 @@ const TestCasesDefaultMimetypes = [
       menu: [
         MENU_ITEMS.openInSystemViewer,
         MENU_ITEMS.alwaysOpenInSystemViewer,
         MENU_ITEMS.show,
         MENU_ITEMS.commandsSeparator,
         MENU_ITEMS.openReferrer,
         MENU_ITEMS.copyLocation,
         MENU_ITEMS.separator,
+        MENU_ITEMS.deleteFile,
         MENU_ITEMS.delete,
         MENU_ITEMS.clearList,
       ],
     },
   },
   {
     name: "Canceled PDF download with improvements pref disabled",
     prefEnabled: false,
@@ -82,16 +84,17 @@ const TestCasesNewMimetypesPrefDisabled 
     ],
     expected: {
       menu: [
         MENU_ITEMS.show,
         MENU_ITEMS.commandsSeparator,
         MENU_ITEMS.openReferrer,
         MENU_ITEMS.copyLocation,
         MENU_ITEMS.separator,
+        MENU_ITEMS.deleteFile,
         MENU_ITEMS.delete,
         MENU_ITEMS.clearList,
       ],
     },
   },
   {
     name: "Canceled txt download with improvements pref disabled",
     prefEnabled: false,
@@ -128,16 +131,17 @@ const TestCasesNewMimetypesPrefEnabled =
     expected: {
       menu: [
         MENU_ITEMS.alwaysOpenSimilarFiles,
         MENU_ITEMS.show,
         MENU_ITEMS.commandsSeparator,
         MENU_ITEMS.openReferrer,
         MENU_ITEMS.copyLocation,
         MENU_ITEMS.separator,
+        MENU_ITEMS.deleteFile,
         MENU_ITEMS.delete,
         MENU_ITEMS.clearList,
       ],
     },
   },
   {
     name: "Canceled txt download with improvements pref enabled",
     prefEnabled: true,
@@ -172,16 +176,17 @@ const TestCasesNewMimetypesPrefEnabled =
     ],
     expected: {
       menu: [
         MENU_ITEMS.show,
         MENU_ITEMS.commandsSeparator,
         MENU_ITEMS.openReferrer,
         MENU_ITEMS.copyLocation,
         MENU_ITEMS.separator,
+        MENU_ITEMS.deleteFile,
         MENU_ITEMS.delete,
         MENU_ITEMS.clearList,
       ],
     },
   },
   {
     name:
       "Completed txt download with application/octet-stream and improvements pref enabled",
@@ -200,16 +205,17 @@ const TestCasesNewMimetypesPrefEnabled =
         // alwaysOpenSimilarFiles still appears since txt files
         // are supported file types.
         MENU_ITEMS.alwaysOpenSimilarFiles,
         MENU_ITEMS.show,
         MENU_ITEMS.commandsSeparator,
         MENU_ITEMS.openReferrer,
         MENU_ITEMS.copyLocation,
         MENU_ITEMS.separator,
+        MENU_ITEMS.deleteFile,
         MENU_ITEMS.delete,
         MENU_ITEMS.clearList,
       ],
     },
   },
 ];
 
 add_task(async function test_setUp() {
--- a/browser/components/newtab/lib/RemoteL10n.jsm
+++ b/browser/components/newtab/lib/RemoteL10n.jsm
@@ -181,17 +181,17 @@ class _RemoteL10n {
    *   * it was told to use the local Fluent file
    */
   _createDOML10n() {
     /* istanbul ignore next */
     let useRemoteL10n = Services.prefs.getBoolPref(USE_REMOTE_L10N_PREF, true);
     if (useRemoteL10n && !L10nRegistry.getInstance().hasSource("cfr")) {
       const appLocale = Services.locale.appLocaleAsBCP47;
       const l10nFluentDir = PathUtils.join(
-        Services.dirsvc.get("ProfD", Ci.nsIFile).path,
+        Services.dirsvc.get("ProfLD", Ci.nsIFile).path,
         "settings",
         "main",
         "ms-language-packs"
       );
       let cfrIndexedFileSource = new L10nFileSource(
         "cfr",
         "app",
         [appLocale],
--- a/browser/installer/windows/msix/AppxManifest.xml.in
+++ b/browser/installer/windows/msix/AppxManifest.xml.in
@@ -1,14 +1,24 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 <!-- #filter substitution -->
-<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2" xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" IgnorableNamespaces="uap uap2 uap3 uap10 rescap">
+<Package
+  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+  xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
+  xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
+  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+  xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
+  xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
+  xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
+  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
+  IgnorableNamespaces="uap uap2 uap3 uap10 rescap">
+
   <Identity Name="@APPX_IDENTITY@" Publisher="@APPX_PUBLISHER@" Version="@APPX_VERSION@" ProcessorArchitecture="@APPX_ARCH@" />
   <Properties>
     <DisplayName>@APPX_DISPLAYNAME@</DisplayName>
     <PublisherDisplayName>@APPX_PUBLISHER_DISPLAY_NAME@</PublisherDisplayName>
     <Description>@APPX_DESCRIPTION@</Description>
     <Logo>Assets\StoreLogo.png</Logo>
     <uap10:PackageIntegrity>
       <uap10:Content Enforcement="on" />
@@ -24,16 +34,21 @@
     <rescap:Capability Name="runFullTrust" />
   </Capabilities>
   <Applications>
     <Application Id="App" Executable="VFS\ProgramFiles\@APPX_INSTDIR@\@MOZ_APP_NAME@.exe" EntryPoint="Windows.FullTrustApplication">
       <uap:VisualElements BackgroundColor="#20123A" DisplayName="@MOZ_APP_DISPLAYNAME@" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="@MOZ_APP_DISPLAYNAME@">
         <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" Square310x310Logo="Assets\LargeTile.png" Square71x71Logo="Assets\SmallTile.png" />
       </uap:VisualElements>
       <Extensions>
+        <uap3:Extension Category="windows.appExecutionAlias" EntryPoint="Windows.FullTrustApplication" Executable="VFS\ProgramFiles\@APPX_INSTDIR@\@MOZ_APP_NAME@.exe">
+          <uap3:AppExecutionAlias>
+            <desktop:ExecutionAlias Alias="@MOZ_APP_NAME@.exe" />
+          </uap3:AppExecutionAlias>
+        </uap3:Extension>
         <uap3:Extension Category="windows.fileTypeAssociation">
           <uap3:FileTypeAssociation Name="htm">
             <uap:SupportedFileTypes>
               <!-- Keep synchronized with
                    https://searchfox.org/mozilla-central/source/browser/installer/windows/nsis/shared.nsh
                    and `os.environment.launched_to_handle` and `os.environment.invoked_to_handle` telemetry in
                    https://searchfox.org/mozilla-central/source/browser/components/BrowserContentHandler.jsm. -->
               <uap:FileType>.avif</uap:FileType>
--- a/browser/locales/en-US/browser/downloads.ftl
+++ b/browser/locales/en-US/browser/downloads.ftl
@@ -84,16 +84,19 @@ downloads-cmd-copy-download-link =
 downloads-cmd-remove-from-history =
     .label = Remove From History
     .accesskey = e
 downloads-cmd-clear-list =
     .label = Clear Preview Panel
     .accesskey = a
 downloads-cmd-clear-downloads =
     .label = Clear Downloads
+    .accesskey = C
+downloads-cmd-delete-file =
+    .label = Delete
     .accesskey = D
 
 # This command is shown in the context menu when downloads are blocked.
 downloads-cmd-unblock =
     .label = Allow Download
     .accesskey = o
 
 # This is the tooltip of the action button shown when malware is blocked.
--- a/browser/themes/BuiltInThemeConfig.jsm
+++ b/browser/themes/BuiltInThemeConfig.jsm
@@ -35,16 +35,64 @@ const BuiltInThemeConfig = new Map([
   [
     "firefox-alpenglow@mozilla.org",
     {
       version: "1.4",
       path: "resource://builtin-themes/alpenglow/",
     },
   ],
   [
+    "2022red-colorway@mozilla.org",
+    {
+      version: "1.0",
+      path: "resource://builtin-themes/monochromatic/2022red/",
+      expiry: "2022-05-03",
+    },
+  ],
+  [
+    "2022orange-colorway@mozilla.org",
+    {
+      version: "1.0",
+      path: "resource://builtin-themes/monochromatic/2022orange/",
+      expiry: "2022-05-03",
+    },
+  ],
+  [
+    "2022green-colorway@mozilla.org",
+    {
+      version: "1.0",
+      path: "resource://builtin-themes/monochromatic/2022green/",
+      expiry: "2022-05-03",
+    },
+  ],
+  [
+    "2022yellow-colorway@mozilla.org",
+    {
+      version: "1.0",
+      path: "resource://builtin-themes/monochromatic/2022yellow/",
+      expiry: "2022-05-03",
+    },
+  ],
+  [
+    "2022purple-colorway@mozilla.org",
+    {
+      version: "1.0",
+      path: "resource://builtin-themes/monochromatic/2022purple/",
+      expiry: "2022-05-03",
+    },
+  ],
+  [
+    "2022blue-colorway@mozilla.org",
+    {
+      version: "1.0",
+      path: "resource://builtin-themes/monochromatic/2022blue/",
+      expiry: "2022-05-03",
+    },
+  ],
+  [
     "lush-soft-colorway@mozilla.org",
     {
       version: "1.0",
       path: "resource://builtin-themes/monochromatic/lush/soft/",
       expiry: "2022-02-08",
     },
   ],
   [
--- a/browser/themes/BuiltInThemes.jsm
+++ b/browser/themes/BuiltInThemes.jsm
@@ -58,16 +58,25 @@ class _BuiltInThemes {
     if (theme) {
       return `${theme.path}preview.svg`;
     }
 
     return null;
   }
 
   /**
+   * @param {string} id An addon's id string.
+   * @return {boolean}
+   *   True if the theme with id `id` is a monochromatic theme.
+   */
+  isMonochromaticTheme(id) {
+    return id.endsWith("-colorway@mozilla.org");
+  }
+
+  /**
    * If the active theme is built-in, this function calls
    * AddonManager.maybeInstallBuiltinAddon for that theme.
    */
   maybeInstallActiveBuiltInTheme() {
     const activeThemeID = Services.prefs.getStringPref(
       kActiveThemePref,
       "default-theme@mozilla.org"
     );
@@ -86,29 +95,37 @@ class _BuiltInThemes {
    * Ensures that all built-in themes are installed and expired themes are
    * uninstalled.
    */
   async ensureBuiltInThemes() {
     let installPromises = [];
     installPromises.push(this._uninstallExpiredThemes());
 
     const now = new Date();
+    this.monochromaticSortIndices = new Map();
+    let monochromaticSortIndex = 0;
     for (let [id, themeInfo] of this.builtInThemeMap.entries()) {
       if (
         !themeInfo.expiry ||
         retainedThemes.includes(id) ||
         new Date(themeInfo.expiry) > now
       ) {
         installPromises.push(
           AddonManager.maybeInstallBuiltinAddon(
             id,
             themeInfo.version,
             themeInfo.path
           )
         );
+        if (this.isMonochromaticTheme(id)) {
+          // Monochromatic themes get sorted in the UI according to their
+          // position in the config, implied by this loop over
+          // builtInThemeMap.entries().
+          this.monochromaticSortIndices.set(id, monochromaticSortIndex++);
+        }
       }
     }
 
     await Promise.all(installPromises);
   }
 
   /**
    * @param {string} id
--- a/browser/themes/addons/jar.mn
+++ b/browser/themes/addons/jar.mn
@@ -47,8 +47,20 @@ browser.jar:
   content/builtin-themes/monochromatic/graffiti/bold                      (monochromatic/graffiti/bold/*.svg)
   content/builtin-themes/monochromatic/graffiti/bold/manifest.json        (monochromatic/graffiti/bold/manifest.json)
   content/builtin-themes/monochromatic/foto/soft                          (monochromatic/foto/soft/*.svg)
   content/builtin-themes/monochromatic/foto/soft/manifest.json            (monochromatic/foto/soft/manifest.json)
   content/builtin-themes/monochromatic/foto/balanced                      (monochromatic/foto/balanced/*.svg)
   content/builtin-themes/monochromatic/foto/balanced/manifest.json        (monochromatic/foto/balanced/manifest.json)
   content/builtin-themes/monochromatic/foto/bold                          (monochromatic/foto/bold/*.svg)
   content/builtin-themes/monochromatic/foto/bold/manifest.json            (monochromatic/foto/bold/manifest.json)
+  content/builtin-themes/monochromatic/2022blue                   (monochromatic/2022blue/*.svg)
+  content/builtin-themes/monochromatic/2022blue/manifest.json     (monochromatic/2022blue/manifest.json)
+  content/builtin-themes/monochromatic/2022green                  (monochromatic/2022green/*.svg)
+  content/builtin-themes/monochromatic/2022green/manifest.json    (monochromatic/2022green/manifest.json)
+  content/builtin-themes/monochromatic/2022orange                 (monochromatic/2022orange/*.svg)
+  content/builtin-themes/monochromatic/2022orange/manifest.json   (monochromatic/2022orange/manifest.json)
+  content/builtin-themes/monochromatic/2022purple                 (monochromatic/2022purple/*.svg)
+  content/builtin-themes/monochromatic/2022purple/manifest.json   (monochromatic/2022purple/manifest.json)
+  content/builtin-themes/monochromatic/2022red                    (monochromatic/2022red/*.svg)
+  content/builtin-themes/monochromatic/2022red/manifest.json      (monochromatic/2022red/manifest.json)
+  content/builtin-themes/monochromatic/2022yellow                 (monochromatic/2022yellow/*.svg)
+  content/builtin-themes/monochromatic/2022yellow/manifest.json   (monochromatic/2022yellow/manifest.json)
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022blue/icon.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg width="63" height="62" viewBox="0 0 63 62" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="31.5" cy="31" r="31" fill="url(#paint0_linear)"/>
+  <defs>
+    <linearGradient id="paint0_linear" x1="44.4829" y1="19" x2="10.4829" y2="53" gradientUnits="userSpaceOnUse">
+      <stop stop-color="hsl(206, 43%, 65%)"/>
+      <stop offset="1" stop-color="hsl(206, 43%, 78%)"/>
+    </linearGradient>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022blue/manifest.json
@@ -0,0 +1,76 @@
+{
+  "manifest_version": 2,
+  "applications": {
+    "gecko": {
+      "id": "2022blue-colorway@mozilla.org"
+    }
+  },
+  "name": "Chillaxing",
+  "author": "Mozilla",
+  "version": "1.0",
+  "icons": {
+    "32": "icon.svg"
+  },
+  "theme": {
+    "colors": {
+      "tab_background_text": "hsl(0, 0%, 0%)",
+      "tab_text": "hsl(0, 0%, 0%)",
+      "tab_selected": "hsl(0, 0%, 100%)",
+      "tab_line": "transparent",
+      "tab_loading": "hsl(207, 60%, 36%)",
+      "tab_loading_inactive": "hsl(207, 60%, 36%)",
+      "frame": "hsl(206, 43%, 65%)",
+      "popup": "hsl(0, 0%, 100%)",
+      "popup_text": "hsl(0, 0%, 0%)",
+      "popup_border": "hsl(207, 43%, 75%)",
+      "popup_highlight": "hsla(207, 44%, 70%, 0.6)",
+      "popup_highlight_text": "hsl(0, 0%, 0%)",
+      "toolbar": "hsl(206, 43%, 78%)",
+      "toolbar_text": "hsl(0, 0%, 0%)",
+      "toolbar_field": "hsl(206, 43%, 65%)",
+      "toolbar_field_text": "hsl(0, 0%, 0%)",
+      "toolbar_field_focus": "hsl(0, 0%, 100%)",
+      "toolbar_field_border_focus": "rgba(0, 96, 223, 0.5)",
+      "toolbar_top_separator": "transparent",
+      "toolbar_bottom_separator": "hsl(206, 43%, 65%)",
+      "ntp_background": "hsl(207, 43%, 85%)",
+      "ntp_card_background": "hsl(0, 0%, 100%)",
+      "ntp_text": "hsl(0, 0%, 0%)",
+      "sidebar": "hsl(0, 0%, 100%)",
+      "sidebar_text": "hsl(0, 0%, 0%)",
+      "sidebar_highlight": "hsla(207, 44%, 70%, 0.6)",
+      "address_bar_box": "hsl(0, 0%, 100%)",
+      "address_bar_box_hover": "hsla(0, 0%, 100%, 0.8)",
+      "address_bar_box_active": "hsla(0, 0%, 100%, 0.65)",
+      "address_bar_box_focus": "hsl(206, 43%, 78%)",
+      "address_bar_box_text": "hsl(0, 0%, 0%)",
+      "address_bar_url_color": "hsl(207, 43%, 41%)",
+      "panel_item_hover": "hsla(207, 44%, 70%, 0.6)",
+      "panel_item_active": "hsla(207, 44%, 70%, 0.75)",
+      "panel_separator": "hsl(207, 43%, 75%)",
+      "icons_attention": "hsl(207, 60%, 36%)",
+      "toolbar_field_icons_attention": "hsl(207, 60%, 36%)",
+      "tab_attention_dot": "hsl(145, 33%, 40%)",
+      "appmenu_update_icon_color": "hsl(145, 33%, 40%)",
+      "appmenu_info_icon_color": "hsl(206, 50%, 41%)"
+    }
+  },
+  "theme_experiment": {
+    "colors": {
+      "address_bar_box": "--urlbar-box-bgcolor",
+      "address_bar_box_hover": "--urlbar-box-hover-bgcolor",
+      "address_bar_box_active": "--urlbar-box-active-bgcolor",
+      "address_bar_box_focus": "--urlbar-box-focus-bgcolor",
+      "address_bar_box_text": "--urlbar-box-text-color",
+      "address_bar_url_color": "--urlbar-popup-url-color",
+      "panel_item_hover": "--panel-item-hover-bgcolor",
+      "panel_item_active": "--panel-item-active-bgcolor",
+      "panel_separator": "--panel-separator-color",
+      "toolbar_field_icons_attention": "--lwt-toolbar-field-icon-fill-attention",
+      "tab_attention_dot": "--lwt-tab-attention-icon-color",
+      "appmenu_update_icon_color": "--panel-banner-item-update-supported-bgcolor",
+      "appmenu_info_icon_color": "--panel-banner-item-info-icon-bgcolor",
+      "tab_loading_inactive": "--lwt-tab-loading-fill-inactive"
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022blue/preview.svg
@@ -0,0 +1,36 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+  - License, v. 2.0. If a copy of the MPL was not distributed with this
+  - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="680" height="92" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="680" height="92" fill="hsl(206, 43%, 65%)" />
+  <g filter="url(#filter0_dd)">
+    <rect x="28" y="5" width="166" height="34" rx="4" fill="hsl(0, 0%, 100%)" stroke="transparent" stroke-width="1.5" />
+  </g>
+  <rect x="51" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect x="221" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect y="44" width="680" height="48" fill="hsl(206, 43%, 78%)" />
+  <circle cx="24" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <circle cx="60" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="73.75" x2="649" y2="73.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="67.75" x2="649" y2="67.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="61.75" x2="649" y2="61.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="114" y="52" width="488" height="32" rx="4" fill="hsl(206, 43%, 65%)" />
+  <circle cx="130" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="146" y="66" width="308" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <defs>
+  <filter id="filter0_dd" x="24" y="1" width="174" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+    <feFlood flood-opacity="0" result="BackgroundImageFix" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="2" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.5 0" />
+    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="0.5" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.9 0" />
+    <feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow" />
+    <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape" />
+  </filter>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022green/icon.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg width="63" height="62" viewBox="0 0 63 62" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="31.5" cy="31" r="31" fill="url(#paint0_linear)"/>
+  <defs>
+    <linearGradient id="paint0_linear" x1="44.4829" y1="19" x2="10.4829" y2="53" gradientUnits="userSpaceOnUse">
+      <stop stop-color="hsl(142, 32%, 57%)"/>
+      <stop offset="1" stop-color="hsl(142, 32%, 73%)"/>
+    </linearGradient>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022green/manifest.json
@@ -0,0 +1,76 @@
+{
+  "manifest_version": 2,
+  "applications": {
+    "gecko": {
+      "id": "2022green-colorway@mozilla.org"
+    }
+  },
+  "name": "Vegging Out",
+  "author": "Mozilla",
+  "version": "1.0",
+  "icons": {
+    "32": "icon.svg"
+  },
+  "theme": {
+    "colors": {
+      "tab_background_text": "hsl(0, 0%, 0%)",
+      "tab_text": "hsl(0, 0%, 0%)",
+      "tab_selected": "hsl(0, 0%, 100%)",
+      "tab_line": "transparent",
+      "tab_loading": "hsl(142, 24%, 32%)",
+      "tab_loading_inactive": "hsl(142, 24%, 32%)",
+      "frame": "hsl(142, 32%, 57%)",
+      "popup": "hsl(0, 0%, 100%)",
+      "popup_text": "hsl(0, 0%, 0%)",
+      "popup_border": "hsl(142, 25%, 75%)",
+      "popup_highlight": "hsla(142, 32%, 78%, 0.8)",
+      "popup_highlight_text": "hsl(0, 0%, 0%)",
+      "toolbar": "hsl(142, 32%, 73%)",
+      "toolbar_text": "hsl(0, 0%, 0%)",
+      "toolbar_field": "hsl(142, 32%, 57%)",
+      "toolbar_field_text": "hsl(0, 0%, 0%)",
+      "toolbar_field_focus": "hsl(0, 0%, 100%)",
+      "toolbar_field_border_focus": "rgba(0, 96, 223, 0.5)",
+      "toolbar_top_separator": "transparent",
+      "toolbar_bottom_separator": "hsl(142, 32%, 57%)",
+      "ntp_background": "hsl(143, 32%, 89%)",
+      "ntp_card_background": "hsl(0, 0%, 100%)",
+      "ntp_text": "hsl(0, 0%, 0%)",
+      "sidebar": "hsl(0, 0%, 100%)",
+      "sidebar_text": "hsl(0, 0%, 0%)",
+      "sidebar_highlight": "hsla(142, 32%, 78%, 0.8)",
+      "address_bar_box": "hsl(0, 0%, 100%)",
+      "address_bar_box_hover": "hsla(0, 0%, 100%, 0.8)",
+      "address_bar_box_active": "hsla(0, 0%, 100%, 0.65)",
+      "address_bar_box_focus": "hsl(142, 32%, 73%)",
+      "address_bar_box_text": "hsl(0, 0%, 0%)",
+      "address_bar_url_color": "hsl(143, 35%, 30%)",
+      "panel_item_hover": "hsla(142, 32%, 78%, 0.8)",
+      "panel_item_active": "hsla(142, 32%, 78%, 0.95)",
+      "panel_separator": "hsl(142, 25%, 75%)",
+      "icons_attention": "hsl(142, 24%, 32%)",
+      "toolbar_field_icons_attention": "hsl(142, 24%, 32%)",
+      "tab_attention_dot": "hsl(141, 15%, 36%)",
+      "appmenu_update_icon_color": "hsl(141, 15%, 36%)",
+      "appmenu_info_icon_color": "hsl(203, 77%, 31%)"
+    }
+  },
+  "theme_experiment": {
+    "colors": {
+      "address_bar_box": "--urlbar-box-bgcolor",
+      "address_bar_box_hover": "--urlbar-box-hover-bgcolor",
+      "address_bar_box_active": "--urlbar-box-active-bgcolor",
+      "address_bar_box_focus": "--urlbar-box-focus-bgcolor",
+      "address_bar_box_text": "--urlbar-box-text-color",
+      "address_bar_url_color": "--urlbar-popup-url-color",
+      "panel_item_hover": "--panel-item-hover-bgcolor",
+      "panel_item_active": "--panel-item-active-bgcolor",
+      "panel_separator": "--panel-separator-color",
+      "toolbar_field_icons_attention": "--lwt-toolbar-field-icon-fill-attention",
+      "tab_attention_dot": "--lwt-tab-attention-icon-color",
+      "appmenu_update_icon_color": "--panel-banner-item-update-supported-bgcolor",
+      "appmenu_info_icon_color": "--panel-banner-item-info-icon-bgcolor",
+      "tab_loading_inactive": "--lwt-tab-loading-fill-inactive"
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022green/preview.svg
@@ -0,0 +1,36 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+  - License, v. 2.0. If a copy of the MPL was not distributed with this
+  - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="680" height="92" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="680" height="92" fill="hsl(142, 32%, 57%)" />
+  <g filter="url(#filter0_dd)">
+    <rect x="28" y="5" width="166" height="34" rx="4" fill="hsl(0, 0%, 100%)" stroke="transparent" stroke-width="1.5" />
+  </g>
+  <rect x="51" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect x="221" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect y="44" width="680" height="48" fill="hsl(142, 32%, 73%)" />
+  <circle cx="24" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <circle cx="60" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="73.75" x2="649" y2="73.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="67.75" x2="649" y2="67.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="61.75" x2="649" y2="61.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="114" y="52" width="488" height="32" rx="4" fill="hsl(142, 32%, 57%)" />
+  <circle cx="130" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="146" y="66" width="308" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <defs>
+  <filter id="filter0_dd" x="24" y="1" width="174" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+    <feFlood flood-opacity="0" result="BackgroundImageFix" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="2" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.5 0" />
+    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="0.5" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.9 0" />
+    <feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow" />
+    <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape" />
+  </filter>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022orange/icon.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg width="63" height="62" viewBox="0 0 63 62" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="31.5" cy="31" r="31" fill="url(#paint0_linear)"/>
+  <defs>
+    <linearGradient id="paint0_linear" x1="44.4829" y1="19" x2="10.4829" y2="53" gradientUnits="userSpaceOnUse">
+      <stop stop-color="hsl(16, 94%, 65%)"/>
+      <stop offset="1" stop-color="hsl(17, 92%, 80%)"/>
+    </linearGradient>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022orange/manifest.json
@@ -0,0 +1,76 @@
+{
+  "manifest_version": 2,
+  "applications": {
+    "gecko": {
+      "id": "2022orange-colorway@mozilla.org"
+    }
+  },
+  "name": "That’s So Fire",
+  "author": "Mozilla",
+  "version": "1.0",
+  "icons": {
+    "32": "icon.svg"
+  },
+  "theme": {
+    "colors": {
+      "tab_background_text": "hsl(0, 0%, 0%)",
+      "tab_text": "hsl(0, 0%, 0%)",
+      "tab_selected": "hsl(0, 0%, 100%)",
+      "tab_line": "transparent",
+      "tab_loading": "hsl(16, 70%, 38%)",
+      "tab_loading_inactive": "hsl(16, 70%, 38%)",
+      "frame": "hsl(16, 94%, 65%)",
+      "popup": "hsl(0, 0%, 100%)",
+      "popup_text": "hsl(0, 0%, 0%)",
+      "popup_border": "hsl(16, 72%, 76%)",
+      "popup_highlight": "hsla(16, 90%, 50%, 0.3)",
+      "popup_highlight_text": "hsl(0, 0%, 0%)",
+      "toolbar": "hsl(17, 92%, 80%)",
+      "toolbar_text": "hsl(0, 0%, 0%)",
+      "toolbar_field": "hsl(16, 94%, 65%)",
+      "toolbar_field_text": "hsl(0, 0%, 0%)",
+      "toolbar_field_focus": "hsl(0, 0%, 100%)",
+      "toolbar_field_border_focus": "rgba(0, 96, 223, 0.5)",
+      "toolbar_top_separator": "transparent",
+      "toolbar_bottom_separator": "hsl(16, 94%, 65%)",
+      "ntp_background": "hsl(17, 92%, 90%)",
+      "ntp_card_background": "hsl(0, 0%, 100%)",
+      "ntp_text": "hsl(0, 0%, 0%)",
+      "sidebar": "hsl(0, 0%, 100%)",
+      "sidebar_text": "hsl(0, 0%, 0%)",
+      "sidebar_highlight": "hsla(16, 90%, 50%, 0.3)",
+      "address_bar_box": "hsl(0, 0%, 100%)",
+      "address_bar_box_hover": "hsla(0, 0%, 100%, 0.8)",
+      "address_bar_box_active": "hsla(0, 0%, 100%, 0.65)",
+      "address_bar_box_focus": "hsl(17, 92%, 80%)",
+      "address_bar_box_text": "hsl(0, 0%, 0%)",
+      "address_bar_url_color": "hsl(16, 90%, 43%)",
+      "panel_item_hover": "hsla(16, 90%, 50%, 0.3)",
+      "panel_item_active": "hsla(16, 90%, 50%, 0.45)",
+      "panel_separator": "hsl(16, 72%, 76%)",
+      "icons_attention": "hsl(16, 70%, 38%)",
+      "toolbar_field_icons_attention": "hsl(16, 70%, 38%)",
+      "tab_attention_dot": "hsl(145, 15%, 43%)",
+      "appmenu_update_icon_color": "hsl(145, 15%, 43%)",
+      "appmenu_info_icon_color": "hsl(203, 77%, 41%)"
+    }
+  },
+  "theme_experiment": {
+    "colors": {
+      "address_bar_box": "--urlbar-box-bgcolor",
+      "address_bar_box_hover": "--urlbar-box-hover-bgcolor",
+      "address_bar_box_active": "--urlbar-box-active-bgcolor",
+      "address_bar_box_focus": "--urlbar-box-focus-bgcolor",
+      "address_bar_box_text": "--urlbar-box-text-color",
+      "address_bar_url_color": "--urlbar-popup-url-color",
+      "panel_item_hover": "--panel-item-hover-bgcolor",
+      "panel_item_active": "--panel-item-active-bgcolor",
+      "panel_separator": "--panel-separator-color",
+      "toolbar_field_icons_attention": "--lwt-toolbar-field-icon-fill-attention",
+      "tab_attention_dot": "--lwt-tab-attention-icon-color",
+      "appmenu_update_icon_color": "--panel-banner-item-update-supported-bgcolor",
+      "appmenu_info_icon_color": "--panel-banner-item-info-icon-bgcolor",
+      "tab_loading_inactive": "--lwt-tab-loading-fill-inactive"
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022orange/preview.svg
@@ -0,0 +1,36 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+  - License, v. 2.0. If a copy of the MPL was not distributed with this
+  - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="680" height="92" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="680" height="92" fill="hsl(16, 94%, 65%)" />
+  <g filter="url(#filter0_dd)">
+    <rect x="28" y="5" width="166" height="34" rx="4" fill="hsl(0, 0%, 100%)" stroke="transparent" stroke-width="1.5" />
+  </g>
+  <rect x="51" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect x="221" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect y="44" width="680" height="48" fill="hsl(17, 92%, 80%)" />
+  <circle cx="24" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <circle cx="60" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="73.75" x2="649" y2="73.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="67.75" x2="649" y2="67.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="61.75" x2="649" y2="61.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="114" y="52" width="488" height="32" rx="4" fill="hsl(16, 94%, 65%)" />
+  <circle cx="130" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="146" y="66" width="308" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <defs>
+  <filter id="filter0_dd" x="24" y="1" width="174" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+    <feFlood flood-opacity="0" result="BackgroundImageFix" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="2" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.5 0" />
+    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="0.5" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.9 0" />
+    <feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow" />
+    <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape" />
+  </filter>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022purple/icon.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg width="63" height="62" viewBox="0 0 63 62" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="31.5" cy="31" r="31" fill="url(#paint0_linear)"/>
+  <defs>
+    <linearGradient id="paint0_linear" x1="44.4829" y1="19" x2="10.4829" y2="53" gradientUnits="userSpaceOnUse">
+      <stop stop-color="hsl(284, 35%, 70%)"/>
+      <stop offset="1" stop-color="hsl(285, 36%, 81%)"/>
+    </linearGradient>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022purple/manifest.json
@@ -0,0 +1,76 @@
+{
+  "manifest_version": 2,
+  "applications": {
+    "gecko": {
+      "id": "2022purple-colorway@mozilla.org"
+    }
+  },
+  "name": "Bomb-dot-com",
+  "author": "Mozilla",
+  "version": "1.0",
+  "icons": {
+    "32": "icon.svg"
+  },
+  "theme": {
+    "colors": {
+      "tab_background_text": "hsl(0, 0%, 0%)",
+      "tab_text": "hsl(0, 0%, 0%)",
+      "tab_selected": "hsl(0, 0%, 100%)",
+      "tab_line": "transparent",
+      "tab_loading": "hsl(285, 35%, 42%)",
+      "tab_loading_inactive": "hsl(285, 35%, 42%)",
+      "frame": "hsl(284, 35%, 70%)",
+      "popup": "hsl(0, 0%, 100%)",
+      "popup_text": "hsl(0, 0%, 0%)",
+      "popup_border": "hsl(284, 80%, 77%)",
+      "popup_highlight": "hsla(284, 61%, 75%, 0.6)",
+      "popup_highlight_text": "hsl(0, 0%, 0%)",
+      "toolbar": "hsl(285, 36%, 81%)",
+      "toolbar_text": "hsl(0, 0%, 0%)",
+      "toolbar_field": "hsl(284, 35%, 70%)",
+      "toolbar_field_text": "hsl(0, 0%, 0%)",
+      "toolbar_field_focus": "hsl(0, 0%, 100%)",
+      "toolbar_field_border_focus": "rgba(0, 96, 223, 0.5)",
+      "toolbar_top_separator": "transparent",
+      "toolbar_bottom_separator": "hsl(284, 35%, 70%)",
+      "ntp_background": "hsl(284, 35%, 88%)",
+      "ntp_card_background": "hsl(0, 0%, 100%)",
+      "ntp_text": "hsl(0, 0%, 0%)",
+      "sidebar": "hsl(0, 0%, 100%)",
+      "sidebar_text": "hsl(0, 0%, 0%)",
+      "sidebar_highlight": "hsla(284, 61%, 75%, 0.6)",
+      "address_bar_box": "hsl(0, 0%, 100%)",
+      "address_bar_box_hover": "hsla(0, 0%, 100%, 0.8)",
+      "address_bar_box_active": "hsla(0, 0%, 100%, 0.65)",
+      "address_bar_box_focus": "hsl(285, 36%, 81%)",
+      "address_bar_box_text": "hsl(0, 0%, 0%)",
+      "address_bar_url_color": "hsl(285, 32%, 40%)",
+      "panel_item_hover": "hsla(284, 61%, 75%, 0.6)",
+      "panel_item_active": "hsla(284, 61%, 75%, 0.75)",
+      "panel_separator": "hsl(284, 80%, 77%)",
+      "icons_attention": "hsl(285, 35%, 42%)",
+      "toolbar_field_icons_attention": "hsl(285, 35%, 42%)",
+      "tab_attention_dot": "hsl(145, 35%, 46%)",
+      "appmenu_update_icon_color": "hsl(145, 35%, 46%)",
+      "appmenu_info_icon_color": "hsl(203, 80%, 50%)"
+    }
+  },
+  "theme_experiment": {
+    "colors": {
+      "address_bar_box": "--urlbar-box-bgcolor",
+      "address_bar_box_hover": "--urlbar-box-hover-bgcolor",
+      "address_bar_box_active": "--urlbar-box-active-bgcolor",
+      "address_bar_box_focus": "--urlbar-box-focus-bgcolor",
+      "address_bar_box_text": "--urlbar-box-text-color",
+      "address_bar_url_color": "--urlbar-popup-url-color",
+      "panel_item_hover": "--panel-item-hover-bgcolor",
+      "panel_item_active": "--panel-item-active-bgcolor",
+      "panel_separator": "--panel-separator-color",
+      "toolbar_field_icons_attention": "--lwt-toolbar-field-icon-fill-attention",
+      "tab_attention_dot": "--lwt-tab-attention-icon-color",
+      "appmenu_update_icon_color": "--panel-banner-item-update-supported-bgcolor",
+      "appmenu_info_icon_color": "--panel-banner-item-info-icon-bgcolor",
+      "tab_loading_inactive": "--lwt-tab-loading-fill-inactive"
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022purple/preview.svg
@@ -0,0 +1,36 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+  - License, v. 2.0. If a copy of the MPL was not distributed with this
+  - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="680" height="92" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="680" height="92" fill="hsl(284, 35%, 70%)" />
+  <g filter="url(#filter0_dd)">
+    <rect x="28" y="5" width="166" height="34" rx="4" fill="hsl(0, 0%, 100%)" stroke="transparent" stroke-width="1.5" />
+  </g>
+  <rect x="51" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect x="221" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect y="44" width="680" height="48" fill="hsl(285, 36%, 81%)" />
+  <circle cx="24" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <circle cx="60" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="73.75" x2="649" y2="73.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="67.75" x2="649" y2="67.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="61.75" x2="649" y2="61.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="114" y="52" width="488" height="32" rx="4" fill="hsl(284, 35%, 70%)" />
+  <circle cx="130" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="146" y="66" width="308" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <defs>
+  <filter id="filter0_dd" x="24" y="1" width="174" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+    <feFlood flood-opacity="0" result="BackgroundImageFix" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="2" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.5 0" />
+    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="0.5" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.9 0" />
+    <feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow" />
+    <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape" />
+  </filter>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022red/icon.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg width="63" height="62" viewBox="0 0 63 62" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="31.5" cy="31" r="31" fill="url(#paint0_linear)"/>
+  <defs>
+    <linearGradient id="paint0_linear" x1="44.4829" y1="19" x2="10.4829" y2="53" gradientUnits="userSpaceOnUse">
+      <stop stop-color="hsl(356, 95%, 67%)"/>
+      <stop offset="1" stop-color="hsl(356, 91%, 75%)"/>
+    </linearGradient>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022red/manifest.json
@@ -0,0 +1,76 @@
+{
+  "manifest_version": 2,
+  "applications": {
+    "gecko": {
+      "id": "2022red-colorway@mozilla.org"
+    }
+  },
+  "name": "Rawrrr!",
+  "author": "Mozilla",
+  "version": "1.0",
+  "icons": {
+    "32": "icon.svg"
+  },
+  "theme": {
+    "colors": {
+      "tab_background_text": "hsl(0, 0%, 0%)",
+      "tab_text": "hsl(0, 0%, 0%)",
+      "tab_selected": "hsl(0, 0%, 100%)",
+      "tab_line": "transparent",
+      "tab_loading": "hsl(356, 72%, 27%)",
+      "tab_loading_inactive": "hsl(356, 72%, 27%)",
+      "frame": "hsl(356, 95%, 67%)",
+      "popup": "hsl(0, 0%, 100%)",
+      "popup_text": "hsl(0, 0%, 0%)",
+      "popup_border": "hsl(356, 61%, 78%)",
+      "popup_highlight": "hsla(356, 75%, 76%, 0.6)",
+      "popup_highlight_text": "hsl(0, 0%, 0%)",
+      "toolbar": "hsl(356, 91%, 75%)",
+      "toolbar_text": "hsl(0, 0%, 0%)",
+      "toolbar_field": "hsl(356, 95%, 67%)",
+      "toolbar_field_text": "hsl(0, 0%, 0%)",
+      "toolbar_field_focus": "hsl(0, 0%, 100%)",
+      "toolbar_field_border_focus": "rgba(0, 96, 223, 0.5)",
+      "toolbar_top_separator": "transparent",
+      "toolbar_bottom_separator": "hsl(356, 95%, 67%)",
+      "ntp_background": "hsl(356, 84%, 88%)",
+      "ntp_card_background": "hsl(0, 0%, 100%)",
+      "ntp_text": "hsl(0, 0%, 0%)",
+      "sidebar": "hsl(0, 0%, 100%)",
+      "sidebar_text": "hsl(0, 0%, 0%)",
+      "sidebar_highlight": "hsla(356, 75%, 76%, 0.6)",
+      "address_bar_box": "hsl(0, 0%, 100%)",
+      "address_bar_box_hover": "hsla(0, 0%, 100%, 0.8)",
+      "address_bar_box_active": "hsla(0, 0%, 100%, 0.65)",
+      "address_bar_box_focus": "hsl(356, 91%, 75%)",
+      "address_bar_box_text": "hsl(0, 0%, 0%)",
+      "address_bar_url_color": "hsl(356, 72%, 42%)",
+      "panel_item_hover": "hsla(356, 75%, 76%, 0.6)",
+      "panel_item_active": "hsla(356, 75%, 76%, 0.75)",
+      "panel_separator": "hsl(356, 61%, 78%)",
+      "icons_attention": "hsl(356, 72%, 27%)",
+      "toolbar_field_icons_attention": "hsl(356, 72%, 27%)",
+      "tab_attention_dot": "hsl(145, 15%, 34%)",
+      "appmenu_update_icon_color": "hsl(145, 15%, 34%)",
+      "appmenu_info_icon_color": "hsl(204, 75%, 28%)"
+    }
+  },
+  "theme_experiment": {
+    "colors": {
+      "address_bar_box": "--urlbar-box-bgcolor",
+      "address_bar_box_hover": "--urlbar-box-hover-bgcolor",
+      "address_bar_box_active": "--urlbar-box-active-bgcolor",
+      "address_bar_box_focus": "--urlbar-box-focus-bgcolor",
+      "address_bar_box_text": "--urlbar-box-text-color",
+      "address_bar_url_color": "--urlbar-popup-url-color",
+      "panel_item_hover": "--panel-item-hover-bgcolor",
+      "panel_item_active": "--panel-item-active-bgcolor",
+      "panel_separator": "--panel-separator-color",
+      "toolbar_field_icons_attention": "--lwt-toolbar-field-icon-fill-attention",
+      "tab_attention_dot": "--lwt-tab-attention-icon-color",
+      "appmenu_update_icon_color": "--panel-banner-item-update-supported-bgcolor",
+      "appmenu_info_icon_color": "--panel-banner-item-info-icon-bgcolor",
+      "tab_loading_inactive": "--lwt-tab-loading-fill-inactive"
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022red/preview.svg
@@ -0,0 +1,36 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+  - License, v. 2.0. If a copy of the MPL was not distributed with this
+  - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="680" height="92" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="680" height="92" fill="hsl(356, 95%, 67%)" />
+  <g filter="url(#filter0_dd)">
+    <rect x="28" y="5" width="166" height="34" rx="4" fill="hsl(0, 0%, 100%)" stroke="transparent" stroke-width="1.5" />
+  </g>
+  <rect x="51" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect x="221" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect y="44" width="680" height="48" fill="hsl(356, 91%, 75%)" />
+  <circle cx="24" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <circle cx="60" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="73.75" x2="649" y2="73.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="67.75" x2="649" y2="67.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="61.75" x2="649" y2="61.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="114" y="52" width="488" height="32" rx="4" fill="hsl(356, 95%, 67%)" />
+  <circle cx="130" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="146" y="66" width="308" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <defs>
+  <filter id="filter0_dd" x="24" y="1" width="174" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+    <feFlood flood-opacity="0" result="BackgroundImageFix" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="2" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.5 0" />
+    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="0.5" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.9 0" />
+    <feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow" />
+    <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape" />
+  </filter>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022yellow/icon.svg
@@ -0,0 +1,12 @@
+<!-- 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/. -->
+<svg width="63" height="62" viewBox="0 0 63 62" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="31.5" cy="31" r="31" fill="url(#paint0_linear)"/>
+  <defs>
+    <linearGradient id="paint0_linear" x1="44.4829" y1="19" x2="10.4829" y2="53" gradientUnits="userSpaceOnUse">
+      <stop stop-color="hsl(39, 98%, 63%)"/>
+      <stop offset="1" stop-color="hsl(39, 98%, 84%)"/>
+    </linearGradient>
+  </defs>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022yellow/manifest.json
@@ -0,0 +1,76 @@
+{
+  "manifest_version": 2,
+  "applications": {
+    "gecko": {
+      "id": "2022yellow-colorway@mozilla.org"
+    }
+  },
+  "name": "Cheese Puff",
+  "author": "Mozilla",
+  "version": "1.0",
+  "icons": {
+    "32": "icon.svg"
+  },
+  "theme": {
+    "colors": {
+      "tab_background_text": "hsl(0, 0%, 0%)",
+      "tab_text": "hsl(0, 0%, 0%)",
+      "tab_selected": "hsl(0, 0%, 100%)",
+      "tab_line": "transparent",
+      "tab_loading": "hsl(29, 84%, 46%)",
+      "tab_loading_inactive": "hsl(29, 84%, 46%)",
+      "frame": "hsl(39, 98%, 63%)",
+      "popup": "hsl(0, 0%, 100%)",
+      "popup_text": "hsl(0, 0%, 0%)",
+      "popup_border": "hsl(39, 100%, 68%)",
+      "popup_highlight": "hsla(39, 100%, 72%, 0.9)",
+      "popup_highlight_text": "hsl(0, 0%, 0%)",
+      "toolbar": "hsl(39, 98%, 84%)",
+      "toolbar_text": "hsl(0, 0%, 0%)",
+      "toolbar_field": "hsl(39, 98%, 63%)",
+      "toolbar_field_text": "hsl(0, 0%, 0%)",
+      "toolbar_field_focus": "hsl(0, 0%, 100%)",
+      "toolbar_field_border_focus": "rgba(0, 96, 223, 0.5)",
+      "toolbar_top_separator": "transparent",
+      "toolbar_bottom_separator": "hsl(39, 98%, 63%)",
+      "ntp_background": "hsl(39, 97%, 87%)",
+      "ntp_card_background": "hsl(0, 0%, 100%)",
+      "ntp_text": "hsl(0, 0%, 0%)",
+      "sidebar": "hsl(0, 0%, 100%)",
+      "sidebar_text": "hsl(0, 0%, 0%)",
+      "sidebar_highlight": "hsla(39, 100%, 72%, 0.9)",
+      "address_bar_box": "hsl(0, 0%, 100%)",
+      "address_bar_box_hover": "hsla(0, 0%, 100%, 0.8)",
+      "address_bar_box_active": "hsla(0, 0%, 100%, 0.65)",
+      "address_bar_box_focus": "hsl(39, 98%, 84%)",
+      "address_bar_box_text": "hsl(0, 0%, 0%)",
+      "address_bar_url_color": "hsl(39, 100%, 30%)",
+      "panel_item_hover": "hsla(39, 100%, 72%, 0.9)",
+      "panel_item_active": "hsl(39, 100%, 72%)",
+      "panel_separator": "hsl(39, 100%, 68%)",
+      "icons_attention": "hsl(29, 84%, 46%)",
+      "toolbar_field_icons_attention": "hsl(29, 84%, 46%)",
+      "tab_attention_dot": "hsl(138, 15%, 44%)",
+      "appmenu_update_icon_color": "hsl(138, 15%, 44%)",
+      "appmenu_info_icon_color": "hsl(203, 77%, 41%)"
+    }
+  },
+  "theme_experiment": {
+    "colors": {
+      "address_bar_box": "--urlbar-box-bgcolor",
+      "address_bar_box_hover": "--urlbar-box-hover-bgcolor",
+      "address_bar_box_active": "--urlbar-box-active-bgcolor",
+      "address_bar_box_focus": "--urlbar-box-focus-bgcolor",
+      "address_bar_box_text": "--urlbar-box-text-color",
+      "address_bar_url_color": "--urlbar-popup-url-color",
+      "panel_item_hover": "--panel-item-hover-bgcolor",
+      "panel_item_active": "--panel-item-active-bgcolor",
+      "panel_separator": "--panel-separator-color",
+      "toolbar_field_icons_attention": "--lwt-toolbar-field-icon-fill-attention",
+      "tab_attention_dot": "--lwt-tab-attention-icon-color",
+      "appmenu_update_icon_color": "--panel-banner-item-update-supported-bgcolor",
+      "appmenu_info_icon_color": "--panel-banner-item-info-icon-bgcolor",
+      "tab_loading_inactive": "--lwt-tab-loading-fill-inactive"
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/themes/addons/monochromatic/2022yellow/preview.svg
@@ -0,0 +1,36 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+  - License, v. 2.0. If a copy of the MPL was not distributed with this
+  - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="680" height="92" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <rect width="680" height="92" fill="hsl(39, 98%, 63%)" />
+  <g filter="url(#filter0_dd)">
+    <rect x="28" y="5" width="166" height="34" rx="4" fill="hsl(0, 0%, 100%)" stroke="transparent" stroke-width="1.5" />
+  </g>
+  <rect x="51" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect x="221" y="20" width="121" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <rect y="44" width="680" height="48" fill="hsl(39, 98%, 84%)" />
+  <circle cx="24" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <circle cx="60" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="73.75" x2="649" y2="73.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="67.75" x2="649" y2="67.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <line x1="663" y1="61.75" x2="649" y2="61.75" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="114" y="52" width="488" height="32" rx="4" fill="hsl(39, 98%, 63%)" />
+  <circle cx="130" cy="68" r="6.25" stroke="hsl(0, 0%, 0%)" stroke-width="1.5" />
+  <rect x="146" y="66" width="308" height="4" rx="2" fill="hsl(0, 0%, 0%)" />
+  <defs>
+  <filter id="filter0_dd" x="24" y="1" width="174" height="42" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
+    <feFlood flood-opacity="0" result="BackgroundImageFix" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="2" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.5 0" />
+    <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow" />
+    <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" />
+    <feOffset />
+    <feGaussianBlur stdDeviation="0.5" />
+    <feColorMatrix type="matrix" values="0 0 0 0 0.501961 0 0 0 0 0.501961 0 0 0 0 0.556863 0 0 0 0.9 0" />
+    <feBlend mode="normal" in2="effect1_dropShadow" result="effect2_dropShadow" />
+    <feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow" result="shape" />
+  </filter>
+  </defs>
+</svg>
--- a/build.gradle
+++ b/build.gradle
@@ -232,20 +232,16 @@ def getArtifactSuffix() {
     def substs = project.ext.mozconfig.substs
 
     def suffix = ""
     // Release artifacts don't specify the channel, for the sake of simplicity.
     if (substs.MOZ_UPDATE_CHANNEL != 'release') {
         suffix += "-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
     }
 
-    if (!substs.MOZ_ANDROID_GECKOVIEW_LITE) {
-        suffix += "-omni"
-    }
-
     return suffix
 }
 
 class MachExec extends Exec {
     def MachExec() {
         // Bug 1543982: When invoking `mach build` recursively, the outer `mach
         // build` itself modifies the environment, causing configure to run
         // again.  This tries to restore the environment that the outer `mach
--- a/build/moz.configure/nss.configure
+++ b/build/moz.configure/nss.configure
@@ -4,17 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 system_lib_option("--with-system-nss", help="Use system NSS")
 
 imply_option("--with-system-nspr", True, when="--with-system-nss")
 
 nss_pkg = pkg_check_modules(
-    "NSS", "nss >= 3.74", when="--with-system-nss", config=False
+    "NSS", "nss >= 3.75", when="--with-system-nss", config=False
 )
 
 set_config("MOZ_SYSTEM_NSS", True, when="--with-system-nss")
 
 
 @depends(nss_pkg, build_environment)
 def nss_config(nss_pkg, build_env):
     cflags = ["-I%s" % os.path.join(build_env.dist, "include", "nss")]
--- a/devtools/client/debugger/src/components/Editor/index.js
+++ b/devtools/client/debugger/src/components/Editor/index.js
@@ -14,31 +14,36 @@ import { features } from "../../utils/pr
 import { getIndentation } from "../../utils/indentation";
 
 import { showMenu } from "../../context-menu/menu";
 import {
   createBreakpointItems,
   breakpointItemActions,
 } from "./menus/breakpoints";
 
-import { continueToHereItem, editorItemActions } from "./menus/editor";
+import {
+  continueToHereItem,
+  editorItemActions,
+  blackBoxLineMenuItem,
+} from "./menus/editor";
 
 import {
   getActiveSearch,
   getSelectedLocation,
   getSelectedSourceWithContent,
   getConditionalPanelLocation,
   getSymbols,
   getIsCurrentThreadPaused,
   getCurrentThread,
   getThreadContext,
   getSkipPausing,
   getInlinePreview,
   getEditorWrapping,
   getHighlightedCalls,
+  getBlackBoxRanges,
 } from "../../selectors";
 
 // Redux actions
 import actions from "../../actions";
 
 import SearchBar from "./SearchBar";
 import HighlightLines from "./HighlightLines";
 import Preview from "./Preview";
@@ -122,16 +127,17 @@ class Editor extends PureComponent {
       selectedLocation: PropTypes.object,
       symbols: PropTypes.object,
       startPanelSize: PropTypes.number,
       endPanelSize: PropTypes.number,
       searchOn: PropTypes.bool,
       inlinePreviewEnabled: PropTypes.bool,
       editorWrappingEnabled: PropTypes.bool,
       skipPausing: PropTypes.bool,
+      blackboxedRanges: PropTypes.object,
     };
   }
 
   $editorWrapper;
   constructor(props) {
     super(props);
 
     this.state = {
@@ -376,16 +382,17 @@ class Editor extends PureComponent {
     const {
       cx,
       selectedSource,
       breakpointActions,
       editorActions,
       isPaused,
       conditionalPanelLocation,
       closeConditionalPanel,
+      blackboxedRanges,
     } = this.props;
     const { editor } = this.state;
     if (!selectedSource || !editor) {
       return;
     }
 
     // only allow one conditionalPanel location.
     if (conditionalPanelLocation) {
@@ -408,16 +415,25 @@ class Editor extends PureComponent {
         selectedSource.content,
         line
       ).trim();
 
       return showMenu(event, [
         ...createBreakpointItems(cx, location, breakpointActions, lineText),
         { type: "separator" },
         continueToHereItem(cx, location, isPaused, editorActions),
+        { type: "separator" },
+        blackBoxLineMenuItem(
+          cx,
+          selectedSource,
+          editorActions,
+          editor,
+          blackboxedRanges,
+          line
+        ),
       ]);
     }
 
     if (target.getAttribute("id") === "columnmarker") {
       return;
     }
 
     this.setState({ contextMenu: event });
@@ -705,16 +721,17 @@ const mapStateToProps = state => {
     searchOn: getActiveSearch(state) === "file",
     conditionalPanelLocation: getConditionalPanelLocation(state),
     symbols: getSymbols(state, selectedSource),
     isPaused: getIsCurrentThreadPaused(state),
     skipPausing: getSkipPausing(state),
     inlinePreviewEnabled: getInlinePreview(state),
     editorWrappingEnabled: getEditorWrapping(state),
     highlightedCalls: getHighlightedCalls(state, getCurrentThread(state)),
+    blackboxedRanges: getBlackBoxRanges(state),
   };
 };
 
 const mapDispatchToProps = dispatch => ({
   ...bindActionCreators(
     {
       openConditionalPanel: actions.openConditionalPanel,
       closeConditionalPanel: actions.closeConditionalPanel,
--- a/devtools/client/debugger/src/components/Editor/menus/editor.js
+++ b/devtools/client/debugger/src/components/Editor/menus/editor.js
@@ -107,69 +107,135 @@ function findBlackBoxRange(source, black
 
   return ranges.find(
     range =>
       (line.start >= range.start.line && line.start <= range.end.line) ||
       (line.end >= range.start.line && line.end <= range.end.line)
   );
 }
 
+export const blackBoxLineMenuItem = (
+  cx,
+  selectedSource,
+  editorActions,
+  editor,
+  blackboxedRanges,
+  // the clickedLine is passed when the context menu
+  // is opened from the gutter, it is not available when the
+  // the context menu is opened from the editor.
+  clickedLine = null
+) => {
+  const { codeMirror } = editor;
+  const from = codeMirror.getCursor("from");
+  const to = codeMirror.getCursor("to");
+
+  const startLine = clickedLine ?? toSourceLine(selectedSource.id, from.line);
+  const endLine = clickedLine ?? toSourceLine(selectedSource.id, to.line);
+
+  const blackboxRange = findBlackBoxRange(selectedSource, blackboxedRanges, {
+    start: startLine,
+    end: endLine,
+  });
+
+  const selectedLineIsBlackBoxed = !!blackboxRange;
+
+  const isSingleLine = selectedLineIsBlackBoxed
+    ? blackboxRange.start.line == blackboxRange.end.line
+    : startLine == endLine;
+
+  // The ignore/unignore line context menu item should be disabled when
+  // 1) The whole source is blackboxed or
+  // 2) Multiple lines are blackboxed or
+  // 3) Multiple lines are selected in the editor
+  const shouldDisable =
+    (selectedSource.isBlackBoxed &&
+      !blackboxedRanges[selectedSource.url].length) ||
+    !isSingleLine;
+
+  return {
+    id: "node-menu-blackbox-line",
+    label: !selectedLineIsBlackBoxed
+      ? L10N.getStr("ignoreContextItem.ignoreLine")
+      : L10N.getStr("ignoreContextItem.unignoreLine"),
+    accesskey: !selectedLineIsBlackBoxed
+      ? L10N.getStr("ignoreContextItem.ignoreLine.accesskey")
+      : L10N.getStr("ignoreContextItem.unignoreLine.accesskey"),
+    disabled: shouldDisable,
+    click: () => {
+      const selectionRange = {
+        start: {
+          line: startLine,
+          column: clickedLine == null ? from.ch : 0,
+        },
+        end: {
+          line: endLine,
+          column: clickedLine == null ? to.ch : 0,
+        },
+      };
+
+      editorActions.toggleBlackBox(
+        cx,
+        selectedSource,
+        !selectedLineIsBlackBoxed,
+        selectedLineIsBlackBoxed ? [blackboxRange] : [selectionRange]
+      );
+    },
+  };
+};
+
 const blackBoxLinesMenuItem = (
   cx,
   selectedSource,
   editorActions,
   editor,
   blackboxedRanges
 ) => {
   const { codeMirror } = editor;
   const from = codeMirror.getCursor("from");
   const to = codeMirror.getCursor("to");
 
   const startLine = toSourceLine(selectedSource.id, from.line);
   const endLine = toSourceLine(selectedSource.id, to.line);
 
-  const shouldDisable =
-    selectedSource.isBlackBoxed && !blackboxedRanges[selectedSource.url].length;
-
   const blackboxRange = findBlackBoxRange(selectedSource, blackboxedRanges, {
     start: startLine,
     end: endLine,
   });
 
+  const selectedLinesAreBlackBoxed = !!blackboxRange;
+
   return {
     id: "node-menu-blackbox-lines",
-    label: !blackboxRange
+    label: !selectedLinesAreBlackBoxed
       ? L10N.getStr("ignoreContextItem.ignoreLines")
       : L10N.getStr("ignoreContextItem.unignoreLines"),
-    accesskey: !blackboxRange
+    accesskey: !selectedLinesAreBlackBoxed
       ? L10N.getStr("ignoreContextItem.ignoreLines.accesskey")
       : L10N.getStr("ignoreContextItem.unignoreLines.accesskey"),
-    disabled: shouldDisable,
+    disabled: false,
     click: () => {
       const selectionRange = {
         start: {
           line: startLine,
           column: from.ch,
         },
         end: {
           line: endLine,
           column: to.ch,
         },
       };
 
       // removes the current selection
-      editor.codeMirror.replaceSelection(
-        editor.codeMirror.getSelection(),
-        "start"
-      );
+      codeMirror.replaceSelection(codeMirror.getSelection(), "start");
+
       editorActions.toggleBlackBox(
         cx,
         selectedSource,
-        !blackboxRange,
-        blackboxRange ? [blackboxRange] : [selectionRange]
+        !selectedLinesAreBlackBoxed,
+        selectedLinesAreBlackBoxed ? [blackboxRange] : [selectionRange]
       );
     },
   };
 };
 
 const watchExpressionItem = (
   cx,
   selectedSource,
@@ -257,25 +323,54 @@ export function editorMenuItems({
       : []),
     { type: "separator" },
     showSourceMenuItem(cx, selectedSource, editorActions),
     { type: "separator" },
     blackBoxMenuItem(cx, selectedSource, editorActions)
   );
 
   if (features.blackboxLines) {
-    items.push(
-      blackBoxLinesMenuItem(
-        cx,
-        selectedSource,
-        editorActions,
-        editor,
-        blackboxedRanges
-      )
+    const startLine = toSourceLine(
+      selectedSource.id,
+      editor.codeMirror.getCursor("from").line
+    );
+    const endLine = toSourceLine(
+      selectedSource.id,
+      editor.codeMirror.getCursor("to").line
     );
+
+    // Find any blackbox ranges that exist for the selected lines
+    const blackboxRange = findBlackBoxRange(selectedSource, blackboxedRanges, {
+      start: startLine,
+      end: endLine,
+    });
+
+    const isMultiLineSelection = blackboxRange
+      ? blackboxRange.start.line !== blackboxRange.end.line
+      : startLine !== endLine;
+
+    const theWholeSourceIsBlackBoxed =
+      selectedSource.isBlackBoxed &&
+      !blackboxedRanges[selectedSource.url].length;
+
+    if (!theWholeSourceIsBlackBoxed) {
+      const blackBoxSourceLinesMenuItem = isMultiLineSelection
+        ? blackBoxLinesMenuItem
+        : blackBoxLineMenuItem;
+
+      items.push(
+        blackBoxSourceLinesMenuItem(
+          cx,
+          selectedSource,
+          editorActions,
+          editor,
+          blackboxedRanges
+        )
+      );
+    }
   }
 
   if (isTextSelected) {
     items.push(
       { type: "separator" },
       watchExpressionItem(cx, selectedSource, selectionText, editorActions),
       evaluateInConsoleItem(selectedSource, selectionText, editorActions)
     );
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -22,16 +22,17 @@ support-files =
   doc_markup_events-overflow.html
   doc_markup_events_react_development_15.4.1.html
   doc_markup_events_react_development_15.4.1_jsx.html
   doc_markup_events_react_production_15.3.1.html
   doc_markup_events_react_production_15.3.1_jsx.html
   doc_markup_events_react_production_16.2.0.html
   doc_markup_events_react_production_16.2.0_jsx.html
   doc_markup_events-source_map.html
+  doc_markup_events_toggle.html
   doc_markup_flashing.html
   doc_markup_html_mixed_case.html
   doc_markup_image_and_canvas.html
   doc_markup_image_and_canvas_2.html
   doc_markup_links.html
   doc_markup_mutation.html
   doc_markup_navigation.html
   doc_markup_not_displayed.html
@@ -141,16 +142,17 @@ skip-if = (os == 'linux' && bits == 32 &
 skip-if = true # Bug 1177550
 [browser_markup_events_react_development_15.4.1.js]
 [browser_markup_events_react_development_15.4.1_jsx.js]
 [browser_markup_events_react_production_15.3.1.js]
 [browser_markup_events_react_production_15.3.1_jsx.js]
 [browser_markup_events_react_production_16.2.0.js]
 [browser_markup_events_react_production_16.2.0_jsx.js]
 [browser_markup_events_source_map.js]
+[browser_markup_events_toggle.js]
 [browser_markup_events-windowed-host.js]
 [browser_markup_flex_display_badge.js]
 [browser_markup_flex_display_badge_telemetry.js]
 [browser_markup_grid_display_badge_01.js]
 [browser_markup_grid_display_badge_02.js]
 [browser_markup_grid_display_badge_03.js]
 [browser_markup_grid_display_badge_telemetry.js]
 [browser_markup_links_01.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events_toggle.js
@@ -0,0 +1,262 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_events_test_runner.js */
+
+"use strict";
+
+// Test that event listeners can be disabled and re-enabled from the markup view event bubble.
+
+const TEST_URL = URL_ROOT_SSL + "doc_markup_events_toggle.html";
+
+loadHelperScript("helper_events_test_runner.js");
+
+add_task(async function() {
+  const { inspector, toolbox } = await openInspectorForURL(TEST_URL);
+  const { resourceCommand } = toolbox.commands;
+  await inspector.markup.expandAll();
+  await selectNode("#target", inspector);
+
+  info(
+    "Click on the target element to make sure the event listeners are properly set"
+  );
+  // There's a "mouseup" event listener that is `console.info` (so we can check "native" events).
+  // In order to know if it was called, we listen for the next console.info resource.
+  let {
+    onResource: onConsoleInfoMessage,
+  } = await resourceCommand.waitForNextResource(
+    resourceCommand.TYPES.CONSOLE_MESSAGE,
+    {
+      ignoreExistingResources: true,
+      predicate(resource) {
+        return resource.message.level == "info";
+      },
+    }
+  );
+  await safeSynthesizeMouseEventAtCenterInContentPage("#target");
+
+  let data = await getTargetElementHandledEventData();
+  is(data.click, 1, `target handled one "click" event`);
+  is(data.mousedown, 1, `target handled one "mousedown" event`);
+  await onConsoleInfoMessage;
+  ok(true, `the "mouseup" event listener (console.info) was called`);
+
+  info("Check that the event tooltip has the expected content");
+  const container = await getContainerForSelector("#target", inspector);
+  const eventTooltipBadge = container.elt.querySelector(
+    ".inspector-badge.interactive[data-event]"
+  );
+  ok(eventTooltipBadge, "The event tooltip badge is displayed");
+
+  const tooltip = inspector.markup.eventDetailsTooltip;
+  let onTooltipShown = tooltip.once("shown");
+  eventTooltipBadge.click();
+  await onTooltipShown;
+  ok(true, "The tooltip is shown");
+
+  Assert.deepEqual(
+    getAsciiHeadersViz(tooltip),
+    ["click [x]", "mousedown [x]", "mouseup [x]"],
+    "The expected events are displayed, all enabled"
+  );
+
+  const [
+    clickHeader,
+    mousedownHeader,
+    mouseupHeader,
+  ] = getHeadersInEventTooltip(tooltip);
+
+  info("Uncheck the mousedown event checkbox");
+  await toggleEventListenerCheckbox(tooltip, mousedownHeader);
+  Assert.deepEqual(
+    getAsciiHeadersViz(tooltip),
+    ["click [x]", "mousedown []", "mouseup [x]"],
+    "mousedown checkbox was unchecked"
+  );
+  await safeSynthesizeMouseEventAtCenterInContentPage("#target");
+  data = await getTargetElementHandledEventData();
+  is(data.click, 2, `target handled another "click" event…`);
+  is(data.mousedown, 1, `… but not a mousedown one`);
+
+  info("Uncheck the click event checkbox");
+  await toggleEventListenerCheckbox(tooltip, clickHeader);
+  Assert.deepEqual(
+    getAsciiHeadersViz(tooltip),
+    ["click []", "mousedown []", "mouseup [x]"],
+    "click checkbox was unchecked"
+  );
+  await safeSynthesizeMouseEventAtCenterInContentPage("#target");
+  data = await getTargetElementHandledEventData();
+  is(data.click, 2, `click event listener was disabled`);
+  is(data.mousedown, 1, `and mousedown still is disabled as well`);
+
+  info("Uncheck the mouseup event checkbox");
+  await toggleEventListenerCheckbox(tooltip, mouseupHeader);
+  Assert.deepEqual(
+    getAsciiHeadersViz(tooltip),
+    ["click []", "mousedown []", "mouseup []"],
+    "mouseup checkbox was unchecked"
+  );
+
+  ({
+    onResource: onConsoleInfoMessage,
+  } = await resourceCommand.waitForNextResource(
+    resourceCommand.TYPES.CONSOLE_MESSAGE,
+    {
+      ignoreExistingResources: true,
+      predicate(resource) {
+        return resource.message.level == "info";
+      },
+    }
+  ));
+  const onTimeout = wait(500).then(() => "TIMEOUT");
+  await safeSynthesizeMouseEventAtCenterInContentPage("#target");
+  const raceResult = await Promise.race([onConsoleInfoMessage, onTimeout]);
+  is(
+    raceResult,
+    "TIMEOUT",
+    "The mouseup event didn't trigger a console.info call, meaning the event listener was disabled"
+  );
+
+  info("Re-enable the mousedown event");
+  await toggleEventListenerCheckbox(tooltip, mousedownHeader);
+  Assert.deepEqual(
+    getAsciiHeadersViz(tooltip),
+    ["click []", "mousedown [x]", "mouseup []"],
+    "mousedown checkbox is checked again"
+  );
+  await safeSynthesizeMouseEventAtCenterInContentPage("#target");
+  data = await getTargetElementHandledEventData();
+  is(data.click, 2, `no additional "click" event were handled`);
+  is(
+    data.mousedown,
+    2,
+    `but we did get a new "mousedown", the event listener was re-enabled`
+  );
+
+  info("Hide the tooltip and show it again");
+  const tooltipHidden = tooltip.once("hidden");
+  tooltip.hide();
+  await tooltipHidden;
+
+  onTooltipShown = tooltip.once("shown");
+  eventTooltipBadge.click();
+  await onTooltipShown;
+  ok(true, "The tooltip is shown again");
+
+  Assert.deepEqual(
+    getAsciiHeadersViz(tooltip),
+    ["click []", "mousedown [x]", "mouseup []"],
+    "Only mousedown checkbox is checked"
+  );
+
+  info("Re-enable mouseup events");
+  await toggleEventListenerCheckbox(
+    tooltip,
+    getHeadersInEventTooltip(tooltip).at(-1)
+  );
+  Assert.deepEqual(
+    getAsciiHeadersViz(tooltip),
+    ["click []", "mousedown [x]", "mouseup [x]"],
+    "mouseup is checked again"
+  );
+
+  ({
+    onResource: onConsoleInfoMessage,
+  } = await resourceCommand.waitForNextResource(
+    resourceCommand.TYPES.CONSOLE_MESSAGE,
+    {
+      ignoreExistingResources: true,
+      predicate(resource) {
+        return resource.message.level == "info";
+      },
+    }
+  ));
+  await safeSynthesizeMouseEventAtCenterInContentPage("#target");
+  await onConsoleInfoMessage;
+  ok(true, "The mouseup event was re-enabled");
+  data = await getTargetElementHandledEventData();
+  is(data.click, 2, `"click" is still disabled`);
+  is(
+    data.mousedown,
+    3,
+    `we received a new "mousedown" event as part of the click`
+  );
+
+  info("Close DevTools to check that event listeners are re-enabled");
+  await closeToolbox();
+  await safeSynthesizeMouseEventAtCenterInContentPage("#target");
+  data = await getTargetElementHandledEventData();
+  is(
+    data.click,
+    3,
+    `a new "click" event was handled after the devtools was closed`
+  );
+  is(
+    data.mousedown,
+    4,
+    `a new "mousedown" event was handled after the devtools was closed`
+  );
+});
+
+function getHeadersInEventTooltip(tooltip) {
+  return Array.from(tooltip.panel.querySelectorAll(".event-header"));
+}
+
+/**
+ * Get an array of string representing a header in its state, e.g.
+ * [
+ *   "click [x]",
+ *   "mousedown []",
+ * ]
+ *
+ * represents an event tooltip with a click and a mousedown event, where the mousedown
+ * event has been disabled.
+ *
+ * @param {EventTooltip} tooltip
+ * @returns Array<String>
+ */
+function getAsciiHeadersViz(tooltip) {
+  return getHeadersInEventTooltip(tooltip).map(
+    el =>
+      `${el.querySelector(".event-tooltip-event-type").textContent} [${
+        getHeaderCheckbox(el).checked ? "x" : ""
+      }]`
+  );
+}
+
+function getHeaderCheckbox(headerEl) {
+  return headerEl.querySelector("input[type=checkbox]");
+}
+
+async function toggleEventListenerCheckbox(tooltip, headerEl) {
+  const onEventToggled = tooltip.once("event-tooltip-listener-toggled");
+  const checkbox = getHeaderCheckbox(headerEl);
+  const previousValue = checkbox.checked;
+  EventUtils.synthesizeMouseAtCenter(
+    getHeaderCheckbox(headerEl),
+    {},
+    headerEl.ownerGlobal
+  );
+  await onEventToggled;
+  is(checkbox.checked, !previousValue, "The checkbox was toggled");
+  is(
+    headerEl.classList.contains("content-expanded"),
+    false,
+    "Clicking on the checkbox did not expand the header"
+  );
+}
+
+/**
+ * @returns Promise<Object> The object keys are event names (e.g. "click", "mousedown"), and
+ *          the values are number representing the number of time the event was handled.
+ *          Note that "mouseup" isn't handled here.
+ */
+function getTargetElementHandledEventData() {
+  return SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
+    // In doc_markup_events_toggle.html , we count the events handled by the target in
+    // a stringified object in dataset.handledEvents.
+    return JSON.parse(
+      content.document.getElementById("target").dataset.handledEvents
+    );
+  });
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_events_toggle.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+  </head>
+  <body>
+    <h1>Toggle Event Listeners</h1>
+    <button id="target" onclick="handleEvent(event)">Target</button>
+    <script>
+      "use strict";
+
+      function handleEvent(e) {
+        const data = JSON.parse(e.target.dataset.handledEvents || "{}");
+        data[e.type] = (data[e.type] || 0) + 1;
+        e.target.dataset.handledEvents = JSON.stringify(data);
+      }
+
+      const domEventsElement = document.getElementById("target");
+      // adding regular event listener
+      domEventsElement.addEventListener("mousedown", handleEvent);
+      // and a "native" event listener
+      domEventsElement.addEventListener("mouseup", console.info)
+    </script>
+  </body>
+</html>
--- a/devtools/client/inspector/markup/test/helper_events_test_runner.js
+++ b/devtools/client/inspector/markup/test/helper_events_test_runner.js
@@ -164,16 +164,29 @@ async function checkEventsForNode(test, 
     });
     testDiff(
       editor.getText(),
       tidiedHandler,
       "handler matches for " + cssSelector,
       ok
     );
 
+    const checkbox = header.querySelector("input[type=checkbox]");
+    ok(checkbox, "The event toggling checkbox is displayed");
+    const disabled = checkbox.hasAttribute("disabled");
+    // We can't disable React/jQuery events at the moment, so ensure that for those,
+    // the checkbox is disabled.
+    const shouldBeDisabled =
+      expected[i].attributes?.includes("React") ||
+      expected[i].attributes?.includes("jQuery");
+    ok(
+      disabled === shouldBeDisabled,
+      `The checkbox is ${shouldBeDisabled ? "disabled" : "enabled"}\n`
+    );
+
     info(`${label} END`);
   }
 
   const tooltipHidden = tooltip.once("hidden");
   tooltip.hide();
   await tooltipHidden;
 }
 
--- a/devtools/client/inspector/markup/views/element-container.js
+++ b/devtools/client/inspector/markup/views/element-container.js
@@ -70,17 +70,22 @@ MarkupElementContainer.prototype = exten
     const tooltip = this.markup.eventDetailsTooltip;
     await tooltip.hide();
 
     const listenerInfo = await this.node.getEventListenerInfo();
 
     const toolbox = this.markup.toolbox;
 
     // Create the EventTooltip which will populate the tooltip content.
-    const eventTooltip = new EventTooltip(tooltip, listenerInfo, toolbox);
+    const eventTooltip = new EventTooltip(
+      tooltip,
+      listenerInfo,
+      toolbox,
+      this.node
+    );
 
     // Disable the image preview tooltip while we display the event details
     this.markup._disableImagePreviewTooltip();
     tooltip.once("hidden", () => {
       eventTooltip.destroy();
 
       // Enable the image preview tooltip after closing the event details
       this.markup._enableImagePreviewTooltip();
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -625,16 +625,26 @@ sourceFooter.unignore=Unignore source
 ignoreContextItem.ignore=Ignore source
 ignoreContextItem.ignore.accesskey=I
 
 # LOCALIZATION NOTE (ignoreContextItem.unignore): Text associated
 # with the unignore context menu item
 ignoreContextItem.unignore=Unignore source
 ignoreContextItem.unignore.accesskey=U
 
+# LOCALIZATION NOTE (ignoreContextItem.ignoreLine): Text associated
+# with the ignore line context menu item
+ignoreContextItem.ignoreLine=Ignore line
+ignoreContextItem.ignoreLine.accesskey=l
+
+# LOCALIZATION NOTE (ignoreContextItem.unignoreLine): Text associated
+# with the unignore line context menu item
+ignoreContextItem.unignoreLine=Unignore line
+ignoreContextItem.unignoreLine.accesskey=n
+
 # LOCALIZATION NOTE (ignoreContextItem.ignoreLines): Text associated
 # with the ignore lines context menu item
 ignoreContextItem.ignoreLines=Ignore lines
 ignoreContextItem.ignoreLines.accesskey=i
 
 # LOCALIZATION NOTE (ignoreContextItem.unignoreLines): Text associated
 # with the unignore lines context menu item
 ignoreContextItem.unignoreLines=Unignore lines
--- a/devtools/client/shared/widgets/tooltip/EventTooltipHelper.js
+++ b/devtools/client/shared/widgets/tooltip/EventTooltipHelper.js
@@ -22,27 +22,34 @@ class EventTooltip {
    * of the event handler.
    *
    * @param {HTMLTooltip} tooltip
    *        The tooltip instance on which the event details content should be set
    * @param {Array} eventListenerInfos
    *        A list of event listeners
    * @param {Toolbox} toolbox
    *        Toolbox used to select debugger panel
+   * @param {NodeFront} nodeFront
+   *        The nodeFront we're displaying event listeners for.
    */
-  constructor(tooltip, eventListenerInfos, toolbox) {
+  constructor(tooltip, eventListenerInfos, toolbox, nodeFront) {
     this._tooltip = tooltip;
     this._toolbox = toolbox;
     this._eventEditors = new WeakMap();
+    this._nodeFront = nodeFront;
+    this._eventListenersAbortController = new AbortController();
 
     // Used in tests: add a reference to the EventTooltip instance on the HTMLTooltip.
     this._tooltip.eventTooltip = this;
 
     this._headerClicked = this._headerClicked.bind(this);
-    this._debugClicked = this._debugClicked.bind(this);
+    this._eventToggleCheckboxChanged = this._eventToggleCheckboxChanged.bind(
+      this
+    );
+
     this._subscriptions = [];
 
     const config = {
       mode: Editor.modes.js,
       lineNumbers: false,
       lineWrapping: true,
       readOnly: true,
       styleActiveLine: true,
@@ -104,17 +111,17 @@ class EventTooltip {
               originalLocation => {
                 const currentLoc = originalLocation || location;
 
                 const newURI = currentLoc.url + ":" + currentLoc.line;
                 filename.textContent = newURI;
                 filename.setAttribute("title", newURI);
 
                 // This is emitted for testing.
-                this._tooltip.emit("event-tooltip-source-map-ready");
+                this._tooltip.emitForTests("event-tooltip-source-map-ready");
               }
             )
           );
         }
       }
 
       filename.textContent = text;
       filename.setAttribute("title", title);
@@ -155,16 +162,37 @@ class EventTooltip {
         capturing.className = "event-tooltip-attributes";
 
         const phase = listener.capturing ? Capturing : Bubbling;
         capturing.textContent = phase;
         capturing.setAttribute("title", phase);
         attributesBox.appendChild(capturing);
       }
 
+      const toggleListenerCheckbox = doc.createElementNS(XHTML_NS, "input");
+      toggleListenerCheckbox.type = "checkbox";
+      toggleListenerCheckbox.className =
+        "event-tooltip-listener-toggle-checkbox";
+      if (listener.eventListenerInfoId) {
+        toggleListenerCheckbox.checked = listener.enabled;
+        toggleListenerCheckbox.setAttribute(
+          "data-event-listener-info-id",
+          listener.eventListenerInfoId
+        );
+        toggleListenerCheckbox.addEventListener(
+          "change",
+          this._eventToggleCheckboxChanged,
+          { signal: this._eventListenersAbortController.signal }
+        );
+      } else {
+        toggleListenerCheckbox.checked = true;
+        toggleListenerCheckbox.setAttribute("disabled", true);
+      }
+      header.appendChild(toggleListenerCheckbox);
+
       // Content
       const editor = new Editor(config);
       this._eventEditors.set(content, {
         editor: editor,
         handler: listener.handler,
         native: listener.native,
         appended: false,
         location,
@@ -177,20 +205,31 @@ class EventTooltip {
     }
 
     this._tooltip.panel.innerHTML = "";
     this._tooltip.panel.appendChild(this.container);
     this._tooltip.setContentSize({ width: CONTAINER_WIDTH, height: Infinity });
   }
 
   _addContentListeners(header) {
-    header.addEventListener("click", this._headerClicked);
+    header.addEventListener("click", this._headerClicked, {
+      signal: this._eventListenersAbortController.signal,
+    });
   }
 
   _headerClicked(event) {
+    // Clicking on the checkbox shouldn't impact the header (checkbox state change is
+    // handled in _eventToggleCheckboxChanged).
+    if (
+      event.target.classList.contains("event-tooltip-listener-toggle-checkbox")
+    ) {
+      event.stopPropagation();
+      return;
+    }
+
     if (event.target.classList.contains("event-tooltip-debugger-icon")) {
       this._debugClicked(event);
       event.stopPropagation();
       return;
     }
 
     const doc = this._tooltip.doc;
     const header = event.currentTarget;
@@ -236,17 +275,17 @@ class EventTooltip {
 
         const container = header.parentElement.getBoundingClientRect();
         if (header.getBoundingClientRect().top < container.top) {
           header.scrollIntoView(true);
         } else if (content.getBoundingClientRect().bottom > container.bottom) {
           content.scrollIntoView(false);
         }
 
-        this._tooltip.emit("event-tooltip-ready");
+        this._tooltip.emitForTests("event-tooltip-ready");
       });
     }
   }
 
   _debugClicked(event) {
     const header = event.currentTarget;
     const content = header.nextElementSibling;
 
@@ -261,16 +300,27 @@ class EventTooltip {
         location.url,
         location.line,
         location.column,
         location.id
       );
     }
   }
 
+  async _eventToggleCheckboxChanged(event) {
+    const checkbox = event.currentTarget;
+    const id = checkbox.getAttribute("data-event-listener-info-id");
+    if (checkbox.checked) {
+      await this._nodeFront.enableEventListener(id);
+    } else {
+      await this._nodeFront.disableEventListener(id);
+    }
+    this._tooltip.emitForTests("event-tooltip-listener-toggled");
+  }
+
   /**
    * Parse URI and return {url, line, column}; or return null if it can't be parsed.
    */
   _parseLocation(uri) {
     if (uri && uri !== "?") {
       uri = uri.replace(/"/g, "");
 
       let matches = uri.match(/(.*):(\d+):(\d+$)/);
@@ -303,30 +353,22 @@ class EventTooltip {
         const { editor } = this._eventEditors.get(box);
         editor.destroy();
       }
 
       this._eventEditors = null;
       this._tooltip.eventTooltip = null;
     }
 
-    const headerNodes = this.container.querySelectorAll(".event-header");
-
-    for (const node of headerNodes) {
-      node.removeEventListener("click", this._headerClicked);
-    }
-
-    const sourceNodes = this.container.querySelectorAll(
-      ".event-tooltip-debugger-icon"
-    );
-    for (const node of sourceNodes) {
-      node.removeEventListener("click", this._debugClicked);
+    if (this._eventListenersAbortController) {
+      this._eventListenersAbortController.abort();
+      this._eventListenersAbortController = null;
     }
 
     for (const unsubscribe of this._subscriptions) {
       unsubscribe();
     }
 
-    this._toolbox = this._tooltip = null;
+    this._toolbox = this._tooltip = this._nodeFront = null;
   }
 }
 
 module.exports.EventTooltip = EventTooltip;
--- a/devtools/server/actors/descriptors/process.js
+++ b/devtools/server/actors/descriptors/process.js
@@ -3,16 +3,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { DevToolsServer } = require("devtools/server/devtools-server");
 const { Cc, Ci } = require("chrome");
 
+const {
+  createBrowserSessionContext,
+} = require("devtools/server/actors/watcher/session-context");
 const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
 const {
   processDescriptorSpec,
 } = require("devtools/shared/specs/descriptors/process");
 
 loader.lazyRequireGetter(
   this,
   "ContentProcessTargetActor",
@@ -152,22 +155,17 @@ const ProcessDescriptorActor = ActorClas
 
   /**
    * Return a Watcher actor, allowing to keep track of targets which
    * already exists or will be created. It also helps knowing when they
    * are destroyed.
    */
   getWatcher() {
     if (!this.watcher) {
-      this.watcher = new WatcherActor(this.conn, {
-        type: "all",
-        // For now, the top level target (ParentProcessTargetActor) is created via ProcessDescriptor.getTarget
-        // and is never replaced by any other, nor is it created by the WatcherActor.
-        isServerTargetSwitchingEnabled: false,
-      });
+      this.watcher = new WatcherActor(this.conn, createBrowserSessionContext());
       this.manage(this.watcher);
     }
     return this.watcher;
   },
 
   form() {
     return {
       actor: this.actorID,
--- a/devtools/server/actors/descriptors/tab.js
+++ b/devtools/server/actors/descriptors/tab.js
@@ -20,16 +20,19 @@ const {
 loader.lazyImporter(
   this,
   "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm"
 );
 const { ActorClassWithSpec, Actor } = require("devtools/shared/protocol");
 const { tabDescriptorSpec } = require("devtools/shared/specs/descriptors/tab");
 const { AppConstants } = require("resource://gre/modules/AppConstants.jsm");
+const {
+  createBrowserElementSessionContext,
+} = require("devtools/server/actors/watcher/session-context");
 
 loader.lazyRequireGetter(
   this,
   "WatcherActor",
   "devtools/server/actors/watcher",
   true
 );
 
@@ -176,23 +179,22 @@ const TabDescriptorActor = ActorClassWit
 
   /**
    * Return a Watcher actor, allowing to keep track of targets which
    * already exists or will be created. It also helps knowing when they
    * are destroyed.
    */
   getWatcher(config) {
     if (!this.watcher) {
-      this.watcher = new WatcherActor(this.conn, {
-        type: "browser-element",
-        browserId: this._browser.browserId,
-        // Nowaday, it should always be enabled except for WebExtension special
-        // codepath and some tests.
-        isServerTargetSwitchingEnabled: config.isServerTargetSwitchingEnabled,
-      });
+      this.watcher = new WatcherActor(
+        this.conn,
+        createBrowserElementSessionContext(this._browser, {
+          isServerTargetSwitchingEnabled: config.isServerTargetSwitchingEnabled,
+        })
+      );
       this.manage(this.watcher);
     }
     return this.watcher;
   },
 
   get _tabbrowser() {
     if (this._browser && typeof this._browser.getTabBrowser == "function") {
       return this._browser.getTabBrowser();
--- a/devtools/server/actors/descriptors/webextension.js
+++ b/devtools/server/actors/descriptors/webextension.js
@@ -14,16 +14,19 @@
 
 const protocol = require("devtools/shared/protocol");
 const {
   webExtensionDescriptorSpec,
 } = require("devtools/shared/specs/descriptors/webextension");
 const {
   connectToFrame,
 } = require("devtools/server/connectors/frame-connector");
+const {
+  createWebExtensionSessionContext,
+} = require("devtools/server/actors/watcher/session-context");
 
 loader.lazyImporter(
   this,
   "AddonManager",
   "resource://gre/modules/AddonManager.jsm"
 );
 loader.lazyImporter(
   this,
@@ -104,25 +107,27 @@ const WebExtensionDescriptorActor = prot
      * Return a Watcher actor, allowing to keep track of targets which
      * already exists or will be created. It also helps knowing when they
      * are destroyed.
      */
     async getWatcher(config = {}) {
       if (!this.watcher) {
         // Ensure connecting to the webextension frame in order to populate this._form
         await this._extensionFrameConnect();
-        this.watcher = new WatcherActor(this.conn, {
-          type: "webextension",
-          addonId: this.addonId,
-          addonBrowsingContextID: this._form.browsingContextID,
-          addonInnerWindowId: this._form.innerWindowId,
-          // For now, there is only one target (WebExtensionTargetActor), it is never replaced,
-          // and is only created via WebExtensionDescriptor.getTarget (and never by the watcher actor).
-          isServerTargetSwitchingEnabled: config.isServerTargetSwitchingEnabled,
-        });
+        this.watcher = new WatcherActor(
+          this.conn,
+          createWebExtensionSessionContext(
+            {
+              addonId: this.addonId,
+              browsingContextID: this._form.browsingContextID,
+              innerWindowId: this._form.innerWindowId,
+            },
+            config
+          )
+        );
         this.manage(this.watcher);
       }
       return this.watcher;
     },
 
     async getTarget() {
       const form = await this._extensionFrameConnect();
       // Merge into the child actor form, some addon metadata
--- a/devtools/server/actors/inspector/event-collector.js
+++ b/devtools/server/actors/inspector/event-collector.js
@@ -380,19 +380,21 @@ class DOMEventCollector extends MainEven
 
       // If this is checking if a node has any listeners then we have found one
       // so return now.
       if (checkOnly) {
         return true;
       }
 
       const eventInfo = {
+        nsIEventListenerInfo: listener,
         capturing: listener.capturing,
         type: listener.type,
         handler: handler,
+        enabled: listener.enabled,
       };
 
       handlers.push(eventInfo);
     }
 
     // If this is checking if a node has any listeners then none were found so
     // return false.
     if (checkOnly) {
@@ -815,26 +817,30 @@ class EventCollector {
 
     return this._chromeEnabled;
   }
 
   /**
    *
    * @param  {DOMNode} node
    *         The node for which events are to be gathered.
-   * @return {Array}
+   * @return {Array<Object>}
    *         An array containing objects in the following format:
    *         {
-   *           type: type,        // e.g. "click"
-   *           handler: handler,  // The function called when event is triggered.
-   *           tags: "jQuery",    // Comma separated list of tags displayed
-   *                              // inside event bubble.
-   *           hide: {            // Flags for hiding certain properties.
-   *             capturing: true,
+   *           {String} type: The event type, e.g. "click"
+   *           {Function} handler: The function called when event is triggered.
+   *           {Boolean} enabled: Whether the listener is enabled or not (event listeners can
+   *                     be disabled via the inspector)
+   *           {String} tags: Comma separated list of tags displayed inside event bubble (e.g. "JQuery")
+   *           {Object} hide: Flags for hiding certain properties.
+   *             {Boolean} capturing
    *           }
+   *           {Boolean} native
+   *           {String|undefined} sourceActor: The sourceActor id of the event listener
+   *           {nsIEventListenerInfo|undefined} nsIEventListenerInfo
    *         }
    */
   getEventListeners(node) {
     const listenerArray = [];
     let dbg;
     if (!this.chromeEnabled) {
       dbg = new Debugger();
     } else {
@@ -890,17 +896,20 @@ class EventCollector {
    *             type: "click",
    *             handler: function() { doSomething() },
    *             origin: "http://www.mozilla.com",
    *             tags: tags,
    *             capturing: true,
    *             hide: {
    *               capturing: true
    *             },
-   *             native: false
+   *             native: false,
+   *             enabled: true
+   *             sourceActor: "sourceActor.1234",
+   *             nsIEventListenerInfo: nsIEventListenerInfo {…},
    *           }
    */
   // eslint-disable-next-line complexity
   processHandlerForEvent(listener, dbg, normalizeListener) {
     let globalDO;
     let eventObj;
 
     try {
@@ -918,16 +927,17 @@ class EventCollector {
         listenerDO = normalizeListener(listenerDO, listener);
       }
 
       const hide = listener.hide || {};
       const override = listener.override || {};
       const tags = listener.tags || "";
       const type = listener.type || "";
       let isScriptBoundToNonScriptElement = false;
+      const enabled = !!listener.enabled;
       let functionSource = handler.toString();
       let line = 0;
       let column = null;
       let native = false;
       let url = "";
       let sourceActor = "";
 
       // If the listener is an object with a 'handleEvent' method, use that.
@@ -1034,16 +1044,18 @@ class EventCollector {
         tags: override.tags || tags,
         capturing:
           typeof override.capturing !== "undefined"
             ? override.capturing
             : capturing,
         hide: typeof override.hide !== "undefined" ? override.hide : hide,
         native,
         sourceActor,
+        nsIEventListenerInfo: listener.nsIEventListenerInfo,
+        enabled,
       };
 
       // Hide the debugger icon for DOM0 and native listeners. DOM0 listeners are
       // generated dynamically from e.g. an onclick="" attribute so the script
       // doesn't actually exist.
       if (native || isScriptBoundToNonScriptElement) {
         eventObj.hide.debugger = true;
       }
--- a/devtools/server/actors/inspector/node.js
+++ b/devtools/server/actors/inspector/node.js
@@ -90,16 +90,19 @@ const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20
  * Server side of the node actor.
  */
 const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
   initialize: function(walker, node) {
     protocol.Actor.prototype.initialize.call(this, null);
     this.walker = walker;
     this.rawNode = node;
     this._eventCollector = new EventCollector(this.walker.targetActor);
+    // Map<id -> nsIEventListenerInfo> that we maintain to be able to disable/re-enable event listeners
+    // The id is generated from getEventListenerInfo
+    this._nsIEventListenersInfo = new Map();
 
     // Store the original display type and scrollable state and whether or not the node is
     // displayed to track changes when reflows occur.
     const wasScrollable = this.isScrollable;
 
     this.currentDisplayType = this.displayType;
     this.wasDisplayed = this.isDisplayed;
     this.wasScrollable = wasScrollable;
@@ -154,16 +157,32 @@ const NodeActor = protocol.ActorClassWit
       this._waitForFrameLoadAbortController.abort();
       this._waitForFrameLoadAbortController = null;
     }
     if (this._waitForFrameLoadIntervalId) {
       clearInterval(this._waitForFrameLoadIntervalId);
       this._waitForFrameLoadIntervalId = null;
     }
 
+    if (this._nsIEventListenersInfo) {
+      // Re-enable all event listeners that we might have disabled
+      for (const nsIEventListenerInfo of this._nsIEventListenersInfo.values()) {
+        // If event listeners/node don't exist anymore, accessing nsIEventListenerInfo.enabled
+        // will throw.
+        try {
+          if (!nsIEventListenerInfo.enabled) {
+            nsIEventListenerInfo.enabled = true;
+          }
+        } catch (e) {
+          // ignore
+        }
+      }
+      this._nsIEventListenersInfo = null;
+    }
+
     this._eventCollector.destroy();
     this._eventCollector = null;
     this.rawNode = null;
     this.walker = null;
   },
 
   // Returns the JSON representation of this object over the wire.
   form: function() {
@@ -555,17 +574,66 @@ const NodeActor = protocol.ActorClassWit
       };
     });
   },
 
   /**
    * Get all event listeners that are listening on this node.
    */
   getEventListenerInfo: function() {
-    return this._eventCollector.getEventListeners(this.rawNode);
+    this._nsIEventListenersInfo.clear();
+
+    const eventListenersData = this._eventCollector.getEventListeners(
+      this.rawNode
+    );
+    let counter = 0;
+    for (const eventListenerData of eventListenersData) {
+      if (eventListenerData.nsIEventListenerInfo) {
+        const id = `event-listener-info-${++counter}`;
+        this._nsIEventListenersInfo.set(
+          id,
+          eventListenerData.nsIEventListenerInfo
+        );
+
+        eventListenerData.eventListenerInfoId = id;
+        // remove the nsIEventListenerInfo since we don't want to send it to the client.
+        delete eventListenerData.nsIEventListenerInfo;
+      }
+    }
+    return eventListenersData;
+  },
+
+  /**
+   * Disable a specific event listener given its associated id
+   *
+   * @param {String} eventListenerInfoId
+   */
+  disableEventListener: function(eventListenerInfoId) {
+    const nsEventListenerInfo = this._nsIEventListenersInfo.get(
+      eventListenerInfoId
+    );
+    if (!nsEventListenerInfo) {
+      throw new Error("Unkown nsEventListenerInfo");
+    }
+    nsEventListenerInfo.enabled = false;
+  },
+
+  /**
+   * (Re-)enable a specific event listener given its associated id
+   *
+   * @param {String} eventListenerInfoId
+   */
+  enableEventListener: function(eventListenerInfoId) {
+    const nsEventListenerInfo = this._nsIEventListenersInfo.get(
+      eventListenerInfoId
+    );
+    if (!nsEventListenerInfo) {
+      throw new Error("Unkown nsEventListenerInfo");
+    }
+    nsEventListenerInfo.enabled = true;
   },
 
   /**
    * Modify a node's attributes.  Passed an array of modifications
    * similar in format to "attributes" mutations.
    * {
    *   attributeName: <string>
    *   attributeNamespace: <optional string>
--- a/devtools/server/actors/network-monitor/network-event-actor.js
+++ b/devtools/server/actors/network-monitor/network-event-actor.js
@@ -14,18 +14,18 @@ const { LongStringActor } = require("dev
 
 /**
  * Creates an actor for a network event.
  *
  * @constructor
  * @param DevToolsServerConnection conn
  *        The connection into which this Actor will be added.
  * @param object sessionContext
- *        WatcherActor's session context. This helps know what is the overall debugged scope.
- *        See watcher actor constructor for more info.
+ *        The Session Context to help know what is debugged.
+ *        See devtools/server/actors/watcher/session-context.js
  * @param object options
  *        Dictionary object with the following attributes:
  *        - onNetworkEventUpdate: optional function
  *          Listener for updates for the network event
  */
 const NetworkEventActor = protocol.ActorClassWithSpec(networkEventSpec, {
   initialize(
     conn,
--- a/devtools/server/actors/targets/target-actor-registry.jsm
+++ b/devtools/server/actors/targets/target-actor-registry.jsm
@@ -33,19 +33,18 @@ var TargetActorRegistry = {
   unregisterXpcShellTargetActor(targetActor) {
     xpcShellTargetActor = null;
   },
 
   /**
    * Return the first target actor matching the passed watcher's session context. Returns null if
    * no matching target actors could be found.
    *
-   * @param {Object} sessionContext: WatcherActor's session context. To only retrieve targets related
-   *                          to the scope of this watcher actor.
-   *                          See watcher actor constructor for more info.
+   * @param {Object} sessionContext: The Session Context to help know what is debugged.
+   *                                 See devtools/server/actors/watcher/session-context.js
    * @param {String} connectionPrefix: DevToolsServerConnection's prefix, in order to select only actor
    *                                   related to the same connection. i.e. the same client.
    * @returns {TargetActor|null}
    */
   getTopLevelTargetActorForContext(sessionContext, connectionPrefix) {
     if (sessionContext.type == "all") {
       if (
         Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT
@@ -73,19 +72,18 @@ var TargetActorRegistry = {
     throw new Error("Unsupported session context type: " + sessionContext.type);
   },
 
   /**
    * Return the target actors matching the passed browser element id.
    * In some scenarios, the registry can have multiple target actors for a given
    * browserId (e.g. the regular DevTools content toolbox + DevTools WebExtensions targets).
    *
-   * @param {Object} sessionContext: WatcherActor's session context. To only retrieve targets related
-   *                          to the scope of this watcher actor.
-   *                          See watcher actor constructor for more info.
+   * @param {Object} sessionContext: The Session Context to help know what is debugged.
+   *                                 See devtools/server/actors/watcher/session-context.js
    * @param {String} connectionPrefix: DevToolsServerConnection's prefix, in order to select only actor
    *                                   related to the same connection. i.e. the same client.
    * @returns {Array<TargetActor>}
    */
   getTargetActors(sessionContext, connectionPrefix) {
     const actors = [];
     for (const actor of windowGlobalTargetActors) {
       const isMatchingPrefix = actor.actorID.startsWith(connectionPrefix);
--- a/devtools/server/actors/targets/window-global.js
+++ b/devtools/server/actors/targets/window-global.js
@@ -251,18 +251,18 @@ const windowGlobalTargetPrototype = {
    *          At the moment this only impacts the WindowGlobalTarget `reconfigure`
    *          implementation. But for server-side target switching this flag will be exposed
    *          to the client and should be available for all target actor classes. It will be
    *          used to detect target switching. (Bug 1644397)
    *        - ignoreSubFrames Boolean
    *          If true, the actor will only focus on the passed docShell and not on the whole
    *          docShell tree. This should be enabled when we have targets for all documents.
    *        - sessionContext Object
-   *          WatcherActor's session context. This helps know what is the overall debugged scope.
-   *          See watcher actor constructor for more info.
+   *          The Session Context to help know what is debugged.
+   *          See devtools/server/actors/watcher/session-context.js
    */
   initialize: function(
     connection,
     {
       docShell,
       followWindowGlobalLifeCycle,
       isTopLevelTarget,
       ignoreSubFrames,
--- a/devtools/server/actors/watcher.js
+++ b/devtools/server/actors/watcher.js
@@ -71,25 +71,19 @@ exports.WatcherActor = protocol.ActorCla
    *   This is done via watchResources/unwatchResources methods, and
    *   resource-available-form/resource-updated-form/resource-destroyed-form events.
    *   Note that these events are also emited on both the watcher actor,
    *   for resources observed from the parent process, as well as on the
    *   target actors, when the resources are observed from the target's process or thread.
    *
    * @param {DevToolsServerConnection} conn
    *        The connection to use in order to communicate back to the client.
-   * @param {Object} sessionContext
-   *        Mandatory argument to define the debugged context of this actor.
-   *        Note that as this object is passed to other processes and thread,
-   *        this should be a serializable object.
-   * @param {String} sessionContext.type: The type of debugged context.
-   *        Can be:
-   *        - "all", to debug everything in the browser.
-   *        - "browser-element", to focus on one given <browser> element
-   *          and all its children resources (workers, iframes,...)
+   * @param {object} sessionContext
+   *        The Session Context to help know what is debugged.
+   *        See devtools/server/actors/watcher/session-context.js
    * @param {Number} sessionContext.browserId: If this is a "browser-element" context type,
    *        the "browserId" of the <browser> element we would like to debug.
    * @param {Boolean} sessionContext.isServerTargetSwitchingEnabled: Flag to to know if we should
    *        spawn new top level targets for the debugged context.
    */
   initialize: function(conn, sessionContext) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this._sessionContext = sessionContext;
--- a/devtools/server/actors/watcher/WatcherRegistry.jsm
+++ b/devtools/server/actors/watcher/WatcherRegistry.jsm
@@ -36,18 +36,18 @@ const { SessionDataHelpers } = ChromeUti
   "resource://devtools/server/actors/watcher/SessionDataHelpers.jsm"
 );
 const { SUPPORTED_DATA } = SessionDataHelpers;
 
 // Define the Map that will be saved in `sharedData`.
 // It is keyed by WatcherActor ID and values contains following attributes:
 // - targets: Set of strings, refering to target types to be listened to
 // - resources: Set of strings, refering to resource types to be observed
-// - sessionContext: WatcherActor's session context. Describe what the watcher should be debugging.
-//            See watcher actor constructor for more info.
+// - sessionContext Object, The Session Context to help know what is debugged.
+//     See devtools/server/actors/watcher/session-context.js
 // - connectionPrefix: The DevToolsConnection prefix of the watcher actor. Used to compute new actor ID in the content processes.
 //
 // Unfortunately, `sharedData` is subject to race condition and may have side effect
 // when read/written from multiple places in the same process,
 // which is why this map should be considered as the single source of truth.
 const sessionDataByWatcherActor = new Map();
 
 // In parallel to the previous map, keep all the WatcherActor keyed by the same WatcherActor ID,
--- a/devtools/server/actors/watcher/browsing-context-helpers.jsm
+++ b/devtools/server/actors/watcher/browsing-context-helpers.jsm
@@ -25,18 +25,18 @@ const isEveryFrameTargetEnabled = Servic
 
 /**
  * Helper function to know if a given BrowsingContext should be debugged by scope
  * described by the given session context.
  *
  * @param {BrowsingContext} browsingContext
  *        The browsing context we want to check if it is part of debugged context
  * @param {Object} sessionContext
- *        WatcherActor's session context. This helps know what is the overall debugged scope.
- *        See watcher actor constructor for more info.
+ *        The Session Context to help know what is debugged.
+ *        See devtools/server/actors/watcher/session-context.js
  * @param {Object} options
  *        Optional arguments passed via a dictionary.
  * @param {Boolean} options.forceAcceptTopLevelTarget
  *        If true, we will accept top level browsing context even when server target switching
  *        is disabled. In case of client side target switching, the top browsing context
  *        is debugged via a target actor that is being instantiated manually by the frontend.
  *        And this target actor isn't created, nor managed by the watcher actor.
  * @param {Boolean} options.acceptInitialDocument
@@ -164,18 +164,18 @@ function isBrowsingContextPartOfContext(
 
 /**
  * Helper function of isBrowsingContextPartOfContext to execute all checks
  * against WindowGlobal interface which aren't specific to a given SessionContext type
  *
  * @param {WindowGlobalParent|WindowGlobalChild} windowGlobal
  *        The WindowGlobal we want to check if it is part of debugged context
  * @param {Object} sessionContext
- *        WatcherActor's session context. This helps know what is the overall debugged scope.
- *        See watcher actor constructor for more info.
+ *        The Session Context to help know what is debugged.
+ *        See devtools/server/actors/watcher/session-context.js
  * @param {Object} options
  *        Optional arguments passed via a dictionary.
  *        See `isBrowsingContextPartOfContext` jsdoc.
  */
 function _validateWindowGlobal(
   windowGlobal,
   sessionContext,
   { acceptInitialDocument, acceptSameProcessIframes }
@@ -227,18 +227,18 @@ function _validateWindowGlobal(
 /**
  * Helper function to know if a given WindowGlobal should be debugged by scope
  * described by the given session context. This method could be called from any process
  * as so accept either WindowGlobalParent or WindowGlobalChild instances.
  *
  * @param {WindowGlobalParent|WindowGlobalChild} windowGlobal
  *        The WindowGlobal we want to check if it is part of debugged context
  * @param {Object} sessionContext
- *        WatcherActor's session context. This helps know what is the overall debugged scope.
- *        See watcher actor constructor for more info.
+ *        The Session Context to help know what is debugged.
+ *        See devtools/server/actors/watcher/session-context.js
  * @param {Object} options
  *        Optional arguments passed via a dictionary.
  *        See `isBrowsingContextPartOfContext` jsdoc.
  */
 function isWindowGlobalPartOfContext(windowGlobal, sessionContext, options) {
   return isBrowsingContextPartOfContext(
     windowGlobal.browsingContext,
     sessionContext,
@@ -253,18 +253,18 @@ function isWindowGlobalPartOfContext(win
  * Get all the BrowsingContexts that should be debugged by the given session context.
  *
  * Really all of them:
  * - For all the privileged windows (browser.xhtml, browser console, ...)
  * - For all chrome *and* content contexts (privileged windows, as well as <browser> elements and their inner content documents)
  * - For all nested browsing context. We fetch the contexts recursively.
  *
  * @param {Object} sessionContext
- *        WatcherActor's session context. This helps know what is the overall debugged scope.
- *        See watcher actor constructor for more info.
+ *        The Session Context to help know what is debugged.
+ *        See devtools/server/actors/watcher/session-context.js
  * @param {Object} options
  *        Optional arguments passed via a dictionary.
  * @param {Boolean} options.acceptSameProcessIframes
  *        If true, we will accept WindowGlobal that runs in the same process as their parent document.
  *        That, even when EFT is disabled.
  */
 function getAllBrowsingContextsForContext(
   sessionContext,
--- a/devtools/server/actors/watcher/moz.build
+++ b/devtools/server/actors/watcher/moz.build
@@ -5,11 +5,12 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     "target-helpers",
 ]
 
 DevToolsModules(
     "browsing-context-helpers.jsm",
+    "session-context.js",
     "SessionDataHelpers.jsm",
     "WatcherRegistry.jsm",
 )
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/watcher/session-context.js
@@ -0,0 +1,100 @@
+/* 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";
+
+// Module to create all the Session Context objects.
+//
+// These are static JSON serializable object that help describe
+// the debugged context. It is passed around to most of the server codebase
+// in order to know which object to consider inspecting and communicating back to the client.
+//
+// These objects are all instantiated by the Descriptor actors
+// and passed as a constructor argument to the Watcher actor.
+//
+// These objects have only two attributes used by all the Session contexts:
+// - type: String
+//   Describes which type of context we are debugging.
+//   Can be "all", "browser-element" or "webextension".
+//   See each create* method for more info about each type and their specific attributes.
+// - isServerTargetSwitchingEnabled: Boolean
+//   If true, targets should all be spawned by the server codebase.
+//   Especially the first, top level target.
+
+/**
+ * Create the SessionContext used by the Browser Toolbox and Browser Console.
+ *
+ * This context means debugging everything.
+ * The whole browser:
+ * - all processes: parent and content,
+ * - all privileges: privileged/chrome and content/web,
+ * - all components/targets: HTML documents, processes, workers, add-ons,...
+ */
+function createBrowserSessionContext() {
+  return {
+    type: "all",
+    // For now, the top level target (ParentProcessTargetActor) is created via ProcessDescriptor.getTarget
+    // and is never replaced by any other, nor is it created by the WatcherActor.
+    isServerTargetSwitchingEnabled: false,
+  };
+}
+
+/**
+ * Create the SessionContext used by the regular web page toolboxes as well as remote debugging android device tabs.
+ *
+ * @param {BrowserElement} browserElement
+ *        The tab to debug. It should be a reference to a <browser> element.
+ * @param {Object} config
+ *        An object with optional configuration. Only supports "isServerTargetSwitchingEnabled" attribute.
+ *        See jsdoc in this file header for more info.
+ */
+function createBrowserElementSessionContext(browserElement, config) {
+  return {
+    type: "browser-element",
+    browserId: browserElement.browserId,
+    // Nowaday, it should always be enabled except for WebExtension special
+    // codepath and some tests.
+    isServerTargetSwitchingEnabled: config.isServerTargetSwitchingEnabled,
+  };
+}
+
+/**
+ * Create the SessionContext used by the web extension toolboxes.
+ *
+ * @param {Object} addon
+ *        First object argument to describe the add-on.
+ * @param {String} addon.addonId
+ *        The web extension ID, to uniquely identify the debugged add-on.
+ * @param {String} addon.browsingContextID
+ *        The ID of the BrowsingContext into which this add-on is loaded.
+ *        For now the top level target is associated with this one precise BrowsingContext.
+ *        Knowing about it later helps associate resources to the same BrowsingContext ID and so the same target.
+ * @param {String} addon.innerWindowId
+ *        The ID of the WindowGlobal into which this add-on is loaded.
+ *        This is used for the same reason as browsingContextID. It helps match the resource with the right target.
+ *        We now also use the WindowGlobal ID/innerWindowId to identify the targets.
+ * @param {Object} config
+ *        An object with optional configuration. Only supports "isServerTargetSwitchingEnabled" attribute.
+ *        See jsdoc in this file header for more info.
+ */
+function createWebExtensionSessionContext(
+  { addonId, browsingContextID, innerWindowId },
+  config
+) {
+  return {
+    type: "webextension",
+    addonId: addonId,
+    addonBrowsingContextID: browsingContextID,
+    addonInnerWindowId: innerWindowId,
+    // For now, there is only one target (WebExtensionTargetActor), it is never replaced,
+    // and is only created via WebExtensionDescriptor.getTarget (and never by the watcher actor).
+    isServerTargetSwitchingEnabled: config.isServerTargetSwitchingEnabled,
+  };
+}
+
+module.exports = {
+  createBrowserSessionContext,
+  createBrowserElementSessionContext,
+  createWebExtensionSessionContext,
+};
--- a/devtools/shared/specs/node.js
+++ b/devtools/shared/specs/node.js
@@ -109,16 +109,28 @@ const nodeSpec = generateActorSpec({
       response: RetVal("imageData"),
     },
     getEventListenerInfo: {
       request: {},
       response: {
         events: RetVal("json"),
       },
     },
+    enableEventListener: {
+      request: {
+        eventListenerInfoId: Arg(0),
+      },
+      response: {},
+    },
+    disableEventListener: {
+      request: {
+        eventListenerInfoId: Arg(0),
+      },
+      response: {},
+    },
     modifyAttributes: {
       request: {
         modifications: Arg(0, "array:json"),
       },
       response: {},
     },
     getFontFamilyDataURL: {
       request: { font: Arg(0, "string"), fillStyle: Arg(1, "nullable:string") },
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -812,20 +812,24 @@ class WorkerFetchResponseEndRunnable fin
           ->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     }
 
     WorkerRunInternal(aWorkerPrivate);
     return true;
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is called twice
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     // Execute Run anyway to make sure we cleanup our promise proxy to avoid
     // leaking the worker thread
     Run();
-    return WorkerRunnable::Cancel();
+    return NS_OK;
   }
 };
 
 class WorkerFetchResponseEndControlRunnable final
     : public MainThreadWorkerControlRunnable,
       public WorkerFetchResponseEndBase {
  public:
   WorkerFetchResponseEndControlRunnable(WorkerPrivate* aWorkerPrivate,
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -496,16 +496,24 @@ nsresult FileReader::GetAsDataURL(Blob* 
 
 /* virtual */
 JSObject* FileReader::WrapObject(JSContext* aCx,
                                  JS::Handle<JSObject*> aGivenProto) {
   return FileReader_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void FileReader::StartProgressEventTimer() {
+  if (!NS_IsMainThread() && !mWeakWorkerRef) {
+    // The worker is possibly shutting down if dispatching a DOM event right
+    // before this call triggered an InterruptCallback call.
+    // XXX Note, the check is limited to workers for now, since it is unclear
+    // in the spec how FileReader should behave in this case on the main thread.
+    return;
+  }
+
   if (!mProgressNotifier) {
     mProgressNotifier = NS_NewTimer(mTarget);
   }
 
   if (mProgressNotifier) {
     mProgressEventWasDelayed = false;
     mTimerIsActive = true;
     mProgressNotifier->Cancel();
--- a/dom/file/StreamBlobImpl.cpp
+++ b/dom/file/StreamBlobImpl.cpp
@@ -58,17 +58,22 @@ StreamBlobImpl::StreamBlobImpl(already_A
                                int64_t aLastModifiedDate, uint64_t aLength,
                                const nsAString& aBlobImplType)
     : BaseBlobImpl(aName, aContentType, aLength, aLastModifiedDate),
       mInputStream(std::move(aInputStream)),
       mBlobImplType(aBlobImplType),
       mIsDirectory(false),
       mFileId(-1) {}
 
-StreamBlobImpl::~StreamBlobImpl() { UnregisterWeakMemoryReporter(this); }
+StreamBlobImpl::~StreamBlobImpl() {
+  if (mInputStream) {
+    mInputStream->Close();
+  }
+  UnregisterWeakMemoryReporter(this);
+}
 
 void StreamBlobImpl::CreateInputStream(nsIInputStream** aStream,
                                        ErrorResult& aRv) {
   nsCOMPtr<nsIInputStream> clonedStream;
   nsCOMPtr<nsIInputStream> replacementStream;
 
   aRv = NS_CloneInputStream(mInputStream, getter_AddRefs(clonedStream),
                             getter_AddRefs(replacementStream));
new file mode 100644
--- /dev/null
+++ b/dom/file/tests/crashtests/1748342.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script id="worker1" type="javascript/worker">
+    self.onmessage = async function (e) {
+      self.close()
+      const reader = new FileReader()
+      for (let i = 0; i < 25; i++) {
+        try { reader.readAsBinaryString(e.data[0]) } catch (e) {}
+      }
+      reader.addEventListener("progress", () => {}, {})
+    }
+  </script>
+  <script>
+    window.addEventListener("load", () => {
+      const script = new Blob([document.querySelector("#worker1").textContent], { type: "text/javascript" })
+      const worker = new Worker(window.URL.createObjectURL(script))
+      const data = new Blob(["70𯸟\nℽ㮼٠𛪇\0𛃧كe۰҅妽󠶖󠙻𝅧𡴶𝌋쮁偵9󠰾󠋼7\r𐇽0🥂.\b፟+⍳፟D󠢪3𣚽󠜎🐾�c_߰a<<=𝅦9𝆭𛰅9󠵼ௌ󠌌𖓄ΐ0�⡖‑뢈/-᭰*٠٪᷁e �𯯐𯬓𯕏‫‑걢V*=­￿**󠛐\u2028שּׁ&‭0󠄯𒡯e\n𛰵󠥿⁤𫍰,󠶒𝅥t\nl𧶈a𦜠09k𯔆䴋�|󠄷🦻𛝫󠁥𖭄", "*𒗬۰0\u2029‌/\n\r+󠎔𖼣k*=🿹𯀊\r٪\r󠳐𝲍慑B�\r󠮱\r\n\"\r\\𯉆۹c卑4󠣰󠻬鴗ꛌ\0⌕:\r\n𝚨9ꛅ٠󠃭𯋾\nJ󠯲9\r゙鈷P\u2029҉󠷒۹e \b緁︡𤆥^𯏂゚੿|٫󠕉揅ᷛ𩊜s𯑳2凅9c8H𦰤-\f%٠𨮫󠚵‑2𫈮P𝋄窥57\n-゙҄H󠭗𯪞𣃂-ᷢשּׁ貌솽|𝉃‬c㙡𯐉᭯mL\r"], {
+        "type": "image/png",
+        "endings": "transparent"
+      })
+      worker.postMessage([data], [])
+    })
+  </script>
+</head>
+</html>
--- a/dom/file/tests/crashtests/crashtests.list
+++ b/dom/file/tests/crashtests/crashtests.list
@@ -1,3 +1,4 @@
 skip-if(ThreadSanitizer) load 1480354.html
 load 1562891.html
 skip-if(Android||ThreadSanitizer) load 1747185.html # Crashes on Android, times out on TSan.
+load 1748342.html
--- a/dom/file/tests/mochitest.ini
+++ b/dom/file/tests/mochitest.ini
@@ -36,10 +36,13 @@ support-files =
 skip-if = (verify && !debug && (os == 'win'))
 [test_fileapi_slice_memFile_1.html]
 [test_fileapi_slice_memFile_2.html]
 [test_fileapi_slice_image.html]
 [test_mozfiledataurl.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_bug1507893.html]
 support-files = worker_bug1507893.js
+[test_bug1742540.html]
+support-files = worker_bug1742540.js
+skip-if = toolkit == 'android' #TIMED_OUT
 [test_blob_reading.html]
 support-files = common_blob_reading.js worker_blob_reading.js
new file mode 100644
--- /dev/null
+++ b/dom/file/tests/test_bug1742540.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1742540</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+  <script>
+
+SimpleTest.waitForExplicitFinish();
+
+add_task(function setupPrefs() {
+  return SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.serviceWorkers.enabled", true],
+      ["dom.serviceWorkers.testing.enabled", true],
+    ],
+  });
+});
+
+function get_file() {
+  return new Promise(resolve => {
+    let xhr = new XMLHttpRequest();
+    xhr.open("GET", "/dynamic/getMyDirectory.sjs", false);
+    xhr.send();
+    let basePath = xhr.responseText;
+
+    let script = SpecialPowers.loadChromeScript(
+      SimpleTest.getTestFileURL("create_file_objects.js")
+    );
+    script.addMessageListener("created-file-objects", files => {
+      resolve(files[0]);
+    });
+    script.sendAsyncMessage("create-file-objects", {
+      fileNames: [basePath + "file_mozfiledataurl_audio.ogg"],
+    });
+  });
+}
+
+function wait_for_message(port, expected_message) {
+  return new Promise(resolve => {
+    port.onmessage = event => {
+      port.onmessage = null;
+      ok(event.data === expected_message, event.data);
+      resolve();
+    };
+  });
+}
+
+function unregister_and_done(registration) {
+  return registration.unregister().then(() => {
+    ok(true, "Will find leaks of nsPipe in BloatView without fix.");
+    SimpleTest.finish;
+  });
+}
+
+add_task(async function send_file_to_serviceworker() {
+  let registration = await navigator.serviceWorker
+    .register("worker_bug1742540.js", { scope: "./" })
+    .then(() => {
+      return navigator.serviceWorker.ready;
+    });
+
+  ok(registration.active, "ServiceWorker is activated");
+
+  let file = await get_file();
+  ok(file.size > 100000, "File size is big enough.");
+  let message = "ServiceWorker receives a file and did not reference it.";
+  let channel = new MessageChannel();
+  let received = wait_for_message(channel.port1, message);
+  registration.active.postMessage({ port: channel.port2, message, file }, [
+    channel.port2,
+  ]);
+  await received;
+
+  let finish = await unregister_and_done(registration);
+});
+
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/file/tests/worker_bug1742540.js
@@ -0,0 +1,5 @@
+onmessage = e => {
+  let file = e.data.file;
+  let port = e.data.port;
+  port.postMessage(e.data.message);
+};
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2909,41 +2909,39 @@ HTMLInputElement* HTMLInputElement::GetS
 
   nsAutoString name;
   GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
 
   HTMLInputElement* selected = container->GetCurrentRadioButton(name);
   return selected;
 }
 
-nsresult HTMLInputElement::MaybeSubmitForm(nsPresContext* aPresContext) {
+void HTMLInputElement::MaybeSubmitForm(nsPresContext* aPresContext) {
   if (!mForm) {
     // Nothing to do here.
-    return NS_OK;
+    return;
   }
 
   RefPtr<PresShell> presShell = aPresContext->GetPresShell();
   if (!presShell) {
-    return NS_OK;
+    return;
   }
 
   // Get the default submit element
   if (RefPtr<nsGenericHTMLFormElement> submitContent =
           mForm->GetDefaultSubmitElement()) {
     WidgetMouseEvent event(true, eMouseClick, nullptr, WidgetMouseEvent::eReal);
     nsEventStatus status = nsEventStatus_eIgnore;
     presShell->HandleDOMEventWithTarget(submitContent, &event, &status);
   } else if (!mForm->ImplicitSubmissionIsDisabled()) {
     // If there's only one text control, just submit the form
     // Hold strong ref across the event
     RefPtr<mozilla::dom::HTMLFormElement> form(mForm);
     form->MaybeSubmit(nullptr);
   }
-
-  return NS_OK;
 }
 
 void HTMLInputElement::SetCheckedInternal(bool aChecked, bool aNotify) {
   // Set the value
   mChecked = aChecked;
 
   // Notify the frame
   if (mType == FormControlType::InputCheckbox ||
@@ -3565,20 +3563,22 @@ bool HTMLInputElement::StepsInputValue(
     return false;
   }
   if (!IsMutable()) {
     return false;
   }
   return true;
 }
 
-static bool ActivatesWithKeyboard(FormControlType aType) {
+static bool ActivatesWithKeyboard(FormControlType aType, uint32_t aKeyCode) {
   switch (aType) {
     case FormControlType::InputCheckbox:
     case FormControlType::InputRadio:
+      // Checkbox and Radio try to submit on Enter press
+      return aKeyCode != NS_VK_RETURN;
     case FormControlType::InputButton:
     case FormControlType::InputReset:
     case FormControlType::InputSubmit:
     case FormControlType::InputFile:
     case FormControlType::InputImage:  // Bug 34418
     case FormControlType::InputColor:
       return true;
     default:
@@ -3725,24 +3725,19 @@ nsresult HTMLInputElement::PostHandleEve
 
   if (NS_SUCCEEDED(rv)) {
     WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
     if (keyEvent && StepsInputValue(*keyEvent)) {
       StepNumberControlForUserEvent(keyEvent->mKeyCode == NS_VK_UP ? 1 : -1);
       FireChangeEventIfNeeded();
       aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
     } else if (!preventDefault) {
-      // Checkbox and Radio try to submit on Enter press
-      if (aVisitor.mEvent->mMessage == eKeyPress &&
-          (mType == FormControlType::InputCheckbox ||
-           mType == FormControlType::InputRadio) &&
-          keyEvent->mKeyCode == NS_VK_RETURN && aVisitor.mPresContext) {
-        MaybeSubmitForm(aVisitor.mPresContext);
-      } else if (ActivatesWithKeyboard(mType)) {
-        // Otherwise we maybe dispatch a synthesized click.
+      if (keyEvent && ActivatesWithKeyboard(mType, keyEvent->mKeyCode) &&
+          keyEvent->IsTrusted()) {
+        // We maybe dispatch a synthesized click for keyboard activation.
         HandleKeyboardActivation(aVisitor);
       }
 
       switch (aVisitor.mEvent->mMessage) {
         case eFocus: {
           // see if we should select the contents of the textbox. This happens
           // for text and password fields when the field was focused by the
           // keyboard or a navigation, the platform allows it, and it wasn't
@@ -3824,24 +3819,28 @@ nsresult HTMLInputElement::PostHandleEve
            * (a) if there is a submit control in the form, click the first
            *     submit control in the form.
            * (b) if there is just one text control in the form, submit by
            *     sending a submit event directly to the form
            * (c) if there is more than one text input and no submit buttons, do
            *     not submit, period.
            */
 
-          if (keyEvent->mKeyCode == NS_VK_RETURN &&
+          if (keyEvent->mKeyCode == NS_VK_RETURN && keyEvent->IsTrusted() &&
               (IsSingleLineTextControl(false, mType) ||
-               mType == FormControlType::InputNumber ||
-               IsDateTimeInputType(mType))) {
-            FireChangeEventIfNeeded();
+               IsDateTimeInputType(mType) ||
+               mType == FormControlType::InputCheckbox ||
+               mType == FormControlType::InputRadio)) {
+            if (IsSingleLineTextControl(false, mType) ||
+                IsDateTimeInputType(mType)) {
+              FireChangeEventIfNeeded();
+            }
+
             if (aVisitor.mPresContext) {
-              rv = MaybeSubmitForm(aVisitor.mPresContext);
-              NS_ENSURE_SUCCESS(rv, rv);
+              MaybeSubmitForm(aVisitor.mPresContext);
             }
           }
 
           if (mType == FormControlType::InputRange && !keyEvent->IsAlt() &&
               !keyEvent->IsControl() && !keyEvent->IsMeta() &&
               (keyEvent->mKeyCode == NS_VK_LEFT ||
                keyEvent->mKeyCode == NS_VK_RIGHT ||
                keyEvent->mKeyCode == NS_VK_UP ||
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -995,17 +995,17 @@ class HTMLInputElement final : public Te
 
   void RadioSetChecked(bool aNotify);
   void SetCheckedChanged(bool aCheckedChanged);
 
   /**
    * MaybeSubmitForm looks for a submit input or a single text control
    * and submits the form if either is present.
    */
-  MOZ_CAN_RUN_SCRIPT nsresult MaybeSubmitForm(nsPresContext* aPresContext);
+  MOZ_CAN_RUN_SCRIPT void MaybeSubmitForm(nsPresContext* aPresContext);
 
   /**
    * Update mFileList with the currently selected file.
    */
   void UpdateFileList();
 
   /**
    * Called after calling one of the SetFilesOrDirectories() functions.
--- a/dom/media/wave/WaveDemuxer.cpp
+++ b/dom/media/wave/WaveDemuxer.cpp
@@ -524,17 +524,18 @@ void WAVTrackDemuxer::UpdateState(const 
   mOffset = aRange.mEnd;
   mTotalChunkLen += aRange.Length();
 }
 
 int32_t WAVTrackDemuxer::Read(uint8_t* aBuffer, int64_t aOffset,
                               int32_t aSize) {
   const int64_t streamLen = StreamLength();
   if (mInfo && streamLen > 0) {
-    aSize = std::min<int64_t>(aSize, streamLen - aOffset);
+    int64_t max = streamLen > aOffset ? streamLen - aOffset : 0;
+    aSize = std::min<int64_t>(aSize, max);
   }
   uint32_t read = 0;
   const nsresult rv = mSource.ReadAt(aOffset, reinterpret_cast<char*>(aBuffer),
                                      static_cast<uint32_t>(aSize), &read);
   NS_ENSURE_SUCCESS(rv, 0);
   return static_cast<int32_t>(read);
 }
 
--- a/dom/midi/MIDIInput.cpp
+++ b/dom/midi/MIDIInput.cpp
@@ -32,17 +32,21 @@ MIDIInput* MIDIInput::Create(nsPIDOMWind
 }
 
 JSObject* MIDIInput::WrapObject(JSContext* aCx,
                                 JS::Handle<JSObject*> aGivenProto) {
   return MIDIInput_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void MIDIInput::Receive(const nsTArray<MIDIMessage>& aMsgs) {
-  nsCOMPtr<Document> doc = GetOwner() ? GetOwner()->GetDoc() : nullptr;
+  if (!GetOwner()) {
+    return;  // Ignore messages once we've been disconnected from the owner
+  }
+
+  nsCOMPtr<Document> doc = GetOwner()->GetDoc();
   if (!doc) {
     NS_WARNING("No document available to send MIDIMessageEvent to!");
     return;
   }
   for (const auto& msg : aMsgs) {
     RefPtr<MIDIMessageEvent> event(
         MIDIMessageEvent::Constructor(this, msg.timestamp(), msg.data()));
     DispatchTrustedEvent(event);
--- a/dom/midi/MIDIPort.cpp
+++ b/dom/midi/MIDIPort.cpp
@@ -158,16 +158,20 @@ already_AddRefed<Promise> MIDIPort::Clos
 
 void MIDIPort::Notify(const void_t& aVoid) {
   // If we're getting notified, it means the MIDIAccess parent object is dead.
   // Nullify our copy.
   mMIDIAccessParent = nullptr;
 }
 
 void MIDIPort::FireStateChangeEvent() {
+  if (!GetOwner()) {
+    return;  // Ignore changes once we've been disconnected from the owner
+  }
+
   StateChange();
 
   MOZ_ASSERT(mPort);
   if (mPort->ConnectionState() == MIDIPortConnectionState::Open ||
       mPort->ConnectionState() == MIDIPortConnectionState::Pending) {
     if (mOpeningPromise) {
       mOpeningPromise->MaybeResolve(this);
       mOpeningPromise = nullptr;
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -359,18 +359,22 @@ class ReleaseNotificationRunnable final 
       : NotificationWorkerRunnable(aNotification->mWorkerPrivate),
         mNotification(aNotification) {}
 
   void WorkerRunInternal(WorkerPrivate* aWorkerPrivate) override {
     mNotification->ReleaseObject();
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is called twice
+    nsresult rv = NotificationWorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     mNotification->ReleaseObject();
-    return NotificationWorkerRunnable::Cancel();
+    return NS_OK;
   }
 };
 
 // Create one whenever you require ownership of the notification. Use with
 // UniquePtr<>. See Notification.h for details.
 class NotificationRef final {
   friend class WorkerNotificationObserver;
 
--- a/dom/serviceworkers/ServiceWorkerOp.cpp
+++ b/dom/serviceworkers/ServiceWorkerOp.cpp
@@ -295,22 +295,26 @@ class ServiceWorkerOp::ServiceWorkerOpRu
     bool rv = mOwner->Exec(aCx, aWorkerPrivate);
     Unused << NS_WARN_IF(!rv);
     mOwner = nullptr;
 
     return rv;
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is permitted
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     MOZ_ASSERT(mOwner);
 
     mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
     mOwner = nullptr;
 
-    return WorkerRunnable::Cancel();
+    return NS_OK;
   }
 
   RefPtr<ServiceWorkerOp> mOwner;
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable,
                              WorkerRunnable)
 
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -179,18 +179,22 @@ class CheckScriptEvaluationWithCallback 
   }
 
   void ReportFetchFlag(bool aFetchHandlerWasAdded) {
     MOZ_ASSERT(NS_IsMainThread());
     mServiceWorkerPrivate->SetHandlesFetch(aFetchHandlerWasAdded);
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is permitted
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     ReportScriptEvaluationResult(false);
-    return WorkerRunnable::Cancel();
+    return NS_OK;
   }
 
  private:
   void ReportScriptEvaluationResult(bool aScriptEvaluationResult) {
 #ifdef DEBUG
     mDone = true;
 #endif
     mScriptEvaluationCallback->SetResult(aScriptEvaluationResult);
@@ -597,20 +601,24 @@ class LifecycleEventWorkerRunnable : pub
   }
 
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
     MOZ_ASSERT(aWorkerPrivate);
     return DispatchLifecycleEvent(aCx, aWorkerPrivate);
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is permitted
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     mCallback->SetResult(false);
     MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(mCallback));
 
-    return WorkerRunnable::Cancel();
+    return NS_OK;
   }
 
  private:
   bool DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 };
 
 /*
  * Used to handle ExtendableEvent::waitUntil() and catch abnormal worker
@@ -1339,21 +1347,24 @@ class FetchEventRunnable : public Extend
 
   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
     MOZ_ASSERT(aWorkerPrivate);
 
     return DispatchFetchEvent(aCx, aWorkerPrivate);
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is permitted
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     nsCOMPtr<nsIRunnable> runnable = new ResumeRequest(mInterceptedChannel);
     if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable))) {
       NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n");
     }
-    WorkerRunnable::Cancel();
     return NS_OK;
   }
 
  private:
   ~FetchEventRunnable() = default;
 
   class ResumeRequest final : public Runnable {
     nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
--- a/dom/webgpu/Instance.cpp
+++ b/dom/webgpu/Instance.cpp
@@ -67,18 +67,17 @@ already_AddRefed<dom::Promise> Instance:
         MOZ_ASSERT(info.id != 0);
         RefPtr<Adapter> adapter = new Adapter(instance, info);
         promise->MaybeResolve(adapter);
       },
       [promise](const Maybe<ipc::ResponseRejectReason>& aResponseReason) {
         if (aResponseReason.isSome()) {
           promise->MaybeRejectWithAbortError("Internal communication error!");
         } else {
-          promise->MaybeRejectWithInvalidStateError(
-              "No matching adapter found!");
+          promise->MaybeResolve(JS::NullHandleValue);
         }
       });
 
   return promise.forget();
 }
 
 }  // namespace webgpu
 }  // namespace mozilla
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -597,26 +597,30 @@ class JSDispatchableRunnable final : pub
     mDispatchable->run(mWorkerPrivate->GetJSContext(),
                        JS::Dispatchable::NotShuttingDown);
     mDispatchable = nullptr;  // mDispatchable may delete itself
 
     return true;
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is called twice
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     MOZ_ASSERT(mDispatchable);
 
     AutoJSAPI jsapi;
     jsapi.Init();
 
     mDispatchable->run(mWorkerPrivate->GetJSContext(),
                        JS::Dispatchable::ShuttingDown);
     mDispatchable = nullptr;  // mDispatchable may delete itself
 
-    return WorkerRunnable::Cancel();
+    return NS_OK;
   }
 };
 
 static bool DispatchToEventLoop(void* aClosure,
                                 JS::Dispatchable* aDispatchable) {
   // This callback may execute either on the worker thread or a random
   // JS-internal helper thread.
 
@@ -1539,16 +1543,20 @@ class CrashIfHangingRunnable : public Wo
       aWorkerPrivate->DumpCrashInformation(mMsg);
       mHasMsg.Flip();
     }
     lock.Notify();
     return true;
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is called twice
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     MonitorAutoLock lock(mMonitor);
     if (!mHasMsg) {
       mMsg.Assign("Canceled");
       mHasMsg.Flip();
     }
     lock.Notify();
 
     return NS_OK;
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -2218,21 +2218,25 @@ void ScriptExecutorRunnable::PostRun(JSC
     // script and GetOrCreateGlobalScope() fails.  In that case we would have
     // returned false from WorkerRun, so assert that.
     MOZ_ASSERT_IF(!result && !mScriptLoader.mRv.Failed(), !aRunResult);
     ShutdownScriptLoader(aCx, aWorkerPrivate, result, mutedError);
   }
 }
 
 nsresult ScriptExecutorRunnable::Cancel() {
+  // We need to check first if cancel is called twice
+  nsresult rv = MainThreadWorkerSyncRunnable::Cancel();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (AllScriptsExecutable()) {
     ShutdownScriptLoader(mWorkerPrivate->GetJSContext(), mWorkerPrivate, false,
                          false);
   }
-  return MainThreadWorkerSyncRunnable::Cancel();
+  return NS_OK;
 }
 
 void ScriptExecutorRunnable::ShutdownScriptLoader(JSContext* aCx,
                                                   WorkerPrivate* aWorkerPrivate,
                                                   bool aResult,
                                                   bool aMutedError) {
   aWorkerPrivate->AssertIsOnWorkerThread();
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -203,21 +203,25 @@ class ExternalRunnableWrapper final : pu
         Throw(aCx, rv);
       }
       return false;
     }
     return true;
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is called twice
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     nsCOMPtr<nsIDiscardableRunnable> doomed =
         do_QueryInterface(mWrappedRunnable);
     MOZ_ASSERT(doomed);  // We checked this earlier!
     doomed->OnDiscard();
-    return WorkerRunnable::Cancel();
+    return NS_OK;
   }
 };
 
 struct WindowAction {
   nsPIDOMWindowInner* mWindow;
   bool mDefaultAction;
 
   MOZ_IMPLICIT WindowAction(nsPIDOMWindowInner* aWindow)
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -462,23 +462,24 @@ MainThreadStopSyncLoopRunnable::MainThre
       mResult(aResult) {
   AssertIsOnMainThread();
 #ifdef DEBUG
   mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
 #endif
 }
 
 nsresult MainThreadStopSyncLoopRunnable::Cancel() {
-  nsresult rv = Run();
+  // We need to check first if cancel is called twice
+  nsresult rv = WorkerSyncRunnable::Cancel();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = Run();
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Run() failed");
 
-  nsresult rv2 = WorkerSyncRunnable::Cancel();
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv2), "Cancel() failed");
-
-  return NS_FAILED(rv) ? rv : rv2;
+  return rv;
 }
 
 bool MainThreadStopSyncLoopRunnable::WorkerRun(JSContext* aCx,
                                                WorkerPrivate* aWorkerPrivate) {
   aWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(mSyncLoopTarget);
 
   nsCOMPtr<nsIEventTarget> syncLoopTarget;
@@ -506,21 +507,25 @@ WorkerControlRunnable::WorkerControlRunn
   MOZ_ASSERT(aWorkerPrivate);
   MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
                  aBehavior == WorkerThreadUnchangedBusyCount,
              "WorkerControlRunnables should not modify the busy count");
 }
 #endif
 
 nsresult WorkerControlRunnable::Cancel() {
+  // We need to check first if cancel is called twice
+  nsresult rv = WorkerRunnable::Cancel();
+  NS_ENSURE_SUCCESS(rv, rv);
+
   if (NS_FAILED(Run())) {
     NS_WARNING("WorkerControlRunnable::Run() failed.");
   }
 
-  return WorkerRunnable::Cancel();
+  return NS_OK;
 }
 
 bool WorkerControlRunnable::DispatchInternal() {
   RefPtr<WorkerControlRunnable> runnable(this);
 
   if (mBehavior == WorkerThreadUnchangedBusyCount) {
     return NS_SUCCEEDED(
         mWorkerPrivate->DispatchControlRunnable(runnable.forget()));
--- a/dom/workers/WorkerRunnable.h
+++ b/dom/workers/WorkerRunnable.h
@@ -73,23 +73,28 @@ class WorkerRunnable : public nsIRunnabl
   bool mCallingCancelWithinRun;
 
  public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
   // If you override Cancel() then you'll need to either call the base class
   // Cancel() method or override IsCanceled() so that the Run() method bails out
   // appropriately.
+  // Cancel() should not be called more than once and we throw
+  // NS_ERROR_UNEXPECTED if it is. If you override it, ensure to call the base
+  // class method first and bail out on failure to avoid unexpected side
+  // effects.
   nsresult Cancel() override;
 
   // The return value is true if and only if both PreDispatch and
   // DispatchInternal return true.
   bool Dispatch();
 
   // See above note about Cancel().
+  // TODO: Check if we can remove the possibility to override IsCanceled.
   virtual bool IsCanceled() const { return mCanceled != 0; }
 
   // True if this runnable is handled by running JavaScript in some global that
   // could possibly be a debuggee, and thus needs to be deferred when the target
   // is paused in the debugger, until the JavaScript invocation in progress has
   // run to completion. Examples are MessageEventRunnable and
   // ReportErrorRunnable. These runnables are segregated into separate
   // ThrottledEventQueues, which the debugger pauses.
--- a/dom/workers/remoteworkers/RemoteWorkerChild.cpp
+++ b/dom/workers/remoteworkers/RemoteWorkerChild.cpp
@@ -176,16 +176,20 @@ class ReleaseWorkerRunnable final : publ
   ~ReleaseWorkerRunnable() { ReleaseMembers(); }
 
   bool WorkerRun(JSContext*, WorkerPrivate*) override {
     ReleaseMembers();
     return true;
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is called twice
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     ReleaseMembers();
     return NS_OK;
   }
 
   void ReleaseMembers() {
     if (!mWorkerPrivate) {
       MOZ_ASSERT(!mWeakRef);
       return;
@@ -221,19 +225,23 @@ class RemoteWorkerChild::InitializeWorke
 
     SelfHolder holder = std::move(mActor);
     MOZ_ASSERT(!mActor);
 
     return true;
   }
 
   nsresult Cancel() override {
+    // We need to check first if cancel is called twice
+    nsresult rv = WorkerRunnable::Cancel();
+    NS_ENSURE_SUCCESS(rv, rv);
+
     MaybeAbort();
 
-    return WorkerRunnable::Cancel();
+    return NS_OK;
   }
 
   // Slowly running out of synonyms for cancel, abort, terminate, etc...
   void MaybeAbort() {
     if (!mActor) {
       return;
     }
 
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -401,20 +401,22 @@ class LoadStartDetectionRunnable final :
       if (mXMLHttpRequestPrivate->SendInProgress()) {
         mXMLHttpRequestPrivate->Unpin();
       }
 
       return true;
     }
 
     nsresult Cancel() override {
-      // This must run!
+      // We need to check first if cancel is called twice
       nsresult rv = MainThreadProxyRunnable::Cancel();
-      nsresult rv2 = Run();
-      return NS_FAILED(rv) ? rv : rv2;
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // On the first cancel, this must run!
+      return Run();
     }
   };
 
  public:
   LoadStartDetectionRunnable(Proxy* aProxy, XMLHttpRequestWorker* aXHRPrivate)
       : Runnable("dom::LoadStartDetectionRunnable"),
         mWorkerPrivate(aProxy->mWorkerPrivate),
         mProxy(aProxy),
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -576,34 +576,46 @@ void gfxFontShaper::MergeFontFeatures(
 
 void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
                                            const char16_t* aString,
                                            uint32_t aLength) {
   CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
 
   CompressedGlyph extendCluster = CompressedGlyph::MakeComplex(false, true);
 
+  const char16_t* const stringStart = aString;
   ClusterIterator iter(aString, aLength);
 
   // the ClusterIterator won't be able to tell us if the string
   // _begins_ with a cluster-extender, so we handle that here
   if (aLength) {
     uint32_t ch = *aString;
     if (aLength > 1 && NS_IS_SURROGATE_PAIR(ch, aString[1])) {
       ch = SURROGATE_TO_UCS4(ch, aString[1]);
     }
     if (IsClusterExtender(ch)) {
       *glyphs = extendCluster;
     }
   }
 
   const char16_t kIdeographicSpace = 0x3000;
+  // Special case for Bengali: although Virama normally clusters with the
+  // preceding letter, we *also* want to cluster it with a following Ya
+  // so that when the Virama+Ya form ya-phala, this is not separated from the
+  // preceding letter by any letter-spacing or justification.
+  const char16_t kBengaliVirama = 0x09CD;
+  const char16_t kBengaliYa = 0x09AF;
   while (!iter.AtEnd()) {
     if (*iter == char16_t(' ') || *iter == kIdeographicSpace) {
       glyphs->SetIsSpace();
+    } else if (*iter == kBengaliYa) {
+      // Unless we're at the start, check for a preceding virama.
+      if (aString > stringStart && *(aString - 1) == kBengaliVirama) {
+        *glyphs = extendCluster;
+      }
     }
     // advance iter to the next cluster-start (or end of text)
     iter.Next();
     // step past the first char of the cluster
     aString++;
     glyphs++;
     // mark all the rest as cluster-continuations
     while (aString < iter) {
--- a/gfx/wgpu_bindings/Cargo.toml
+++ b/gfx/wgpu_bindings/Cargo.toml
@@ -12,28 +12,28 @@ publish = false
 [lib]
 
 [features]
 default = []
 
 [dependencies.wgc]
 package = "wgpu-core"
 git = "https://github.com/gfx-rs/wgpu"
-rev = "1e593a6"
+rev = "6bc896f"
 #Note: "replay" shouldn't ideally be needed,
 # but it allows us to serialize everything across IPC.
 features = ["replay", "trace", "serial-pass"]
 
 [dependencies.wgt]
 package = "wgpu-types"
 git = "https://github.com/gfx-rs/wgpu"
-rev = "1e593a6"
+rev = "6bc896f"
 
 [dependencies.wgh]
 package = "wgpu-hal"
 git = "https://github.com/gfx-rs/wgpu"
-rev = "1e593a6"
+rev = "6bc896f"
 
 [dependencies]
 bincode = "1"
 log = "0.4"
 parking_lot = "0.11"
 serde = "1"
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -85,17 +85,17 @@ void GetCurrentScreenConfiguration(Scree
 RefPtr<mozilla::MozPromise<bool, bool, false>> LockScreenOrientation(
     const hal::ScreenOrientation& aOrientation) {
   return Hal()
       ->SendLockScreenOrientation(aOrientation)
       ->Then(
           GetCurrentSerialEventTarget(), __func__,
           [=](const mozilla::MozPromise<bool, ipc::ResponseRejectReason,
                                         true>::ResolveOrRejectValue& aValue) {
-            if (aValue.IsResolve() && aValue.ResolveValue()) {
+            if (aValue.IsResolve()) {
               return mozilla::MozPromise<bool, bool, false>::CreateAndResolve(
                   true, __func__);
             }
             return mozilla::MozPromise<bool, bool, false>::CreateAndReject(
                 false, __func__);
           });
 }
 
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xhtml
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xhtml
@@ -226,17 +226,21 @@ https://bugzilla.mozilla.org/show_bug.cg
                       "preventExtensions", "freeze", "fromEntries", "isFrozen", "seal",
                       "isSealed", "assign", "getPrototypeOf", "values",
                       "entries", "isExtensible", "hasOwn"]);
   gPrototypeProperties['Array'] =
     ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
       "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
       "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
       "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys",
-      "values", "constructor", "flat", "flatMap", "at", "groupBy", "groupByToMap"];
+      "values", "constructor", "flat", "flatMap", "at"];
+  if (isNightlyBuild) {
+    gPrototypeProperties['Array'].push("groupBy");
+    gPrototypeProperties['Array'].push("groupByToMap");
+  }
   gConstructorProperties['Array'] =
     constructorProps(["isArray", "from", "of", Symbol.species]);
   for (var c of typedArrayClasses) {
     gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"];
     gConstructorProperties[c] = constructorProps(["BYTES_PER_ELEMENT"]);
   }
   gPrototypeProperties['TypedArray'] =
     ["length", "buffer", "byteLength", "byteOffset", Symbol.iterator, "subarray",
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1237,17 +1237,16 @@ void PresShell::Destroy() {
       fs->GetLoadStatistics(fontCount, fontSize);
       Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, fontCount);
       Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE,
                             uint32_t(fontSize / 1024));
     } else {
       Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, 0);
       Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, 0);
     }
-    mPresContext->CancelManagedPostRefreshObservers();
   }
 
 #ifdef MOZ_REFLOW_PERF
   DumpReflows();
   mReflowCountMgr = nullptr;
 #endif
 
   if (mZoomConstraintsClient) {
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1000,16 +1000,18 @@ void nsPresContext::DetachPresShell() {
   // this way to preserve the old ordering, but I doubt anything would break.
   if (mCounterStyleManager) {
     mCounterStyleManager->Disconnect();
     mCounterStyleManager = nullptr;
   }
 
   mPresShell = nullptr;
 
+  CancelManagedPostRefreshObservers();
+
   if (mAnimationEventDispatcher) {
     mAnimationEventDispatcher->Disconnect();
     mAnimationEventDispatcher = nullptr;
   }
   if (mEffectCompositor) {
     mEffectCompositor->Disconnect();
     mEffectCompositor = nullptr;
   }
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -544,19 +544,20 @@ class nsPresContext : public nsISupports
    */
   void SetSafeAreaInsets(const mozilla::ScreenIntMargin& aInsets);
 
   mozilla::ScreenIntMargin GetSafeAreaInsets() const { return mSafeAreaInsets; }
 
   void RegisterManagedPostRefreshObserver(mozilla::ManagedPostRefreshObserver*);
   void UnregisterManagedPostRefreshObserver(
       mozilla::ManagedPostRefreshObserver*);
+
+ protected:
   void CancelManagedPostRefreshObservers();
 
- protected:
   void UpdateEffectiveTextZoom();
 
 #ifdef DEBUG
   void ValidatePresShellAndDocumentReleation() const;
 #endif  // #ifdef DEBUG
 
   void SetTextZoom(float aZoom) {
     MOZ_ASSERT(aZoom > 0.0f, "invalid zoom factor");
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1809,16 +1809,17 @@ void nsRefreshDriver::AppendTickReasonsT
 }
 
 bool nsRefreshDriver::
     ShouldKeepTimerRunningWhileWaitingForFirstContentfulPaint() {
   // On top level content pages keep the timer running initially so that we
   // paint the page soon enough.
   if (mThrottled || mTestControllingRefreshes || !XRE_IsContentProcess() ||
       !mPresContext->Document()->IsTopLevelContentDocument() ||
+      mPresContext->Document()->IsInitialDocument() ||
       gfxPlatform::IsInLayoutAsapMode() || mPresContext->HadContentfulPaint() ||
       mPresContext->Document()->GetReadyStateEnum() ==
           Document::READYSTATE_COMPLETE) {
     return false;
   }
   if (mBeforeFirstContentfulPaintTimerRunningLimit.IsNull()) {
     // Don't let the timer to run forever, so limit to 4s for now.
     mBeforeFirstContentfulPaintTimerRunningLimit =
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -387,16 +387,20 @@ android.libraryVariants.all { variant ->
 apply plugin: 'maven-publish'
 
 version = getVersionNumber()
 group = 'org.mozilla.geckoview'
 
 def getArtifactId() {
     def id = "geckoview" + project.ext.artifactSuffix
 
+    if (!mozconfig.substs.MOZ_ANDROID_GECKOVIEW_LITE) {
+        id += "-omni"
+    }
+
     if (mozconfig.substs.MOZILLA_OFFICIAL && !mozconfig.substs.MOZ_ANDROID_FAT_AAR_ARCHITECTURES) {
         // In automation, per-architecture artifacts identify
         // the architecture; multi-architecture artifacts don't.
         // When building locally, we produce a "skinny AAR" with
         // one target architecture masquerading as a "fat AAR"
         // to enable Gradle composite builds to substitute this
         // project into consumers easily.
         id += "-${mozconfig.substs.ANDROID_CPU_ARCH}"
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/OrientationDelegateTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/OrientationDelegateTest.kt
@@ -42,56 +42,52 @@ class OrientationDelegateTest : BaseSess
                 assertThat("Div went fullscreen", fullScreen, equalTo(true))
             }
         })
         promise.value
     }
 
     private fun lockPortrait() {
         val promise = mainSession.evaluatePromiseJS("screen.orientation.lock('portrait-primary')")
-        sessionRule.delegateDuringNextWait(object : OrientationController.OrientationDelegate {
+        sessionRule.waitUntilCalled(object : OrientationController.OrientationDelegate {
             @AssertCalled(count = 1)
             override fun onOrientationLock(aOrientation: Int): GeckoResult<AllowOrDeny> {
                 assertThat(
                     "The orientation should be portrait",
                     aOrientation,
                     equalTo(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
                 )
                 activityRule.scenario.onActivity { activity ->
                     activity.requestedOrientation = aOrientation
                 }
                 return GeckoResult.allow()
             }
         })
         sessionRule.runtime.orientationChanged(Configuration.ORIENTATION_PORTRAIT)
         promise.value
-        // Remove previous delegate
-        mainSession.waitForRoundTrip()
     }
 
     private fun lockLandscape() {
         val promise = mainSession.evaluatePromiseJS("screen.orientation.lock('landscape-primary')")
-        sessionRule.delegateDuringNextWait(object : OrientationController.OrientationDelegate {
+        sessionRule.waitUntilCalled(object : OrientationController.OrientationDelegate {
             @AssertCalled(count = 1)
             override fun onOrientationLock(aOrientation: Int): GeckoResult<AllowOrDeny> {
                 assertThat(
                     "The orientation should be landscape",
                     aOrientation,
                     equalTo(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
                 )
                 activityRule.scenario.onActivity { activity ->
                     activity.requestedOrientation = aOrientation
                 }
                 return GeckoResult.allow()
             }
         })
         sessionRule.runtime.orientationChanged(Configuration.ORIENTATION_LANDSCAPE)
         promise.value
-        // Remove previous delegate
-        mainSession.waitForRoundTrip()
     }
 
     @Test fun orientationLock() {
         sessionRule.setPrefsUntilTestEnd(mapOf("dom.screenorientation.allow-lock" to true))
         goFullscreen()
         activityRule.scenario.onActivity { activity ->
             // If the orientation is landscape, lock to portrait and wait for delegate. If portrait, lock to landscape instead.
             if (activity.resources.configuration.orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){
@@ -143,34 +139,32 @@ class OrientationDelegateTest : BaseSess
         mainSession.evaluateJS("screen.orientation.lock('landscape-primary')")
     }
 
     @Test fun orientationLockUnlock() {
         sessionRule.setPrefsUntilTestEnd(mapOf("dom.screenorientation.allow-lock" to true))
         goFullscreen()
 
         val promise = mainSession.evaluatePromiseJS("screen.orientation.lock('landscape-primary')")
-        sessionRule.delegateDuringNextWait(object : OrientationController.OrientationDelegate {
+        sessionRule.waitUntilCalled(object : OrientationController.OrientationDelegate {
             @AssertCalled(count = 1)
             override fun onOrientationLock(aOrientation: Int): GeckoResult<AllowOrDeny> {
                 assertThat(
                     "The orientation value is as expected",
                     aOrientation,
                     equalTo(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
                 )
                 activityRule.scenario.onActivity { activity ->
                     activity.requestedOrientation = aOrientation
                 }
                 return GeckoResult.allow()
             }
         })
         sessionRule.runtime.orientationChanged(Configuration.ORIENTATION_LANDSCAPE)
         promise.value
-        // Remove previous delegate
-        mainSession.waitForRoundTrip()
 
         // after locking to orientation landscape, unlock to default
         mainSession.evaluateJS("screen.orientation.unlock()")
         sessionRule.waitUntilCalled(object : OrientationController.OrientationDelegate {
             @AssertCalled(count = 1)
             override fun onOrientationUnlock() {
                 activityRule.scenario.onActivity { activity ->
                     activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
--- a/mobile/android/gradle/with_gecko_binaries.gradle
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -33,16 +33,21 @@ def getNDKDirectory() {
     def files = new FileNameFinder().getFileNames(mozbuild, "android-ndk-*/source.properties")
     if (files) {
         // It would be nice to sort these by version, but that's too much effort right now.
         return project.file(files.first()).parentFile.toString()
     }
     return null
 }
 
+def hasCompileArtifacts() {
+    return project.mozconfig.substs.COMPILE_ENVIRONMENT
+        || project.mozconfig.substs.MOZ_ARTIFACT_BUILDS
+}
+
 // Get the LLVM bin folder, either from the currently used toolchain or, if
 // this is an artifact build, from the Android NDK.
 @Memoized
 def getLlvmBinFolder() {
     // If we have a clang path, just use that
     if (project.mozconfig.substs.MOZ_CLANG_PATH) {
         return project.file(project.mozconfig.substs.MOZ_CLANG_PATH)
             .parentFile
@@ -188,17 +193,17 @@ class SyncLibsAndUpdateGenerationID exte
     }
 }
 
 ext.configureVariantWithGeckoBinaries = { variant ->
     def omnijarDir = "${topobjdir}/dist/geckoview"
     def distDir = "${topobjdir}/dist/geckoview"
 
     def syncOmnijarFromDistDir
-    if (mozconfig.substs.COMPILE_ENVIRONMENT) {
+    if (hasCompileArtifacts()) {
         syncOmnijarFromDistDir = task("syncOmnijarFromDistDirFor${variant.name.capitalize()}", type: Sync) {
             onlyIf {
                 if (source.empty) {
                     throw new StopExecutionException("Required omnijar not found in ${omnijarDir}/{omni.ja,assets/omni.ja}.  Have you built and packaged?")
                 }
                 return true
             }
 
@@ -244,17 +249,17 @@ ext.configureVariantWithGeckoBinaries = 
     } else {
         task("syncLibsFromDistDirFor${variant.name.capitalize()}", type: Sync) {
             into("${project.buildDir}/moz.build/src/${variant.name}/jniLibs")
             from("${distDir}/lib")
         }
     } }()
 
     syncLibsFromDistDir.onlyIf { task ->
-        if (!mozconfig.substs.COMPILE_ENVIRONMENT && !mozconfig.substs.MOZ_ARTIFACT_BUILDS) {
+        if (!hasCompileArtifacts()) {
             // We won't have JNI libraries if we're not compiling and we're not downloading
             // artifacts.  Such a configuration is used for running lints, generating docs, etc.
             return true
         }
         if (files(task.source).empty) {
             throw new StopExecutionException("Required JNI libraries not found in ${task.source}.  Have you built and packaged?")
         }
         return true
@@ -262,17 +267,17 @@ ext.configureVariantWithGeckoBinaries = 
 
     def syncAssetsFromDistDir = task("syncAssetsFromDistDirFor${variant.name.capitalize()}", type: Sync) {
         into("${project.buildDir}/moz.build/src/${variant.name}/assets")
         from("${distDir}/assets") {
             exclude 'omni.ja'
         }
     }
 
-    if (mozconfig.substs.COMPILE_ENVIRONMENT) {
+    if (hasCompileArtifacts()) {
         // Local (read, not 'official') builds want to reflect developer changes to
         // the omnijar sources, and (when compiling) to reflect developer changes to
         // the native binaries.  To do this, the Gradle build calls out to the
         // moz.build system, which can be re-entrant.  Official builds are driven by
         // the moz.build system and should never be re-entrant in this way.
         if (!mozconfig.substs.MOZILLA_OFFICIAL) {
             syncOmnijarFromDistDir.dependsOn rootProject.machStagePackage
             syncLibsFromDistDir.dependsOn rootProject.machStagePackage
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -412,17 +412,19 @@ def android_geckoview_docs(
     # Extract new javadoc to specified directory inside repo.
     src_tar = mozpath.join(
         command_context.topobjdir,
         "gradle",
         "build",
         "mobile",
         "android",
         "geckoview",
-        "libs",
+        "docs",
+        "javadoc",
+        "withGeckoBinaries-debug",
         "geckoview-javadoc.jar",
     )
     dst_path = mozpath.join(repo_path, javadoc_path.format(**fmt))
     mozfile.remove(dst_path)
     mozfile.extract_zip(src_tar, dst_path)
 
     # Commit and push.
     command_context.run_process(["git", "add", "--all"], append_env=env, pass_thru=True)
--- a/mobile/android/modules/geckoview/GeckoViewStorageController.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewStorageController.jsm
@@ -43,18 +43,17 @@ const ClearFlags = [
     // HISTORY
     1 << 3,
     Ci.nsIClearDataService.CLEAR_HISTORY |
       Ci.nsIClearDataService.CLEAR_SESSION_HISTORY,
   ],
   [
     // DOM_STORAGES
     1 << 4,
-    Ci.nsIClearDataService.CLEAR_APPCACHE |
-      Ci.nsIClearDataService.CLEAR_DOM_QUOTA |
+    Ci.nsIClearDataService.CLEAR_DOM_QUOTA |
       Ci.nsIClearDataService.CLEAR_DOM_PUSH_NOTIFICATIONS |
       Ci.nsIClearDataService.CLEAR_REPORTS,
   ],
   [
     // AUTH_SESSIONS
     1 << 5,
     Ci.nsIClearDataService.CLEAR_AUTH_TOKENS |
       Ci.nsIClearDataService.CLEAR_AUTH_CACHE,
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -3969,17 +3969,17 @@
 - name: dom.webcomponents.elementInternals.enabled
   type: bool
   value: true
   mirror: always
 
 # Is support for form-associated custom element enabled?
 - name: dom.webcomponents.formAssociatedCustomElement.enabled
   type: bool
-  value: @IS_NIGHTLY_BUILD@
+  value: true
   mirror: always
 
 # Is support for the Web GPU API enabled?
 - name: dom.webgpu.enabled
   type: RelaxedAtomicBool
   value: false
   mirror: always
 
--- a/mozglue/baseprofiler/core/platform.cpp
+++ b/mozglue/baseprofiler/core/platform.cpp
@@ -1980,17 +1980,17 @@ static char FeatureCategory(uint32_t aFe
   }
 
   if (aFeature & AvailableFeatures()) {
     return '-';
   }
   return 'x';
 }
 
-static void PrintUsageThenExit(int aExitCode) {
+static void PrintUsage() {
   PrintToConsole(
       "\n"
       "Profiler environment variable usage:\n"
       "\n"
       "  MOZ_BASE_PROFILER_HELP\n"
       "  If set to any value, prints this message.\n"
       "  (Only BaseProfiler features are known here; Use MOZ_PROFILER_HELP\n"
       "  for Gecko Profiler help, with more features).\n"
@@ -2083,18 +2083,16 @@ static void PrintUsageThenExit(int aExit
       "  This platform %s native unwinding.\n"
       "\n",
 #if defined(HAVE_NATIVE_UNWIND)
       "supports"
 #else
       "does not support"
 #endif
   );
-
-  exit(aExitCode);
 }
 
 ////////////////////////////////////////////////////////////////////////
 // BEGIN Sampler
 
 #if defined(GP_OS_linux) || defined(GP_OS_android)
 struct SigHandlerCoordinator;
 #endif
@@ -2456,18 +2454,18 @@ static uint32_t ParseFeature(const char*
     return ProfilerFeature::Name_;                \
   }
 
   BASE_PROFILER_FOR_EACH_FEATURE(PARSE_FEATURE_BIT)
 
 #undef PARSE_FEATURE_BIT
 
   PrintToConsole("\nUnrecognized feature \"%s\".\n\n", aFeature);
-  // Since we may have an old feature we don't implement anymore, don't exit
-  PrintUsageThenExit(0);
+  // Since we may have an old feature we don't implement anymore, don't exit.
+  PrintUsage();
   return 0;
 }
 
 uint32_t ParseFeaturesFromStringArray(const char** aFeatures,
                                       uint32_t aFeatureCount,
                                       bool aIsStartup /* = false */) {
   uint32_t features = 0;
   for (size_t i = 0; i < aFeatureCount; i++) {
@@ -2571,17 +2569,18 @@ void profiler_init(void* aStackTop) {
 
   profiler_init_main_thread_id();
 
   VTUNE_INIT();
 
   MOZ_RELEASE_ASSERT(!CorePS::Exists());
 
   if (getenv("MOZ_BASE_PROFILER_HELP")) {
-    PrintUsageThenExit(0);  // terminates execution
+    PrintUsage();
+    exit(0);
   }
 
   SharedLibraryInfo::Initialize();
 
   uint32_t features = DefaultFeatures() & AvailableFeatures();
 
   UniquePtr<char[]> filterStorage;
 
@@ -2647,32 +2646,34 @@ void profiler_init(void* aStackTop) {
       } else if (sizeSuffix == "GB") {
         capacityLong *= (1000 * 1000 * 1000) / scBytesPerEntry;
       } else if (sizeSuffix == "GiB") {
         capacityLong *= (1024 * 1024 * 1024) / scBytesPerEntry;
       } else if (!sizeSuffix.empty()) {
         PrintToConsole(
             "- MOZ_PROFILER_STARTUP_ENTRIES unit must be one of the "
             "following: KB, KiB, MB, MiB, GB, GiB");
-        PrintUsageThenExit(1);
+        PrintUsage();
+        exit(1);
       }
 
       // `long` could be 32 or 64 bits, so we force a 64-bit comparison with
       // the maximum 32-bit signed number (as more than that is clamped down to
       // 2^31 anyway).
       if (errno == 0 && capacityLong > 0 &&
           static_cast<uint64_t>(capacityLong) <=
               static_cast<uint64_t>(INT32_MAX)) {
         capacity = PowerOfTwo32(ActivePS::ClampToAllowedEntries(
             static_cast<uint32_t>(capacityLong)));
         LOG("- MOZ_PROFILER_STARTUP_ENTRIES = %u", unsigned(capacity.Value()));
       } else {
         PrintToConsole("- MOZ_PROFILER_STARTUP_ENTRIES not a valid integer: %s",
                        startupCapacity);
-        PrintUsageThenExit(1);
+        PrintUsage();
+        exit(1);
       }
     }
 
     const char* startupDuration = getenv("MOZ_PROFILER_STARTUP_DURATION");
     if (startupDuration && startupDuration[0] != '\0') {
       // The duration is a floating point number. Use StringToDouble rather than
       // strtod, so that "." is used as the decimal separator regardless of OS
       // locale.
@@ -2680,50 +2681,53 @@ void profiler_init(void* aStackTop) {
       if (durationVal && *durationVal >= 0.0) {
         if (*durationVal > 0.0) {
           duration = Some(*durationVal);
         }
         LOG("- MOZ_PROFILER_STARTUP_DURATION = %f", *durationVal);
       } else {
         PrintToConsole("- MOZ_PROFILER_STARTUP_DURATION not a valid float: %s",
                        startupDuration);
-        PrintUsageThenExit(1);
+        PrintUsage();
+        exit(1);
       }
     }
 
     const char* startupInterval = getenv("MOZ_PROFILER_STARTUP_INTERVAL");
     if (startupInterval && startupInterval[0] != '\0') {
       // The interval is a floating point number. Use StringToDouble rather than
       // strtod, so that "." is used as the decimal separator regardless of OS
       // locale.
       auto intervalValue = StringToDouble(MakeStringSpan(startupInterval));
       if (intervalValue && *intervalValue > 0.0 && *intervalValue <= 1000.0) {
         interval = *intervalValue;
         LOG("- MOZ_PROFILER_STARTUP_INTERVAL = %f", interval);
       } else {
         PrintToConsole("- MOZ_PROFILER_STARTUP_INTERVAL not a valid float: %s",
                        startupInterval);
-        PrintUsageThenExit(1);
+        PrintUsage();
+        exit(1);
       }
     }
 
     features |= StartupExtraDefaultFeatures() & AvailableFeatures();
 
     const char* startupFeaturesBitfield =
         getenv("MOZ_PROFILER_STARTUP_FEATURES_BITFIELD");
     if (startupFeaturesBitfield && startupFeaturesBitfield[0] != '\0') {
       errno = 0;
       features = strtol(startupFeaturesBitfield, nullptr, 10);
       if (errno == 0) {
         LOG("- MOZ_PROFILER_STARTUP_FEATURES_BITFIELD = %d", features);
       } else {
         PrintToConsole(
             "- MOZ_PROFILER_STARTUP_FEATURES_BITFIELD not a valid integer: %s",
             startupFeaturesBitfield);
-        PrintUsageThenExit(1);
+        PrintUsage();
+        exit(1);
       }
     } else {
       const char* startupFeatures = getenv("MOZ_PROFILER_STARTUP_FEATURES");
       if (startupFeatures) {
         // Interpret startupFeatures as a list of feature strings, separated by
         // commas.
         UniquePtr<char[]> featureStringStorage;
         Vector<const char*> featureStringArray =
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -647,21 +647,21 @@ already_AddRefed<nsIDNSService> nsDNSSer
 
       if (XRE_IsContentProcess() || XRE_IsParentProcess()) {
         return ChildDNSService::GetSingleton();
       }
 
       return nullptr;
     }
 
-    if (XRE_IsParentProcess()) {
+    if (XRE_IsParentProcess() || XRE_IsSocketProcess()) {
       return GetSingleton();
     }
 
-    if (XRE_IsContentProcess() || XRE_IsSocketProcess()) {
+    if (XRE_IsContentProcess()) {
       return ChildDNSService::GetSingleton();
     }
 
     return nullptr;
   };
 
   if (gInited) {
     return getDNSHelper();
@@ -671,17 +671,16 @@ already_AddRefed<nsIDNSService> nsDNSSer
   if (dns) {
     gInited = true;
   }
   return dns.forget();
 }
 
 already_AddRefed<nsDNSService> nsDNSService::GetSingleton() {
   MOZ_ASSERT_IF(nsIOService::UseSocketProcess(), XRE_IsSocketProcess());
-  MOZ_ASSERT_IF(!nsIOService::UseSocketProcess(), XRE_IsParentProcess());
 
   if (!gDNSService) {
     if (!NS_IsMainThread()) {
       return nullptr;
     }
     gDNSService = new nsDNSService();
     if (NS_SUCCEEDED(gDNSService->Init())) {
       ClearOnShutdown(&gDNSService);
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -2451,16 +2451,17 @@ void nsHttpConnectionMgr::OnMsgCompleteU
         NS_NewRunnableFunction("net::nsHttpConnectionMgr::OnMsgCompleteUpgrade",
                                transportAvailableFunc));
   } else {
     transportAvailableFunc();
   }
 }
 
 void nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase*) {
+  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   uint32_t param = static_cast<uint32_t>(inParam);
   uint16_t name = ((param)&0xFFFF0000) >> 16;
   uint16_t value = param & 0x0000FFFF;
 
   switch (name) {
     case MAX_CONNECTIONS:
       mMaxConns = value;
       break;
@@ -2528,19 +2529,29 @@ void nsHttpConnectionMgr::ActivateTimeou
   }
 
   if (!mTimeoutTick) {
     mTimeoutTick = NS_NewTimer();
     if (!mTimeoutTick) {
       NS_WARNING("failed to create timer for http timeout management");
       return;
     }
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    if (!mSocketThreadTarget) {
+      NS_WARNING("cannot activate timout if not initialized or shutdown");
+      return;
+    }
     mTimeoutTick->SetTarget(mSocketThreadTarget);
   }
 
+  if (mIsShuttingDown) {  // Atomic
+    // don't set a timer to generate an event if we're shutting down
+    // (and mSocketThreadTarget might be null or garbage if we're shutting down)
+    return;
+  }
   MOZ_ASSERT(!mTimeoutTickArmed, "timer tick armed");
   mTimeoutTickArmed = true;
   mTimeoutTick->Init(this, 1000, nsITimer::TYPE_REPEATING_SLACK);
 }
 
 class UINT64Wrapper : public ARefBase {
  public:
   explicit UINT64Wrapper(uint64_t aUint64) : mUint64(aUint64) {}
@@ -2556,16 +2567,17 @@ class UINT64Wrapper : public ARefBase {
 nsresult nsHttpConnectionMgr::UpdateCurrentTopBrowsingContextId(uint64_t aId) {
   RefPtr<UINT64Wrapper> idWrapper = new UINT64Wrapper(aId);
   return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateCurrentTopBrowsingContextId,
                    0, idWrapper);
 }
 
 void nsHttpConnectionMgr::SetThrottlingEnabled(bool aEnable) {
   LOG(("nsHttpConnectionMgr::SetThrottlingEnabled enable=%d", aEnable));
+  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
 
   mThrottleEnabled = aEnable;
 
   if (mThrottleEnabled) {
     EnsureThrottleTickerIfNeeded();
   } else {
     DestroyThrottleTicker();
     ResumeReadOf(mActiveTransactions[false]);
@@ -2942,16 +2954,17 @@ void nsHttpConnectionMgr::EnsureThrottle
       mThrottleTicker->Init(this, mThrottleReadInterval,
                             nsITimer::TYPE_ONE_SHOT);
     }
   }
 
   LogActiveTransactions('^');
 }
 
+// Can be called with or without the monitor held
 void nsHttpConnectionMgr::DestroyThrottleTicker() {
   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
 
   // Nothing to throttle, hence no need for this timer anymore.
   CancelDelayedResumeBackgroundThrottledTransactions();
 
   MOZ_ASSERT(!mThrottleEnabled || !IsThrottleTickerNeeded());
 
@@ -3371,16 +3384,17 @@ void nsHttpConnectionMgr::OnMsgSpeculati
   LOG(
       ("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s, "
        "mFetchHTTPSRR=%d]\n",
        args->mTrans->ConnectionInfo()->HashKey().get(), args->mFetchHTTPSRR));
   DoSpeculativeConnection(args->mTrans, args->mFetchHTTPSRR);
 }
 
 bool nsHttpConnectionMgr::BeConservativeIfProxied(nsIProxyInfo* proxy) {
+  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   if (mBeConservativeForProxy) {
     // The pref says to be conservative for proxies.
     return true;
   }
 
   if (!proxy) {
     // There is no proxy, so be conservative by default.
     return true;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -203,38 +203,40 @@ class nsHttpConnectionMgr final : public
   friend class DnsAndConnectSocket;
   friend class PendingTransactionInfo;
 
   //-------------------------------------------------------------------------
   // NOTE: these members may be accessed from any thread (use mReentrantMonitor)
   //-------------------------------------------------------------------------
 
   ReentrantMonitor mReentrantMonitor{"nsHttpConnectionMgr.mReentrantMonitor"};
+  // This is used as a flag that we're shut down, and no new events should be
+  // dispatched.
   nsCOMPtr<nsIEventTarget> mSocketThreadTarget;
 
+  Atomic<bool, mozilla::Relaxed> mIsShuttingDown{false};
+
+  //-------------------------------------------------------------------------
+  // NOTE: these members are only accessed on the socket transport thread
+  //-------------------------------------------------------------------------
   // connection limits
   uint16_t mMaxUrgentExcessiveConns{0};
   uint16_t mMaxConns{0};
   uint16_t mMaxPersistConnsPerHost{0};
   uint16_t mMaxPersistConnsPerProxy{0};
   uint16_t mMaxRequestDelay{0};  // in seconds
   bool mThrottleEnabled{false};
   uint32_t mThrottleVersion{2};
   uint32_t mThrottleSuspendFor{0};
   uint32_t mThrottleResumeFor{0};
   uint32_t mThrottleReadLimit{0};
   uint32_t mThrottleReadInterval{0};
   uint32_t mThrottleHoldTime{0};
   TimeDuration mThrottleMaxTime;
   bool mBeConservativeForProxy{true};
-  Atomic<bool, mozilla::Relaxed> mIsShuttingDown{false};
-
-  //-------------------------------------------------------------------------
-  // NOTE: these members are only accessed on the socket transport thread
-  //-------------------------------------------------------------------------
 
   [[nodiscard]] bool ProcessPendingQForEntry(ConnectionEntry*,
                                              bool considerAll);
   bool DispatchPendingQ(nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ,
                         ConnectionEntry* ent, bool considerAll);
 
   // This function selects transactions from mPendingTransactionTable to
   // dispatch according to the following conditions:
--- a/netwerk/test/unit/test_httpssvc_retry_with_ech.js
+++ b/netwerk/test/unit/test_httpssvc_retry_with_ech.js
@@ -80,17 +80,17 @@ function channelOpenPromise(chan, flags)
     let internal = chan.QueryInterface(Ci.nsIHttpChannelInternal);
     internal.setWaitForHTTPSSVCRecord();
     chan.asyncOpen(new ChannelListener(finish, null, flags));
   });
 }
 
 add_task(async function testConnectWithECH() {
   const ECH_CONFIG_FIXED =
-    "AEv+CgBHTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAAwBkABZlY2gtcHVibGljLmV4YW1wbGUuY29tAAA=";
+    "AEn+DQBFTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAA2QWZWNoLXB1YmxpYy5leGFtcGxlLmNvbQAA";
   trrServer = new TRRServer();
   await trrServer.start();
 
   Services.prefs.setIntPref("network.trr.mode", 3);
   Services.prefs.setCharPref(
     "network.trr.uri",
     `https://foo.example.com:${trrServer.port}/dns-query`
   );
@@ -145,17 +145,17 @@ add_task(async function testConnectWithE
 
   await trrServer.stop();
 });
 
 add_task(async function testEchRetry() {
   dns.clearCache(true);
 
   const ECH_CONFIG_TRUSTED_RETRY =
-    "AEv+CgBHTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAAQBkABZlY2gtcHVibGljLmV4YW1wbGUuY29tAAA=";
+    "AEn+DQBFTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAMAA2QWZWNoLXB1YmxpYy5leGFtcGxlLmNvbQAA";
   trrServer = new TRRServer();
   await trrServer.start();
 
   Services.prefs.setIntPref("network.trr.mode", 3);
   Services.prefs.setCharPref(
     "network.trr.uri",
     `https://foo.example.com:${trrServer.port}/dns-query`
   );
--- a/netwerk/test/unit/test_httpssvc_retry_without_ech.js
+++ b/netwerk/test/unit/test_httpssvc_retry_without_ech.js
@@ -68,17 +68,17 @@ function channelOpenPromise(chan, flags)
     let internal = chan.QueryInterface(Ci.nsIHttpChannelInternal);
     internal.setWaitForHTTPSSVCRecord();
     chan.asyncOpen(new ChannelListener(finish, null, flags));
   });
 }
 
 add_task(async function testRetryWithoutECH() {
   const ECH_CONFIG_FIXED =
-    "AFL+CgBOTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAAwBkAB1kZWxlZ2F0ZWQtZW5hYmxlZC5leGFtcGxlLmNvbQAA";
+    "AEn+DQBFTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAA2QWZWNoLXB1YmxpYy5leGFtcGxlLmNvbQAA";
   trrServer = new TRRServer();
   await trrServer.start();
 
   Services.prefs.setIntPref("network.trr.mode", 3);
   Services.prefs.setCharPref(
     "network.trr.uri",
     `https://foo.example.com:${trrServer.port}/dns-query`
   );
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -437,17 +437,19 @@ skip-if =
   os == 'linux' && bits == 64 && !debug # Bug 1710277
 run-sequentially = http3server
 [test_http3_perf.js]
 skip-if =
   os == 'android'
   os == 'linux' && bits == 64 && !debug # Bug 1747519
 run-sequentially = http3server
 [test_http3_prio.js]
-skip-if = os == 'android'
+skip-if = 
+  os == 'android'
+  os == 'linux' && bits == 64 && !debug # Bug 1742691
 run-sequentially = http3server
 [test_http3_early_hint_listener.js]
 skip-if = os == 'android'
 run-sequentially = http3server
 [test_node_execute.js]
 [test_loadgroup_cancel.js]
 [test_obs-fold.js]
 [test_defaultURI.js]
--- a/security/manager/ssl/tests/unit/test_encrypted_client_hello.js
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello.js
@@ -7,25 +7,25 @@
 // can be regenerated by running EncryptedClientHelloServer
 // and dumping the output of SSL_EncodeEchConfig. They do not
 // expire. An update here is only needed if the host or ECH
 // ciphersuite configuration changes, or if the keypair in
 // EncryptedClientHelloServer.cpp is modified.
 
 // Public name: ech-public.example.com
 const ECH_CONFIG_FIXED =
-  "AEv+CgBHTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAAwBkABZlY2gtcHVibGljLmV4YW1wbGUuY29tAAA=";
+  "AEn+DQBFTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAA2QWZWNoLXB1YmxpYy5leGFtcGxlLmNvbQAA";
 
 // Public name: ech-public.example.com, Unsupported AEAD to prompt retry_configs from a trusted host.
 const ECH_CONFIG_TRUSTED_RETRY =
-  "AEv+CgBHTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAAQBkABZlY2gtcHVibGljLmV4YW1wbGUuY29tAAA=";
+  "AEn+DQBFTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAMAA2QWZWNoLXB1YmxpYy5leGFtcGxlLmNvbQAA";
 
 // Public name: selfsigned.example.com. Unsupported AEAD to prompt retry_configs from an untrusted host.
 const ECH_CONFIG_UNTRUSTED_RETRY =
-  "AEv+CgBHTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAAQBkABZzZWxmc2lnbmVkLmV4YW1wbGUuY29tAAA=";
+  "AEn+DQBFTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAMAA2QWc2VsZnNpZ25lZC5leGFtcGxlLmNvbQAA";
 
 function shouldBeAcceptedEch(aTransportSecurityInfo) {
   Assert.ok(
     aTransportSecurityInfo.isAcceptedEch,
     "This host should have accepted ECH"
   );
 }
 
--- a/security/manager/ssl/tests/unit/test_encrypted_client_hello_client_only.js
+++ b/security/manager/ssl/tests/unit/test_encrypted_client_hello_client_only.js
@@ -1,17 +1,16 @@
 /* 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";
 
 // Public Name = delegated-enabled.example.com
 const ECH_CONFIG_FIXED =
-  "AFL+CgBOTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAAwBkAB1kZWxlZ2F0ZWQtZW5hYmxlZC5leGFtcGxlLmNvbQAA";
-
+  "AFD+DQBMTQAgACCKB1Y5SfrGIyk27W82xPpzWTDs3q72c04xSurDWlb9CgAEAAEAA2QdZGVsZWdhdGVkLWVuYWJsZWQuZXhhbXBsZS5jb20AAA==";
 do_get_profile();
 
 // An arbitrary, non-ECH server.
 add_tls_server_setup(
   "DelegatedCredentialsServer",
   "test_delegated_credentials"
 );
 
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_74_RTM
\ No newline at end of file
+44e6341be5e8
\ No newline at end of file
--- a/security/nss/automation/abi-check/previous-nss-release
+++ b/security/nss/automation/abi-check/previous-nss-release
@@ -1,1 +1,1 @@
-NSS_3_73_BRANCH
+NSS_3_74_BRANCH
--- a/security/nss/build.sh
+++ b/security/nss/build.sh
@@ -63,20 +63,16 @@ fuzz=0
 fuzz_tls=0
 fuzz_oss=0
 no_local_nspr=0
 sslkeylogfile=1
 
 gyp_params=(--depth="$cwd" --generator-output=".")
 ninja_params=()
 
-# Assume that the target architecture is the same as the host by default.
-host_arch=$(python "$cwd/coreconf/detect_host_arch.py")
-target_arch=$host_arch
-
 # Assume that MSVC is wanted if this is running on windows.
 platform=$(uname -s)
 if [ "${platform%-*}" = "MINGW32_NT" -o "${platform%-*}" = "MINGW64_NT" ]; then
     msvc=1
 fi
 
 # Parse command line arguments.
 all_args=("$@")
@@ -127,22 +123,34 @@ while [ $# -gt 0 ]; do
         --system-nspr) set_nspr_path "/usr/include/nspr/:"; no_local_nspr=1 ;;
         --system-sqlite) gyp_params+=(-Duse_system_sqlite=1) ;;
         --enable-fips) gyp_params+=(-Ddisable_fips=0) ;;
         --enable-libpkix) gyp_params+=(-Ddisable_libpkix=0) ;;
         --mozpkix-only) gyp_params+=(-Dmozpkix_only=1 -Ddisable_tests=1 -Dsign_libs=0) ;;
         --disable-keylog) sslkeylogfile=0 ;;
         --enable-legacy-db) gyp_params+=(-Ddisable_dbm=0) ;;
         --mozilla-central) gyp_params+=(-Dmozilla_central=1) ;;
+	--python) python="$2"; shift ;;
+	--python=*) python="${1#*=}" ;;
         -D*) gyp_params+=("$1") ;;
         *) show_help; exit 2 ;;
     esac
     shift
 done
 
+if [ -n "$python" ]; then
+    gyp_params+=(-Dpython="$python")
+fi
+
+if [ -z "$target_arch" ]; then
+    # Assume that the target architecture is the same as the host by default.
+    host_arch=$(${python:-python} "$cwd/coreconf/detect_host_arch.py")
+    target_arch=$host_arch
+fi
+
 # Set the target architecture and build type.
 gyp_params+=(-Dtarget_arch="$target_arch")
 if [ "$opt_build" = 1 ]; then
     target=Release
 else
     target=Debug
 fi
 
--- a/security/nss/cmd/addbuiltin/addbuiltin.c
+++ b/security/nss/cmd/addbuiltin/addbuiltin.c
@@ -547,17 +547,19 @@ main(int argc, char **argv)
             fprintf(stderr, "%s: Cannot change stdout to binary mode.\n", progName);
             exit(1);
         }
     }
 #endif
 
     nickname = strdup(addbuiltin.options[opt_Nickname].arg);
 
-    NSS_NoDB_Init(NULL);
+    if (NSS_NoDB_Init(NULL) != SECSuccess) {
+        exit(1);
+    }
 
     if (addbuiltin.options[opt_Distrust].activated ||
         addbuiltin.options[opt_DistrustCRL].activated) {
         addbuiltin.options[opt_ExcludeCert].activated = PR_TRUE;
         addbuiltin.options[opt_ExcludeHash].activated = PR_TRUE;
     }
 
     if (addbuiltin.options[opt_Distrust].activated) {
--- a/security/nss/cmd/bltest/tests/ecdsa/README
+++ b/security/nss/cmd/bltest/tests/ecdsa/README
@@ -1,4 +1,5 @@
-0 nistp256
-1 nistp384
-# the following tests are not yet implemented
-2 nistp521
+The files in this directory contain test-vectors for ECDSA using NIST P-256 Curve (the test-vectors from 0 to 6 included), using NIST P-384 Curve (the test-vectors from 7 to 13 included) and using NIST P-521 Curve (the test-vectors from 14 to 20 included). 
+
+The key files used for the signature contain a curve, a private key and a public key. Each key is represented as follows: Base64(len (curveID), curveID, len(privateKey), privateKey, len(publicKey), publicKey). The length is 4 bytes long. The curveID is a DER encoded OID (as stated in http://www.secg.org/sec2-v2.pdf). A public key (a point) is encoded as 0x4 || x coordinate || y coordinate, where (x, y) computed using the base point. The private key is generated randomly. To generate the test-vectors we were using Sage Math system. 
+
+The random nonces (sigseed) and the plaintexts (already as hashes) are generated randomly and encoded using Base64 encoding. The resulted ciphertexts are presented in the ciphertext files and encoded using Base64 encoding. 
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext0
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext0
@@ -1,1 +1,1 @@
-GoWqve3YezF7HOABQjioFL/3oq32oM9pHsGTQTJE7aFE62nItVqAdg==
+g0O4OK4uKlJAlR1MDRLTBjyfxFI3PLKLYk+pJyu7gDJTgMptbhg+vZS0lgEBR7jHFDG89TymXn2bZ+NWDE6h5Q==
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext1
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext1
@@ -1,1 +1,1 @@
-PM6xHbiwP6Xcb44mg7BHtaJvd8PkxgvHAB1sh2cF0so3naFf0Tj6vQ==
+yaMRWtXb5AUhgva2zYOq3aczhYOP5pEldcaXtbnIbARVv3o2RjdhnVl2Nq6BaL36nPdO6KZrjHm0oQoUvD2bTA==
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext10
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext10
@@ -1,2 +1,2 @@
-AF3bbyED08NTrUgKmag9HiuUbaW0skXA/Bp9RPjRAD6M0rp3nvLDKozI940jxPP1
-nWpHF7VcyCVzJeV6
+AIlc19+p3H+s2rJ98KkTaA351Vz2pAVuMCFB1jRshJVrw4QbHS+UQ9VuSGjZLe6dTf5vBAjlfeYQ
+NGnU7yhOxU2nl3tI+9qe/MrAL76d3e0+G/jBHk8hp006TbdiBrNK
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext11
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext11
@@ -1,2 +1,1 @@
-AOLrxy4FWd29ToUjOwLs6GyQ+dYZN6NkZ8oVO6dsAEXt55ePlCWZbOtmk6v9PrNG
-JOsY/MHnGhDeAGRl
+L8GGYb8eZCyjr/YAgmB7jWonjqXZ773+Iizm80wRe9PQzQKjaP3c9PiBjAJ5W8VBH8X0twayfznc/v4jozzE2PC3adIkOiIhn4TGWd0zcD/TpxeVreCEOLtCnO7ZTwGh
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext12
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext12
@@ -1,2 +1,1 @@
-aQHMte9cFByD9Ff3rZOPOtPI75luPoxemmgjXIgh/9jEeoTdDk8xuAYQUkayCfs+
-DpDaGnOLkfAyZ8GcuaCujg==
+lbxmnrmyoI+SotJ0F3cTBm+XigEB12hCBhXWdLotb1juvr80ksg2komHRqzKuuyrgQbmQwTnpQfmn/Y2iNx3CsL4++UWRH77/MaleccxGjHTJ0fNoQsBTT3Pa7s+1FDg
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext13
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext13
@@ -1,2 +1,1 @@
-AaeVCRJQPbpTqa1+zLd/8xAbkz3KKTr0dlS4tuGC8hc9j5esAeEv+7IklbA3v5Jz
-jC+nJy4p81iNO5E9H8nfGGckfQSiFzHG
+wRJKnt6DnqSYtMHr0HX/4v3qT2FTdLA/aY40y0Grz8Jc5aPD062+mUSSI3d43BKy3aGvJBJPXo5FfBAGZrrcPTZ4hb783D8MnRRzGnYqlP18d+HxGbhI/X3FgQvDpvoA
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext14
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext14
@@ -1,2 +1,3 @@
-AgU0N7zJPg/1UxmCWD5Z+DqDqkRKjy4heFgayCyopb/u4XErAZArgsjashAxzMKC
-PSDJasPT90T5Va8sNtjXtSpHWxc2roV9
+AcKWiddIfeC5MGnUw8HxiW6h55HY+QgSgE2aPGdgv06hz3fF7+ibSHHWOOOKDAb+DOkDS/06fUSj
+Bz4JPrI+1S7GAfsjSDNA7FCdMMDlz6SzJ/AXtDqEnqPvuKjxkZTIWFoGcl3ckEqaXuHZQY6/ZUEC
+tU6BBedWeaZcj0VDiFDhT7VM
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext15
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext15
@@ -1,2 +1,3 @@
-NXo8is+7lAoOwWGt7+GBbT/UX8LGs8TXEHBI+tX9311pJ4J3pfBYobgN0ZK6ZBtp
-dS6PkrPaQp0S9nrfTOS5uAH95eD1eymRfCbOnjTUKzLuIn53V17vRjdcDtLzrhzX
+ALrhfS4bAPvF9GooBRAmS5BPItf9JJxuB9kXAnqFyoTfeo9Qj2X1BsZ1lRQ9/tNm791Pg434w6XT
+jVifx3sWdo79AZ/sgRq97VGVjVLvSNPkEuDWUP0UcjHQP7sOPZS3i04pactfM2D/xI9KKTH7nF0/
+oNQGqp3kSV5pdo9tBD5kNLxP
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext16
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext16
@@ -1,3 +1,3 @@
-ADhxjBz/ACTy4GJlL0tYZpyNpC4DsXND9lJuU7x9N7g6gkpJyBPw3vBYU1olw6PH
-dnegpgAm4Gh6MCsZB4KBcLwl1wjt4B3p2eqEqDYn5fiie5f4XuRomvI92jR5Sb+I
-nBLCHIppt/Q=
+AUpC4BhuE7JTjlHZVrtjHZgzyudEOC5/I73cN3IJREJoUi4GaD34Lbc2Uaj2I40J/imqTukMAF92
+AIhzAoPKbY7LATmfsfCzC9QJJ110ch19FxipEUEmgYi2khejzsDgSjhDP9DjUHyvnX5GCC/jGzbZ
++38Vef+1pH73V1Dk7VGPfly/
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext17
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext17
@@ -1,3 +1,3 @@
-AGhHQ6kfdZRgu1svQTXEIewvFVglnUy6ANPumyUbM14AEfRkCUNa1uzvhV1sbWYj
-qT3egQCA9MTjThDNJeDOvvL6hVVOryUv4+C3RtkpQGCtdml+CSsjVTej8h9JbMds
-Dme40b2G6fE=
+AVu1YHSDn9uZIQcRKy79vaUtrfJb6dG6gX001BmwMnInkt3QpylrzyPScpWlMHHeNG7WhSJudTp+
+9EbcmQOvG6eUAb1cnamAm0q3oX4PlJMM50lUsX1bjSG7CEYwb0x1H9xr9fN+TgFMUgvZZXC7pY5r
+bhHlEUYQcMmRKG9ZHFc6LbEj
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext18
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext18
@@ -1,3 +1,3 @@
-AGBuqk48tufy0bKEWpu+xEHsmi+6KCfdwOSRwLDnpVetGe9AWknHDzeTSwe0QxcE
-RsEkUZGDpxfzUlCLSSSU+ErrYY/uyLV2AJTb3prB6A2YNwdmFGeRbDoxeOu7FuQA
-3gxBQhR+TGMuskeM+BdHFmFrwvTTdHCGzjTBa5S8mbgEJTfeik/it28T/9i+duZ8
+AHC8RciYJUDbd9w/47KfKD/9pKS98cXqep7XnKi9NDRuKi1wzgnVDMiUACkRA4IRy2I1/9cNKA8o
+j17hRNbWAEO9AF2fg5BhJSNLiOAkhBCt2GvLOsBRvPX58xn4u6gWyY8oV5bFBrMQvHS2avNujgHs
+MI+/lDXXb7w6GOFiScWeX6NC
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext19
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext19
@@ -1,3 +1,3 @@
-AaiotJfCiWU1d2LFe+t0CcWHDSF7EOlApWYJ+RNRSq8TbkXJIzi6abbb7BovtRwf
-i/COYwjS7OnkFQ6x5Pdrb7OZ0dTAdDRXAKtXWSKR20Y4fhnx/HUxisFwKrsCEQ3O
-uVtwDG8rh5V8zjBnCEcs5Iy9CsklucibR0PIyglVmW+ZuY42YNebuOC2VUKqHNF7
+AXNA1v4coiWNri0FEpHBpyDBt3IWjsaSa222ySUgcf6bz1SUIfB0DBGPZI7CZpRHl+B8oRHaD6gB
++IN6+4UI+nf+ACSsoM4ivi6HO3OcwG1uK1I93qhujMOkHSWZ27njMq6K20VbAW1WIAOwIwhEyqUc
+br/V+GZCCQgoKdmRh4Q2daOi
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext2
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext2
@@ -1,1 +1,1 @@
-Vli8Hau3xL8oder6ZdM9Y3fMd92jbguiMq6F+9CUjlUQXy5EwAVGeg==
+tnDjU02HxiZc64bV5bXn0SY9qRc302CSEXbSojvsVWL9MDLf7n+HYoCL3KzDzc9c3Am23YkNe+UmGmyiQ2cDsQ==
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext20
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext20
@@ -1,3 +1,3 @@
-ALAM5hGnex7TvBbSEzDlfv+n5g7aWyRyZsBbl2Y6wW1plSovbq2GcV6w1ZV1Vlot
-70zbqkKyNApvTi3xoD4Ens6pAeLMYDILwaQhnyJZWQv3etbWqUKJZNgfH1IDj03k
-n9hbjYLX3y4bc4CnrhOiv5Ab34s7M8wUYcjC+DbHwhLl/S6N
+APkk3zqRJEzbXeIJ1d7RgzkRJzgTUZwwpTDSVvSGgkMLs/vKSBnWHQk4sVGbU5lTNbgltF6sBC+1
+J2XgsiQ/xqLbAG/Bu4mbGTmWs1kaNDiDFg/BUr+au2QQSx8HbBTHZcXLmsLxNzItCS8oAlRA2Rjv
+Nq9oZNaz3rebPdt9xHXFh6bw
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext3
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext3
@@ -1,1 +1,1 @@
-AFohw5TN/dpmqbhp/T4z1Rl1boAUA6r9eEPJbYN0zf+eHZzyvezxqjxU
+4KKGd2/fEIClyuhRwrehW6BKH0/+OQgsirRfzIcrOZvFzAfKi5khbVYxHK37tL9P5PomDbXN4Dp0AeruvBLAiA==
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext4
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext4
@@ -1,1 +1,1 @@
-AtJdCPXn5yQW34jekhsnsNmaMOeeA3KIVl1d2+7pb6QycUAzYccgwSrp
+pnjNl+SQmTBayW5VQ+CkmKP0xnB7UTBY4RsZtkSbCeTHWSbv3i6bZPR105a0uMLG5nS/gQjyl4eH/zE+MgETpA==
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext5
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext5
@@ -1,1 +1,1 @@
-AzEg0sOGHwxd0o3cv+o9dsRPOzXMAdpgtI6O0uUmVN2+a5qI5FYQlItz
+U9uDPGgYelgtc3AfIMnUtUUbAy0r+Gfu4Ig2RnrtlEZyEBE4VFKqwT55ScQqD5FUwjCqhjtnrKohq+FbrRejjA==
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext6
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext6
@@ -1,1 +1,1 @@
-5+HDXH/ieN8Bzxd3dfxKZoqbbhsm7jyeqWdemt6Xy0kx+7zwSYsh9Ng5KRdy6wtA
+7ED/IZr06H2cFwgTZ3iOtg59SCsSIa4+t1DW9cwd2u1oMgkkFvPHMgQboH8aULC6lRE8aGMJZTZ6WnTYJR3hBw==
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext7
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext7
@@ -1,1 +1,1 @@
-WcS9umnUASP0X6lHvkWJwPY37ZVvAMLBERHLjL3Vzg6QVjwcS8kDVortTFei3aTx
+b37O7Ui12JgVhBxhPsr6SVBu9WAfYgNj0VE5j8sIq7S2KH2UkLMsV87cAn0LAj58clN0lmBJXJViLyU9zptz5A/IodDm/mAY92yVr2V/SdOGdrubjqQ74giLceDaWK5G
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext8
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext8
@@ -1,2 +1,1 @@
-ItpmPaGAaoe2feXPbh5+EASLGnEzyYbEnwJ+JFNSOQcoY4a/cMV2rn8FYyBsEDiZ
-LPDBU0i2uOg=
+5wrpYRET0Om1gAeZOQrqeDkWLC11pKLF2ECxWwaUD4lqDbJYKF6mfYPXz8hrrbFvfq1TQywm+Wvb9fkAofHg99tdlv5HOE0vrhIdZHyXACtJ9hAjlYYZhmp0wSjP02ZH
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/ciphertext9
+++ b/security/nss/cmd/bltest/tests/ecdsa/ciphertext9
@@ -1,2 +1,1 @@
-QjzCVGRUjulOLqeBqC5xpY0GWomOrmQUCtImY0czn98a/jHrdgsSRKiMHukBUxM1
-TIRGjkV2L+A=
+Ehmee5KL3MVy+ulw/4sw22HbIqY2pPp3V29Q7MTTSmA9xxhyLe4tuzWvio3rRFFaSiTDJUs9FJ/qN5uRrU59VtyU27jv+jXmRZGF14eW/+bI7pJ46u2I4oC/PKWrJUlg
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/key1
+++ b/security/nss/cmd/bltest/tests/ecdsa/key1
@@ -1,4 +1,1 @@
-AAAABwYFK4EEACIAAABhBLWMJG3t4khPYcsl3H492rAqukJ1RqJm27pqpN54rFGG
-r2VDwOfqb9tMninq8IyOh42eaaVOEPXXu4Q/ATWBEfrbTRBjTpzAE2SSPuQma0lM
-q0RSVECCgdBOKIhB0H6VxAAAADA3WPjUaMWCS9E5KbVDrEcf5CV5tCNNWJQkwjsA
-yALMCiXJqRVXwbq42WMuaELMW+g=
+AAAACgYIKoZIzj0DAQcAAABBBP8XY2jN9iSwGmgjiKiEJ13traQZAfQjp9gm/s1ued3vKcAUoCya7wVzoOtE+1e318eseAmUCFTmSue3oRQ4iNAAAAAgUoPXpYUpyVq3AM/eQ8krtoa+IvndvJSu2d0Wfmw4G3k=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key10
@@ -0,0 +1,3 @@
+AAAABwYFK4EEACIAAABhBGNmUY5lDAhCxgugJReD7Q9NWibKxv4mPCeXk90hyZKquY0U1Z6WUOEY
+PbpMei6MDGdjOUnMAfTLKa10WajMqIFgR7rInBQqZqOtpKqtFUb3ilJbiOZUnUpn4zuWoDUCFQAA
+ADD/dS8s2Bui8eTEmURjxj0bzlPVfhbPErX/BETy11xb63U48OK2obH+N5b1mJlck3w=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key11
@@ -0,0 +1,3 @@
+AAAABwYFK4EEACIAAABhBI+fZwvjDD9sX9mh9nLTOiy6npcJ2nzGGeDLkcSlFhsnwigQLvyMNBvP
+d7afj9P09hHRozYAf2Z0UWhCHAiFccw9GChq2eTgCvTl6jxNKsdPIHqa1+eoukMB2mLchEKk8gAA
+ADAVhVW7PB4G4UBgfYgSDrbKZ4MJaED8XCvlKjjvPHaiYlr7+le4A54annNInynW3DY=
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key12
@@ -0,0 +1,3 @@
+AAAABwYFK4EEACIAAABhBPF9n9O34oRxht9krXuHEQFusdW2Q7XrgWTMX4w4YXasi3OXSzOHywxa
+JQ8jIXu7AcUVQTLfhSwQvswwvzvJUD1vPiJH/AZFPhQOrg8fYzsuSVs0NLQ/PW0nDqdcz9BVxAAA
+ADBfPgxyWf18Zn/ftiQfrpU/+Td0WD3QhAVYbNmRGGzP73YSLgmD8rafs1fW6NjZEOg=
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key13
@@ -0,0 +1,3 @@
+AAAABwYFK4EEACIAAABhBN9riLgrpq184TDaWpgqnU4pYyqyTXreZQ69O2sU4GENCGN9tmsgyGi3
+lt0r4wRwchlU4cxJeO5f6XrX0dqF46s57Pw6+tFf2OCU1X0jCPn7NrYz8X9Gjw9ZrKnqESLNyQAA
+ADBIzoBK0nAaFkL9z0H/6dmakHfrRZtrZE556sN0bHbN6uo+YGv8JK4xYJ+okqorCV4=
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key14
@@ -0,0 +1,5 @@
+AAAABwYFK4EEACMAAACFBAHLMSpMFVyG6mXE7SZ5O5Bwv4d8/QiAB3BzpXkyrU1W
+jJ9O9uOYTXM+cFtF5v56+LsI4yGkaAl9+RF6lFPjrhpIswCmBmEqMBgZpjoz38my
+nLHBI9MaFF8AHkRQwD3LJLo4eSZHOVkdIvDYLwicdlgr0zD3Nf76/HB1+0DkBGqE
+MyG22gAAAEIAFah7z179UbqqdH68pzdZsP1ChXjtYZ11rBM0+HP7yLirxH3ahKTt
+DjsY19GEjz4gKsaLfLiQ1/Dp+VKVLcBKpk0=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key15
@@ -0,0 +1,4 @@
+AAAABwYFK4EEACMAAACFBAHNraBOZWsOfiIfcjggO2UgdVT/hWKgnuPNxeiBUN0y7hhWdeJDJEQw
+h8CKaKe34NwLHkI9v3srwrjjgrZhSE1C0wEqnTHutNYvzgdim0/1ez1AltxjpE2EELzUqnsrXl+V
+IbvO/SMGiUb4SPz3ddc3BDQ6uCCWkawr6CryY6fbQHSZgQAAAEIBVb3HzBg8uneljgGlyFD+6hQs
+nyNyOwYeMZTgd7pPCY43K4TGDGFtKzpDWPWjf5/TvLgxFhvzwPGLPMK5TLPtTPs=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key16
@@ -0,0 +1,4 @@
+AAAABwYFK4EEACMAAACFBAELM1S0u6wWCy7/Alg1B/a3VSSnO42mKq/7Eydq+ae+nqhLUa7tX7Vv
+e6N1uWdJOX4t1ZwdHKyQUrrcR2ZhXCymmABTwSVTmcqRRIALlehX+Z24mE1hMyxJwlJAGOmDNpXg
+aWhBXQXu10vqQVfWqOER5OdvZlSMDlsHVmldgTxFi/tw8wAAAEIBc6g47xXFFvgOcqgikHnuMm+4
+F4YmKLbIXuCAlVqeMtlGG4utC4jeX2BKgsVoifsL1amJM4NNP9PJmeXHjtJKpRo=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key17
@@ -0,0 +1,4 @@
+AAAABwYFK4EEACMAAACFBADPXFUBwFhZPy1f6FaZu6IWEDpPRh0UmxGsbBYyXGXme/zgR9masrLY
+Q9SmgR9I+qfl2tpBjmwB8ltoNjcxyJGg+QFAdH/rM+0TRJo6qDm7uJNzY5mloOsKVgefekfMEPdX
+A2Sn2FVafJLZW7A6R6GvxRX5btTgjM6/XfnJaHr47DFBMwAAAEIAwzmBaAc3LPeoyPzcX70vAo6T
+jfI7tvA+N3DXvynLTcKWYZjIilMIqsNn7u+eMQ0xDM45im6j3yPcATmgGzUDam4=
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key18
@@ -0,0 +1,4 @@
+AAAABwYFK4EEACMAAACFBAFieatjCQKes5RNySyOxoR3TvNJ9tDAHwsR9RZJM4S3At6tU3fe8Pql
+yPt1eibbLvh7wgELngpPmoIRVWGG4LbCiQGHsWFJtpZQ2bs7BHGHT7L/t+vGqOUtvubTLq+xfTgW
+pUj0epQp+M8ZD0Fd622S4hODtlmPae695+yzNfUub4AjrAAAAEIBDMKWdudLb+7AriZjMEir6Qr+
+JUw6SG5KjiAi3nYymFRqr7tRGwUTkvX1Q64lMV7BgJ1Ch9qW6J11g4H6vtbGxLc=
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key19
@@ -0,0 +1,4 @@
+AAAABwYFK4EEACMAAACFBAFRn1tqa8+H8gsEY6SDyUIg7L0ssJAePQcx1ADEbqorSXraND7BecWW
+fdzB/l3KbHXcFWAySIEhHJsxiaZEkoiOlQHKvTgw4WVzThQ09xcPBxP01R2Z29jRvoR6dh0vwCt6
+/Yw8Bt5DkafIV3fhrGSQcAPHNomub9yKoSrntNzgPBPZKQAAAEIBBeO8oosJMamU5b7R5LVAkl+J
+WEpiwFKGyK9svHBF5xbBy5HSFxt0tKXCzkhaJhu/tXGyeSedFATSTznrUR71tYU=
--- a/security/nss/cmd/bltest/tests/ecdsa/key2
+++ b/security/nss/cmd/bltest/tests/ecdsa/key2
@@ -1,5 +1,1 @@
-AAAABwYFK4EEACMAAACFBAHLMSpMFVyG6mXE7SZ5O5Bwv4d8/QiAB3BzpXkyrU1W
-jJ9O9uOYTXM+cFtF5v56+LsI4yGkaAl9+RF6lFPjrhpIswCmBmEqMBgZpjoz38my
-nLHBI9MaFF8AHkRQwD3LJLo4eSZHOVkdIvDYLwicdlgr0zD3Nf76/HB1+0DkBGqE
-MyG22gAAAEIAFah7z179UbqqdH68pzdZsP1ChXjtYZ11rBM0+HP7yLirxH3ahKTt
-DjsY19GEjz4gKsaLfLiQ1/Dp+VKVLcBKpk0=
+AAAACgYIKoZIzj0DAQcAAABBBGVG3cGzcX5hDchx/w8PZLuMMc2P6qlhzbfzWvtxVTkEd7MQ9deN7hIlyVUYk198Q2PQIRTiWtRVYA7bRwIlRCIAAAAggL+HbIocY2czS/UJE8y5lYKptWxLz7l2nkDCicqb3BM=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key20
@@ -0,0 +1,4 @@
+AAAABwYFK4EEACMAAACFBAAO18BJbSwNGxuZotCaEE2ZRutYNruikxsjqbXE5WtbyED65bllWUzD
+iCi9kn+r7avAL/ld+qDoTme1Pmzt+BSreQBBird/RGH5XIDuge8UQjnNMUPu6Iu4/j4DcoDuewtG
+O2y44isoGdRvOc3Iw9jQULJ5VtJtuCMmsIleglJ9gjAO9QAAAEIBPQClifuzZvzcrw4Hahu1UH3o
+A1m6xnJUK9JL8B/tZmUCdUwBevXHQ1xIajGxVka1DnYC7KzfgoqTJQhZnmejCOY=
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key3
@@ -0,0 +1,1 @@
+AAAACgYIKoZIzj0DAQcAAABBBNgKE5o33CAcWXYgyE967xgyVCx/Ny3T+46oDsNoGguKssq2oVqlhpDwJeuwcSWjFUADuZKdpfGsfsCZwoG2oTsAAAAgc1T8iAilEQPJwL3QLVoSYH+gj9WyaMIzlEXb/BdDXHA=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key4
@@ -0,0 +1,1 @@
+AAAACgYIKoZIzj0DAQcAAABBBF5bq/2D0xa1ImD8HKoGiWLNU2p7HOegQcYVROWRQQyzFl3UOtrjQVHsef4oKfo8G3eHWAJRVc+iuLyvGOPQXl8AAAAgjzwuwj9STrQmn1vaUjll1jDQe6K/cH0F2IbIuFImXgQ=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key5
@@ -0,0 +1,1 @@
+AAAACgYIKoZIzj0DAQcAAABBBFB50pPWkcrENLddIYxsb/1DyEEFqk+k3NODT7NfrgDPmP+rgdYQS8dpSTDMLio+lS9BWAHEXPLJpY9RuSkjlD8AAAAgP9tI3QDXD5JFPCNfxYRSiQCsvwH+rKefnaKPUOBqIcM=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key6
@@ -0,0 +1,1 @@
+AAAACgYIKoZIzj0DAQcAAABBBMaKRuPseagu0jdyGJMGK/v/R3hGN2Jgsx0nLOKxDQTjD96BClG7fFOf4KlWY5+SVvIa+ySmH95oOEvlvFw/O7QAAAAg9pRcgToGhu2rwCf97g7rWxMv8ZM+nn7KhN1ChI25xuU=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key7
@@ -0,0 +1,3 @@
+AAAABwYFK4EEACIAAABhBLWMJG3t4khPYcsl3H492rAqukJ1RqJm27pqpN54rFGGr2VDwOfqb9tM
+ninq8IyOh42eaaVOEPXXu4Q/ATWBEfrbTRBjTpzAE2SSPuQma0lMq0RSVECCgdBOKIhB0H6VxAAA
+ADA3WPjUaMWCS9E5KbVDrEcf5CV5tCNNWJQkwjsAyALMCiXJqRVXwbq42WMuaELMW+g=
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key8
@@ -0,0 +1,3 @@
+AAAABwYFK4EEACIAAABhBFAOD4kkHmLG2ASSN8n3K/R/ZNOKl8egsJUv+l2KNicOjOJqwTnhi1gN
+0gFX6tV0ZN9IEzj48bvMG3T9goEptgk5GWVMZv4tbsctnWzO6xEOD3szB0rWc+0Gdc9ZNxVuWQAA
+ADBMZ8FYtBjL0iyvCuK3sv7SKqjPBlRap0IhzlhGq8yROlBNj9O9T+SbVPqSGg4dca0=
new file mode 100644
--- /dev/null
+++ b/security/nss/cmd/bltest/tests/ecdsa/key9
@@ -0,0 +1,3 @@
+AAAABwYFK4EEACIAAABhBHwN4O5s8oMHEZRW9Cw25pBY1wN3aEA1FhY+YB6pGuRWW2gyR8gER0LJ
+iaL678GU9dRO6M3vqtxUXtmS/f3RkvsV/kcQa5tod5G7EPGzcnnhxB4jQ7s+eVtDEE3LQRm54AAA
+ADA4ahA2Ems2zcznoW2Ogdv9XOTfuVU5cx7RvcygOqljsXs7kMIiK0g7ChP9AgRcmmM=
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext1
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext1
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+70+lqMS2yiBPPask+j3Iru0I+CBps0dkxKYv9wkKN/0=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext10
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext10
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+O7xOr9dtXLrVOUwnoZuOamJoksOCu/AJQl7vnM5nKBrG+MiyB6tT0QinvJf/V/Dg
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext11
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext11
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+2Ea3W3p+4T9F8jQ3u1sB08h45Icn0g0XZdAkqkZAl8C+bNRt7HFD2yelVjO1n2++
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext12
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext12
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+7tI6LK4h3omU9JIsy3YQ5D35Q6bvA7SSHC5dfr7HRHVBO0aHG8LvB/MmUeSKC1Lx
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext13
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext13
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+V3J3ZsDxwglKTddpj0cZb+iDqOyJ6GeQqJAkPW9bFwAmsD2UVBntvKR4kQsk7CQR
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext15
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext15
@@ -1,1 +1,2 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+ASGhzaNwWzEVNsr0G7vLgzHLmwanZp/58qj/yrcC711bn6cAzVnm0yD7klFyypW55PmE07T0b45D
+0hMzO3URX8kY
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext16
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext16
@@ -1,1 +1,2 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+ACU0to4fsw+gcz5fwzPuRGxbnh1wk0kNPbk09Bg7zarJb/0SnZf0RL/JciIZXS0mfZwBprfYcLss
+g5E09EiLyYPE
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext17
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext17
@@ -1,1 +1,2 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+AHFZ+2hr7LE73SrITnxBdRIAQuIhe9sJv2IjH6mZ63hz7B0lUZBRq1L1UZYCMiJcySQE72fm58qD
+HBTp4TZFT4i6
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext18
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext18
@@ -1,1 +1,2 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+ASwIYky567pL6O3quwElPDfKVZO/7mDgYfyTfDXPdgzshZy5m0m9QeBHyt+nR0tHHEQGGm+fyhD2
+j3ymdPPQ7vhO
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext19
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext19
@@ -1,1 +1,2 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+AeL/vCfQJqo6G0cKf4JP85zORGd2wDp47NI8jilqs3hPzN0DRV322Kgwpn1Wm819FP6zOiMgtw+p
+lKiA2AoX9vA1
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext2
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext2
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+SfpXl5Llf4iquhkXy4lshoIqoSbnaBB+PxGW17x99sU=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext20
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext20
@@ -1,1 +1,2 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+ANDejmBr18vwOj5bRuaVvsbCJsJUnsY0h7meGKBmehWiaSzEy+Uk3frUxD+jFwB3QJ+y00mfjJHk
+gcQTb1dNKtk7
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext3
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext3
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+5zRhgEl3WocyPf53pVA08iC9rhwsXNu6esNgfNOd09A=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext4
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext4
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+CbvtewBEVhbGdugFywOCh7YyM/99PoGsqgmM100hFok=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext5
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext5
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+cROiGlKZaGbm1nfrM2L8YXXX9l+h4IkQtI5ovo66Vm0=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext6
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext6
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+nBtD5rFYJ+4NmPw6qfeAxfcjl+UtcHJ6AtXgnC04Kck=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext8
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext8
@@ -1,1 +1,2 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+Mxl2AoAoODkjI3BRRJhERyaYl3mGdClyckJTOYg3ApBlAXdQY2mQJmOXQDiUiXhyGEIyEWWRcJJI
+GQ==
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/plaintext9
+++ b/security/nss/cmd/bltest/tests/ecdsa/plaintext9
@@ -1,1 +1,1 @@
-qZk+NkcGgWq6PiVxeFDCbJzQ2J0=
+cxSK1gNt4qh6Q/YMYTPGb06yvMmOA8CdF2Fe+TA3KWu9Sma4Uw60bsNiUXVfGJaG
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed1
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed1
@@ -1,1 +1,1 @@
-aHpm2QZI+ZOGfhIfTd+d2wEgVYQ=
++uIjgHPQK45HEoD6scaacINDhCSlavy/LOQstFOjA9I=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed10
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed10
@@ -1,1 +1,1 @@
-fjIzMWJpdHNPZlRleHQwMTAyMDMwNDA1MDYwNzA=
+Ee3Cm8MeIculRkFplZnPEv7gBCPRTq+C9g55xgfw6XlEDgwQ2O4sW0QBpSbV1bkE
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed11
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed11
@@ -1,1 +1,1 @@
-fjIzMWJpdHNPZlRleHQwMTAyMDMwNDA1MDYwNzA=
+eacQrgxAKTRE67OWEAIn1PiBGMjr1MHrAvugBZGvWLWj0qpJK7ysGrP5AUU5knA6
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed12
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed12
@@ -1,1 +1,1 @@
-/jI1NmJpdHNPZlRleHQwMTAyMDMwNDA1MDYwNzA4MDk=
+OZyWwfeQNAwuldqcxrvG6D1U53pto7nNeBtFOxWApMAsfy8znVlNJ392yPOYFXR1
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed13
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed13
@@ -1,1 +1,1 @@
-ATI4MWJpdHNPZlRleHQwMTAyMDMwNDA1MDYwNzA4MDkwYTBi
+E5M7txkzv3/JJzgkWgYkjSb+auo3diREUK6QtEe5tUovRinB9D62rwsUnYO4Zh9h
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed15
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed15
@@ -1,1 +1,2 @@
-/jM4NGJpdHNPZlRleHQwMTAyMDMwNDA1MDYwNzA4MDkwYTBiMGMwZDBlMGYxMDEx
+AWTDEcA39i86TZu4RjBbkDE4Bo0PcPsy3Vs7uSpfEpzG1za21tk7778bg0zjh+Cn40uqfG0F47do
+hMKNtshEivBN
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed16
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed16
@@ -1,1 +1,2 @@
-fjQwN2JpdHNPZlRleHQwMTAyMDMwNDA1MDYwNzA4MDkwYTBiMGMwZDBlMGYxMDExMTIx
+ASnw0hIQ2FzntL2vHcZrIFWeJHVhlPlIBQformN6nv8vzp7a9/hqIudPY/uHv001e9ryEuczmG36
+cgjmxOTEca2X
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed17
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed17
@@ -1,1 +1,2 @@
-fjQwN2JpdHNPZlRleHQwMTAyMDMwNDA1MDYwNzA4MDkwYTBiMGMwZDBlMGYxMDExMTIx
+AKdGyvpvfzX4pI3TBFsTbVyLLlOoVQXpvz8xYLGB6n/bMEe3pLpsb8lRCbVuS/9agXzY3XZN27PX
+tf3CclGx4rbW
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed18
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed18
@@ -1,2 +1,2 @@
-PjU2NmJpdHNPZlRleHQwMDAxMDIwMzA0MDUwNjA3MDgwOTBhMGIwYzBkMGUwZjEwMTExMjEz
-MTQxNTE2MTcxODE5MWExYjE=
+AUr+RxF9oCVYkIwufo/WMRy6x4ftZlTojmEwUIQ3XS/tEtsqOJmvSKB304R1P6hpdghAYUVQ5Lf0
+P8BheUDuOLE5
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed19
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed19
@@ -1,2 +1,2 @@
-PjU2NmJpdHNPZlRleHQwMDAxMDIwMzA0MDUwNjA3MDgwOTBhMGIwYzBkMGUwZjEwMTExMjEz
-MTQxNTE2MTcxODE5MWExYjE=
+AJWl5lI4w6SWNnOAIM5JG2/zJlLmG8KtuVy0LALG0geNHjNuh6WOjmBMOB0Ru14m/nvVp/AOXOaD
+NXbhZaCuuwoX
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed2
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed2
@@ -1,1 +1,1 @@
-aHpm2QZI+ZOGfhIfTd+d2wEgVYQ=
+a5F2DtjzM797shbFrp7g4O/tBT9jdtljEWKhnldZOak=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed20
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed20
@@ -1,2 +1,2 @@
-/jUyMGJpdHNPZlRleHQwMDAxMDIwMzA0MDUwNjA3MDgwOTBhMGIwYzBkMGUwZjEwMTExMjEz
-MTQxNTE2MTcxODE=
+ABJ+5ak5x4Npkzxcq1hRKJjhKjIy4jVkVDhgZ9+3p+1ItpbJxsWMlVD835yQyeW/lFOENrcLo2Qw
+RyNEPndnPG5D
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed3
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed3
@@ -1,1 +1,1 @@
-aHpm2QZI+ZOGfhIfTd+d2wEgVYQ=
+IV8M637yzsL+KYLyALkf92O+euGBw9PrMopiHcb/SJU=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed4
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed4
@@ -1,1 +1,1 @@
-aHpm2QZI+ZOGfhIfTd+d2wEgVYQ=
+ZxclhYSFGNGLM7tZ1Z5k39ZVXiWCnd/PnsVUza3O/WQ=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed5
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed5
@@ -1,1 +1,1 @@
-aHpm2QZI+ZOGfhIfTd+d2wEgVYQ=
+gbPop+RXoXIuYeNAb+IGgLwdTE1AnhG7LsPkfETvayQ=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed6
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed6
@@ -1,1 +1,1 @@
-/jE5MmJpdHNPZlRleHQwMDAwMDAwMDAw
+WAHT3XYcVbT0ZB5MxfUo9636BVA4JkEKcYBFhQvIWkQ=
\ No newline at end of file
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed8
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed8
@@ -1,1 +1,1 @@
-/jIyNGJpdHNPZlRleHQwMDAwMDAwMDAwMDAwMA==
+LLGTvUo3kUNR3qdAVcvKCDEFPT/ialozxy0RY3aJJxkJJ3NpuXl3l7v6dUo51/qg
--- a/security/nss/cmd/bltest/tests/ecdsa/sigseed9
+++ b/security/nss/cmd/bltest/tests/ecdsa/sigseed9
@@ -1,1 +1,1 @@
-/jIyNGJpdHNPZlRleHQwMDAwMDAwMDAwMDAwMA==
+3pch8aH8+9zNR8+8P8gS0ftX0dqTkzExF1x4TkKfnYUfttwtkU0D1ge62Hg0tiVr
--- a/security/nss/cmd/crlutil/crlutil.c
+++ b/security/nss/cmd/crlutil/crlutil.c
@@ -1039,17 +1039,20 @@ main(int argc, char **argv)
         readonly = PR_TRUE;
     }
 
     PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
 
     PK11_SetPasswordFunc(SECU_GetModulePassword);
 
     if (showFileCRL) {
-        NSS_NoDB_Init(NULL);
+        rv = NSS_NoDB_Init(NULL);
+        if (rv != SECSuccess) {
+            goto loser;
+        }
     } else {
         secstatus = NSS_Initialize(SECU_ConfigDirectory(NULL), dbPrefix, dbPrefix,
                                    "secmod.db", readonly ? NSS_INIT_READONLY : 0);
         if (secstatus != SECSuccess) {
             SECU_PrintPRandOSError(progName);
             rv = SECFailure;
             goto loser;
         }
--- a/security/nss/cmd/makepqg/makepqg.c
+++ b/security/nss/cmd/makepqg/makepqg.c
@@ -290,17 +290,19 @@ main(int argc, char **argv)
     if (rv != 0) {
         return 1;
     }
 
     if (outFile == NULL) {
         outFile = stdout;
     }
 
-    NSS_NoDB_Init(NULL);
+    if (NSS_NoDB_Init(NULL) != SECSuccess) {
+        return 1;
+    }
 
     if (keySizeInBits > 1024 || qSizeInBits != 0) {
         rv = PK11_PQG_ParamGenV2((unsigned)keySizeInBits,
                                  (unsigned)qSizeInBits, (unsigned)(g /
                                                                    8),
                                  &pqgParams, &pqgVerify);
     } else if (g) {
         rv = PK11_PQG_ParamGenSeedLen((unsigned)j, (unsigned)(g / 8),
@@ -335,15 +337,18 @@ main(int argc, char **argv)
     if (passed != SECSuccess) {
         fprintf(stderr, "%s: PQG parameters failed verification.\n", progName);
         goto loser;
     }
     fprintf(stderr, "%s: PQG parameters passed verification.\n", progName);
 
     PK11_PQG_DestroyParams(pqgParams);
     PK11_PQG_DestroyVerify(pqgVerify);
+    if (NSS_Shutdown() != SECSuccess) {
+        return 1;
+    }
     return 0;
 
 loser:
     PK11_PQG_DestroyParams(pqgParams);
     PK11_PQG_DestroyVerify(pqgVerify);
     return 1;
 }
--- a/security/nss/cmd/pk11gcmtest/pk11gcmtest.c
+++ b/security/nss/cmd/pk11gcmtest/pk11gcmtest.c
@@ -434,27 +434,32 @@ aes_gcm_kat(const char *respfn)
     printf("PASS\n");
 loser:
     fclose(aesresp);
 }
 
 int
 main(int argc, char **argv)
 {
-    if (argc < 2)
-        exit(1);
+    if (argc < 2) {
+        return 1;
+    }
 
-    NSS_NoDB_Init(NULL);
+    if (NSS_NoDB_Init(NULL) != SECSuccess) {
+        return 1;
+    }
 
     /*************/
     /*   AES     */
     /*************/
     if (strcmp(argv[1], "aes") == 0) {
         /* argv[2]=kat argv[3]=gcm argv[4]=<test name>.rsp */
         if (strcmp(argv[2], "kat") == 0) {
             /* Known Answer Test (KAT) */
             aes_gcm_kat(argv[4]);
         }
     }
 
-    NSS_Shutdown();
+    if (NSS_Shutdown() != SECSuccess) {
+        return 1;
+    }
     return 0;
 }
--- a/security/nss/cmd/sdbthreadtst/sdbthreadtst.c
+++ b/security/nss/cmd/sdbthreadtst/sdbthreadtst.c
@@ -191,23 +191,27 @@ main(int argc, char **argv)
     for (i = 0; i < THREAD_COUNT; i++) {
         if (thread[i] == NULL) {
             continue;
         }
         status = PR_JoinThread(thread[i]);
         if (status != PR_SUCCESS) {
             ERROR++;
             fprintf(stderr,
-                    "PR_CreateThread filed iteration %d, %s]n", i,
+                    "PR_CreateThread filed iteration %d, %s\n", i,
                     PORT_ErrorToString(PORT_GetError()));
         }
     }
+    if (NSS_Shutdown() != SECSuccess) {
+        ERROR++;
+        fprintf(stderr, "NSS_Shutdown failed: %s\n",
+                PORT_ErrorToString(PORT_GetError()));
+    }
     printf("%d failures and %d errors found\n", FAILED, ERROR);
     /* clean up */
-    NSS_Shutdown();
     if (FAILED) {
         exit(1);
     }
     if (ERROR) {
         exit(2);
     }
     exit(0);
 }
--- a/security/nss/cmd/ssltap/ssltap.c
+++ b/security/nss/cmd/ssltap/ssltap.c
@@ -2575,11 +2575,13 @@ main(int argc, char *argv[])
         flush_stream(&serverstream);
         /* Connection is closed, so reset the current cipher */
         currentcipher = 0;
         c_count++;
         PR_fprintf(PR_STDERR, "Connection %d Complete [%s]\n", c_count,
                    get_time_string());
     } while (looparound); /* accept connection and process it. */
     PR_Close(s_rend);
-    NSS_Shutdown();
+    if (NSS_Shutdown() != SECSuccess) {
+        return 1;
+    }
     return 0;
 }
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -131,16 +131,17 @@
     'nss_include_dir%': '/usr/include/nss',
     'only_dev_random%': 1,
     'disable_fips%': 1,
     'mozpkix_only%': 0,
     'mozilla_central%': 0,
     'coverage%': 0,
     'softfp_cflags%': '',
     'enable_draft_hpke%': 0,
+    'force_integrated_as%': 0,
   },
   'target_defaults': {
     # Settings specific to targets should go here.
     # This is mostly for linking to libraries.
     'variables': {
       'mapfile%': '',
       'static_libs%': 0,
       'debug_optimization_level%': '0',
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/cpputil/databuffer.h
+++ b/security/nss/cpputil/databuffer.h
@@ -27,16 +27,25 @@ class DataBuffer {
   ~DataBuffer() { delete[] data_; }
 
   DataBuffer& operator=(const DataBuffer& other) {
     if (&other != this) {
       Assign(other);
     }
     return *this;
   }
+  DataBuffer& operator=(DataBuffer&& other) {
+    if (this == &other) {
+      data_ = other.data_;
+      len_ = other.len_;
+      other.data_ = nullptr;
+      other.len_ = 0;
+    }
+    return *this;
+  }
 
   void Allocate(size_t l) {
     delete[] data_;
     data_ = new uint8_t[l ? l : 1]();  // Don't depend on new [0].
     len_ = l;
   }
 
   void Truncate(size_t l) { len_ = (std::min)(len_, l); }
--- a/security/nss/cpputil/nss_scoped_ptrs.h
+++ b/security/nss/cpputil/nss_scoped_ptrs.h
@@ -3,18 +3,20 @@
 /* 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 nss_scoped_ptrs_h__
 #define nss_scoped_ptrs_h__
 
 #include <memory>
+
 #include "cert.h"
 #include "keyhi.h"
+#include "nss.h"
 #include "p12.h"
 #include "pk11hpke.h"
 #include "pk11pqg.h"
 #include "pk11pub.h"
 #include "pkcs11uri.h"
 #include "secmod.h"
 
 struct ScopedDelete {
@@ -49,16 +51,17 @@ struct ScopedDelete {
   void operator()(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); }
   void operator()(SECKEYPrivateKeyList* list) {
     SECKEY_DestroyPrivateKeyList(list);
   }
   void operator()(SECMODModule* module) { SECMOD_DestroyModule(module); }
   void operator()(SEC_PKCS12DecoderContext* dcx) {
     SEC_PKCS12DecoderFinish(dcx);
   }
+  void operator()(NSSInitContext* init) { NSS_ShutdownContext(init); }
 };
 
 template <class T>
 struct ScopedMaybeDelete {
   void operator()(T* ptr) {
     if (ptr) {
       ScopedDelete del;
       del(ptr);
@@ -70,16 +73,17 @@ struct ScopedMaybeDelete {
 
 SCOPED(CERTCertList);
 SCOPED(CERTCertificate);
 SCOPED(CERTCertificateList);
 SCOPED(CERTDistNames);
 SCOPED(CERTName);
 SCOPED(CERTSubjectPublicKeyInfo);
 SCOPED(HpkeContext);
+SCOPED(NSSInitContext);
 SCOPED(PK11Context);
 SCOPED(PK11GenericObject);
 SCOPED(PK11SlotInfo);
 SCOPED(PK11SlotList);
 SCOPED(PK11SymKey);
 SCOPED(PK11URI);
 SCOPED(PLArenaPool);
 SCOPED(PQGParams);
new file mode 100644
--- /dev/null
+++ b/security/nss/doc/rst/releases/nss_3_68_1.rst
@@ -0,0 +1,62 @@
+.. _mozilla_projects_nss_nss_3_68_1_release_notes:
+
+NSS 3.68.1 release notes
+========================
+
+`Introduction <#introduction>`__
+--------------------------------
+
+.. container::
+
+   Network Security Services (NSS) 3.68.1 (ESR) was released on **1 December 2021**.
+
+   **This release contains an important security fix for CVE-2021-43527:**
+
+   https://www.mozilla.org/en-US/security/advisories/mfsa2021-51/
+
+.. _distribution_information:
+
+`Distribution Information <#distribution_information>`__
+--------------------------------------------------------
+
+.. container::
+
+   The HG tag is NSS_3_68_1_RTM. NSS 3.68.1 requires NSPR 4.32 or newer.
+
+   NSS 3.68.1 source distributions are available on ftp.mozilla.org for secure HTTPS download:
+
+   -  Source tarballs:
+      https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_68_1_RTM/src/
+
+   Other releases are available :ref:`mozilla_projects_nss_releases`.
+
+.. _changes_in_nss_3.68.1:
+
+`Changes in NSS 3.68.1 <#changes_in_nss_3.68.1>`__
+----------------------------------------------------
+
+.. container::
+
+   - Bug 1735028 - Check for missing signedData field.
+   - Bug 1737470 - Ensure DER encoded signatures are within size limits.
+
+
+`Compatibility <#compatibility>`__
+----------------------------------
+
+.. container::
+
+   NSS 3.68.1 shared libraries are backwards-compatible with all older NSS 3.x shared
+   libraries. A program linked with older NSS 3.x shared libraries will work with
+   this new version of the shared libraries without recompiling or
+   relinking. Furthermore, applications that restrict their use of NSS APIs to the
+   functions listed in NSS Public Functions will remain compatible with future
+   versions of the NSS shared libraries.
+
+`Feedback <#feedback>`__
+------------------------
+
+.. container::
+
+   Bugs discovered should be reported by filing a bug report on
+   `bugzilla.mozilla.org <https://bugzilla.mozilla.org/enter_bug.cgi?product=NSS>`__ (product NSS).
new file mode 100644
--- /dev/null
+++ b/security/nss/doc/rst/releases/nss_3_68_2.rst
@@ -0,0 +1,57 @@
+.. _mozilla_projects_nss_nss_3_68_2_release_notes:
+
+NSS 3.68.2 (ESR) release notes
+==============================
+
+`Introduction <#introduction>`__
+--------------------------------
+
+.. container::
+
+   Network Security Services (NSS) 3.68.2 (ESR) was released on **15 December 2021**.
+
+.. _distribution_information:
+
+`Distribution Information <#distribution_information>`__
+--------------------------------------------------------
+
+.. container::
+
+   The HG tag is NSS_3_68_2_RTM. NSS 3.68.2 requires NSPR 4.32 or newer.
+
+   NSS 3.68.2 source distributions are available on ftp.mozilla.org for secure HTTPS download:
+
+   -  Source tarballs:
+      https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_68_2_RTM/src/
+
+   Other releases are available :ref:`mozilla_projects_nss_releases`.
+
+.. _changes_in_nss_3.68.2:
+
+`Changes in NSS 3.68.2 <#changes_in_nss_3.68.2>`__
+----------------------------------------------------
+
+.. container::
+
+   - Bug 966856 - Add SHA-2 support to mozilla::pkix's OCSP implementation.
+
+
+`Compatibility <#compatibility>`__
+----------------------------------
+
+.. container::
+
+   NSS 3.68.2 shared libraries are backwards-compatible with all older NSS 3.x shared
+   libraries. A program linked with older NSS 3.x shared libraries will work with
+   this new version of the shared libraries without recompiling or
+   relinking. Furthermore, applications that restrict their use of NSS APIs to the
+   functions listed in NSS Public Functions will remain compatible with future
+   versions of the NSS shared libraries.
+
+`Feedback <#feedback>`__
+------------------------
+
+.. container::
+
+   Bugs discovered should be reported by filing a bug report on
+   `bugzilla.mozilla.org <https://bugzilla.mozilla.org/enter_bug.cgi?product=NSS>`__ (product NSS).
new file mode 100644
--- /dev/null
+++ b/security/nss/doc/rst/releases/nss_3_72_1.rst
@@ -0,0 +1,57 @@
+.. _mozilla_projects_nss_nss_3_72_1_release_notes:
+
+NSS 3.72.1 release notes
+========================
+
+`Introduction <#introduction>`__
+--------------------------------
+
+.. container::
+
+   Network Security Services (NSS) 3.72.1 was released on **15 December 2021**.
+
+.. _distribution_information:
+
+`Distribution Information <#distribution_information>`__
+--------------------------------------------------------
+
+.. container::
+
+   The HG tag is NSS_3_72_1_RTM. NSS 3.72.1 requires NSPR 4.32 or newer.
+
+   NSS 3.72.1 source distributions are available on ftp.mozilla.org for secure HTTPS download:
+
+   -  Source tarballs:
+      https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_72_1_RTM/src/
+
+   Other releases are available :ref:`mozilla_projects_nss_releases`.
+
+.. _changes_in_nss_3.72.1:
+
+`Changes in NSS 3.72.1 <#changes_in_nss_3.72.1>`__
+----------------------------------------------------
+
+.. container::
+
+   - Bug 966856 - Add SHA-2 support to mozilla::pkix's OCSP implementation.
+
+
+`Compatibility <#compatibility>`__
+----------------------------------
+
+.. container::
+
+   NSS 3.72.1 shared libraries are backwards-compatible with all older NSS 3.x shared
+   libraries. A program linked with older NSS 3.x shared libraries will work with
+   this new version of the shared libraries without recompiling or
+   relinking. Furthermore, applications that restrict their use of NSS APIs to the
+   functions listed in NSS Public Functions will remain compatible with future
+   versions of the NSS shared libraries.
+
+`Feedback <#feedback>`__
+------------------------
+
+.. container::
+
+   Bugs discovered should be reported by filing a bug report on
+   `bugzilla.mozilla.org <https://bugzilla.mozilla.org/enter_bug.cgi?product=NSS>`__ (product NSS).
new file mode 100644
--- /dev/null
+++ b/security/nss/doc/rst/releases/nss_3_73.rst
@@ -0,0 +1,65 @@
+.. _mozilla_projects_nss_nss_3_73_release_notes:
+
+NSS 3.73 release notes
+======================
+
+`Introduction <#introduction>`__
+--------------------------------
+
+.. container::
+
+   Network Security Services (NSS) 3.73 was released on **1 December 2021**.
+
+   **This release contains an important security fix for CVE-2021-43527:**
+
+   https://www.mozilla.org/en-US/security/advisories/mfsa2021-51/
+
+.. _distribution_information:
+
+`Distribution Information <#distribution_information>`__
+--------------------------------------------------------
+
+.. container::
+
+   The HG tag is NSS_3_73_RTM. NSS 3.73 requires NSPR 4.32 or newer.
+
+   NSS 3.73 source distributions are available on ftp.mozilla.org for secure HTTPS download:
+
+   -  Source tarballs:
+      https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_73_RTM/src/
+
+   Other releases are available :ref:`mozilla_projects_nss_releases`.
+
+.. _changes_in_nss_3.73:
+
+`Changes in NSS 3.73 <#changes_in_nss_3.73>`__
+----------------------------------------------------
+
+.. container::
+
+   - Bug 1735028 - Check for missing signedData field.
+   - Bug 1737470 - Ensure DER encoded signatures are within size limits.
+   - Bug 1729550 - NSS needs FiPS 140-3 version indicators.
+   - Bug 1692132 - pkix_CacheCert_Lookup doesn't return cached certs.
+   - Bug 1738600 - Sunset Coverity from NSS.
+
+
+`Compatibility <#compatibility>`__
+----------------------------------
+
+.. container::
+
+   NSS 3.73 shared libraries are backwards-compatible with all older NSS 3.x shared
+   libraries. A program linked with older NSS 3.x shared libraries will work with
+   this new version of the shared libraries without recompiling or
+   relinking. Furthermore, applications that restrict their use of NSS APIs to the
+   functions listed in NSS Public Functions will remain compatible with future
+   versions of the NSS shared libraries.
+
+`Feedback <#feedback>`__
+------------------------
+
+.. container::
+
+   Bugs discovered should be reported by filing a bug report on
+   `bugzilla.mozilla.org <https://bugzilla.mozilla.org/enter_bug.cgi?product=NSS>`__ (product NSS).
new file mode 100644
--- /dev/null
+++ b/security/nss/doc/rst/releases/nss_3_73_1.rst
@@ -0,0 +1,57 @@
+.. _mozilla_projects_nss_nss_3_73_1_release_notes:
+
+NSS 3.73.1 release notes
+========================
+
+`Introduction <#introduction>`__
+--------------------------------
+
+.. container::
+
+   Network Security Services (NSS) 3.73.1 was released on **15 December 2021**.
+
+.. _distribution_information:
+
+`Distribution Information <#distribution_information>`__
+--------------------------------------------------------
+
+.. container::
+
+   The HG tag is NSS_3_73_1_RTM. NSS 3.73.1 requires NSPR 4.32 or newer.
+
+   NSS 3.73.1 source distributions are available on ftp.mozilla.org for secure HTTPS download:
+
+   -  Source tarballs:
+      https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_73_1_RTM/src/
+
+   Other releases are available :ref:`mozilla_projects_nss_releases`.
+
+.. _changes_in_nss_3.73.1:
+
+`Changes in NSS 3.73.1 <#changes_in_nss_3.73.1>`__
+----------------------------------------------------
+
+.. container::
+
+   - Bug 966856 - Add SHA-2 support to mozilla::pkix's OCSP implementation.
+
+
+`Compatibility <#compatibility>`__
+----------------------------------
+
+.. container::
+
+   NSS 3.73.1 shared libraries are backwards-compatible with all older NSS 3.x shared
+   libraries. A program linked with older NSS 3.x shared libraries will work with
+   this new version of the shared libraries without recompiling or
+   relinking. Furthermore, applications that restrict their use of NSS APIs to the
+   functions listed in NSS Public Functions will remain compatible with future
+   versions of the NSS shared libraries.
+
+`Feedback <#feedback>`__
+------------------------
+
+.. container::
+
+   Bugs discovered should be reported by filing a bug report on
+   `bugzilla.mozilla.org <https://bugzilla.mozilla.org/enter_bug.cgi?product=NSS>`__ (product NSS).
new file mode 100644
--- /dev/null
+++ b/security/nss/doc/rst/releases/nss_3_74.rst
@@ -0,0 +1,77 @@
+.. _mozilla_projects_nss_nss_3_74_release_notes:
+
+NSS 3.74 release notes
+======================
+
+`Introduction <#introduction>`__
+--------------------------------
+
+.. container::
+
+   Network Security Services (NSS) 3.74 was released on **6 January 2022**.
+
+
+.. _distribution_information:
+
+`Distribution Information <#distribution_information>`__
+--------------------------------------------------------
+
+.. container::
+
+   The HG tag is NSS_3_74_RTM. NSS 3.74 requires NSPR 4.32 or newer.
+
+   NSS 3.74 source distributions are available on ftp.mozilla.org for secure HTTPS download:
+
+   -  Source tarballs:
+      https://ftp.mozilla.org/pub/mozilla.org/security/nss/releases/NSS_3_74_RTM/src/
+
+   Other releases are available :ref:`mozilla_projects_nss_releases`.
+
+.. _changes_in_nss_3.74:
+
+`Changes in NSS 3.74 <#changes_in_nss_3.74>`__
+----------------------------------------------------
+
+.. container::
+
+   - Bug 966856 - mozilla::pkix: support SHA-2 hashes in CertIDs in OCSP responses.
+   - Bug 1553612 - Ensure clients offer consistent ciphersuites after HRR.
+   - Bug 1721426 - NSS does not properly restrict server keys based on policy.
+   - Bug 1733003 - Set nssckbi version number to 2.54.
+   - Bug 1735407 - Replace Google Trust Services LLC (GTS) R4 root certificate in NSS.
+   - Bug 1735407 - Replace Google Trust Services LLC (GTS) R3 root certificate in NSS.
+   - Bug 1735407 - Replace Google Trust Services LLC (GTS) R2 root certificate in NSS.
+   - Bug 1735407 - Replace Google Trust Services LLC (GTS) R1 root certificate in NSS.
+   - Bug 1735407 - Replace GlobalSign ECC Root CA R4 in NSS.
+   - Bug 1733560 - Remove Expired Root Certificates from NSS - DST Root CA X3.
+   - Bug 1740807 - Remove Expiring Cybertrust Global Root and GlobalSign root certificates from NSS.
+   - Bug 1741930 - Add renewed Autoridad de Certificacion Firmaprofesional CIF A62634068 root certificate to NSS.
+   - Bug 1740095 - Add iTrusChina ECC root certificate to NSS.
+   - Bug 1740095 - Add iTrusChina RSA root certificate to NSS.
+   - Bug 1738805 - Add ISRG Root X2 root certificate to NSS.
+   - Bug 1733012 - Add Chunghwa Telecom's HiPKI Root CA - G1 root certificate to NSS.
+   - Bug 1738028 - Avoid a clang 13 unused variable warning in opt build.
+   - Bug 1735028 - Check for missing signedData field.
+   - Bug 1737470 - Ensure DER encoded signatures are within size limits.
+
+
+
+`Compatibility <#compatibility>`__
+----------------------------------
+
+.. container::
+
+   NSS 3.74 shared libraries are backwards-compatible with all older NSS 3.x shared
+   libraries. A program linked with older NSS 3.x shared libraries will work with
+   this new version of the shared libraries without recompiling or
+   relinking. Furthermore, applications that restrict their use of NSS APIs to the
+   functions listed in NSS Public Functions will remain compatible with future
+   versions of the NSS shared libraries.
+
+`Feedback <#feedback>`__
+------------------------
+
+.. container::
+
+   Bugs discovered should be reported by filing a bug report on
+   `bugzilla.mozilla.org <https://bugzilla.mozilla.org/enter_bug.cgi?product=NSS>`__ (product NSS).
--- a/security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc
+++ b/security/nss/gtests/pk11_gtest/pk11_chacha20poly1305_unittest.cc
@@ -14,63 +14,51 @@
 #include "cpputil.h"
 #include "nss_scoped_ptrs.h"
 
 #include "testvectors/chachapoly-vectors.h"
 #include "gtest/gtest.h"
 
 namespace nss_test {
 
-static const CK_MECHANISM_TYPE kMech = CKM_NSS_CHACHA20_POLY1305;
-static const CK_MECHANISM_TYPE kMechXor = CKM_NSS_CHACHA20_CTR;
+static const CK_MECHANISM_TYPE kMech = CKM_CHACHA20_POLY1305;
+static const CK_MECHANISM_TYPE kMechLegacy = CKM_NSS_CHACHA20_POLY1305;
+static const CK_MECHANISM_TYPE kMechXor = CKM_CHACHA20;
+static const CK_MECHANISM_TYPE kMechXorLegacy = CKM_NSS_CHACHA20_CTR;
 // Some test data for simple tests.
 static const uint8_t kKeyData[32] = {'k'};
-static const uint8_t kCtrNonce[16] = {'c', 0, 0, 0, 'n'};
+static const uint8_t kXorParamsLegacy[16] = {'c', 0, 0, 0, 'n'};
+static const uint8_t kCounter[4] = {'c', 0};
+static const uint8_t kNonce[12] = {'n', 0};
+static const CK_CHACHA20_PARAMS kXorParams{
+    /* pBlockCounter */ const_cast<CK_BYTE_PTR>(kCounter),
+    /* blockCounterBits */ sizeof(kCounter) * 8,
+    /* pNonce */ const_cast<CK_BYTE_PTR>(kNonce),
+    /* ulNonceBits */ sizeof(kNonce) * 8,
+};
 static const uint8_t kData[16] = {'d'};
+static const uint8_t kExpectedXor[sizeof(kData)] = {
+    0xd8, 0x15, 0xd3, 0xb3, 0xe9, 0x34, 0x3b, 0x7a,
+    0x24, 0xf6, 0x5f, 0xd7, 0x95, 0x3d, 0xd3, 0x51};
+static const size_t kTagLen = 16;
 
 class Pkcs11ChaCha20Poly1305Test
     : public ::testing::TestWithParam<ChaChaTestVector> {
  public:
   void EncryptDecrypt(const ScopedPK11SymKey& key, const bool invalid_iv,
                       const bool invalid_tag, const uint8_t* data,
-                      size_t data_len, const uint8_t* aad, size_t aad_len,
-                      const uint8_t* iv, size_t iv_len,
+                      size_t data_len, CK_MECHANISM_TYPE mech, SECItem* params,
+                      std::vector<uint8_t>* nonce, std::vector<uint8_t>* aad,
                       const uint8_t* ct = nullptr, size_t ct_len = 0) {
-    // Prepare AEAD params.
-    CK_NSS_AEAD_PARAMS aead_params;
-    aead_params.pNonce = toUcharPtr(iv);
-    aead_params.ulNonceLen = iv_len;
-    aead_params.pAAD = toUcharPtr(aad);
-    aead_params.ulAADLen = aad_len;
-    aead_params.ulTagLen = 16;
-
-    SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params),
-                      sizeof(aead_params)};
-
-    // Encrypt with bad parameters (TagLen is too long).
+    std::vector<uint8_t> encrypted(data_len + kTagLen);
     unsigned int encrypted_len = 0;
-    std::vector<uint8_t> encrypted(data_len + aead_params.ulTagLen);
-    aead_params.ulTagLen = 158072;
+    // Encrypt.
     SECStatus rv =
-        PK11_Encrypt(key.get(), kMech, &params, encrypted.data(),
-                     &encrypted_len, encrypted.size(), data, data_len);
-    EXPECT_EQ(SECFailure, rv);
-    EXPECT_EQ(0U, encrypted_len);
-
-    // Encrypt with bad parameters (TagLen is too short).
-    aead_params.ulTagLen = 2;
-    rv = PK11_Encrypt(key.get(), kMech, &params, encrypted.data(),
-                      &encrypted_len, encrypted.size(), data, data_len);
-    EXPECT_EQ(SECFailure, rv);
-    EXPECT_EQ(0U, encrypted_len);
-
-    // Encrypt.
-    aead_params.ulTagLen = 16;
-    rv = PK11_Encrypt(key.get(), kMech, &params, encrypted.data(),
-                      &encrypted_len, encrypted.size(), data, data_len);
+        PK11_Encrypt(key.get(), mech, params, encrypted.data(), &encrypted_len,
+                     encrypted.size(), data, data_len);
 
     // Return if encryption failure was expected due to invalid IV.
     // Without valid ciphertext, all further tests can be skipped.
     if (invalid_iv) {
       EXPECT_EQ(rv, SECFailure);
       EXPECT_EQ(0U, encrypted_len)
           << "encrypted_len is unmodified after failure";
       return;
@@ -87,98 +75,145 @@ class Pkcs11ChaCha20Poly1305Test
                   invalid_tag);
     }
 
     // Get the *estimated* plaintext length. This value should
     // never be zero as it could lead to a NULL outPtr being
     // passed to a subsequent decryption call (for AEAD we
     // must authenticate even when the pt is zero-length).
     unsigned int decrypt_bytes_needed = 0;
-    rv = PK11_Decrypt(key.get(), kMech, &params, nullptr, &decrypt_bytes_needed,
+    rv = PK11_Decrypt(key.get(), mech, params, nullptr, &decrypt_bytes_needed,
                       0, encrypted.data(), encrypted_len);
     EXPECT_EQ(rv, SECSuccess);
     EXPECT_GT(decrypt_bytes_needed, data_len);
 
     // Now decrypt it
     std::vector<uint8_t> decrypted(decrypt_bytes_needed);
     unsigned int decrypted_len = 0;
-    rv = PK11_Decrypt(key.get(), kMech, &params, decrypted.data(),
-                      &decrypted_len, decrypted.size(), encrypted.data(),
-                      encrypted.size());
+    rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len,
+                      decrypted.size(), encrypted.data(), encrypted.size());
     EXPECT_EQ(rv, SECSuccess);
 
     // Check the plaintext.
     ASSERT_EQ(data_len, decrypted_len);
     EXPECT_TRUE(!memcmp(data, decrypted.data(), decrypted_len));
 
     // Decrypt with bogus data.
     // Skip if there's no data to modify.
     if (encrypted_len > 0) {
       decrypted_len = 0;
       std::vector<uint8_t> bogus_ciphertext(encrypted);
       bogus_ciphertext[0] ^= 0xff;
-      rv = PK11_Decrypt(key.get(), kMech, &params, decrypted.data(),
+      rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(),
                         &decrypted_len, decrypted.size(),
                         bogus_ciphertext.data(), encrypted_len);
       EXPECT_EQ(rv, SECFailure);
       EXPECT_EQ(0U, decrypted_len);
     }
 
     // Decrypt with bogus tag.
     // Skip if there's no tag to modify.
     if (encrypted_len > 0) {
       decrypted_len = 0;
       std::vector<uint8_t> bogus_tag(encrypted);
       bogus_tag[encrypted_len - 1] ^= 0xff;
-      rv = PK11_Decrypt(key.get(), kMech, &params, decrypted.data(),
+      rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(),
                         &decrypted_len, decrypted.size(), bogus_tag.data(),
                         encrypted_len);
       EXPECT_EQ(rv, SECFailure);
       EXPECT_EQ(0U, decrypted_len);
     }
 
-    // Decrypt with bogus IV.
-    // iv_len == 0 is invalid and should be caught earlier.
-    // Still skip, if there's no IV to modify.
-    if (iv_len != 0) {
+    // Decrypt with bogus nonce.
+    // A nonce length of 0 is invalid and should be caught earlier.
+    ASSERT_NE(0U, nonce->size());
+    decrypted_len = 0;
+    nonce->data()[0] ^= 0xff;
+    rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(), &decrypted_len,
+                      data_len, encrypted.data(), encrypted.size());
+    EXPECT_EQ(rv, SECFailure);
+    EXPECT_EQ(0U, decrypted_len);
+    nonce->data()[0] ^= 0xff;  // restore value
+
+    // Decrypt with bogus additional data.
+    // Skip when AAD was empty and can't be modified.
+    // Alternatively we could generate random aad.
+    if (aad->size() != 0) {
       decrypted_len = 0;
-      SECItem bogus_params(params);
-      CK_NSS_AEAD_PARAMS bogusAeadParams(aead_params);
-      bogus_params.data = reinterpret_cast<unsigned char*>(&bogusAeadParams);
+      aad->data()[0] ^= 0xff;
 
-      std::vector<uint8_t> bogusIV(iv, iv + iv_len);
-      bogusAeadParams.pNonce = toUcharPtr(bogusIV.data());
-      bogusIV[0] ^= 0xff;
-
-      rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(),
+      rv = PK11_Decrypt(key.get(), mech, params, decrypted.data(),
                         &decrypted_len, data_len, encrypted.data(),
                         encrypted.size());
       EXPECT_EQ(rv, SECFailure);
       EXPECT_EQ(0U, decrypted_len);
     }
+  }
 
-    // Decrypt with bogus additional data.
-    // Skip when AAD was empty and can't be modified.
-    // Alternatively we could generate random aad.
-    if (aad_len != 0) {
-      decrypted_len = 0;
-      SECItem bogus_params(params);
-      CK_NSS_AEAD_PARAMS bogus_aead_params(aead_params);
-      bogus_params.data = reinterpret_cast<unsigned char*>(&bogus_aead_params);
+  void EncryptDecrypt(const ScopedPK11SymKey& key, const bool invalid_iv,
+                      const bool invalid_tag, const uint8_t* data,
+                      size_t data_len, const uint8_t* aad_ptr, size_t aad_len,
+                      const uint8_t* iv_ptr, size_t iv_len,
+                      const uint8_t* ct = nullptr, size_t ct_len = 0) {
+    std::vector<uint8_t> nonce(iv_ptr, iv_ptr + iv_len);
+    std::vector<uint8_t> aad(aad_ptr, aad_ptr + aad_len);
+    // Prepare AEAD params.
+    CK_SALSA20_CHACHA20_POLY1305_PARAMS aead_params;
+    aead_params.pNonce = toUcharPtr(nonce.data());
+    aead_params.ulNonceLen = nonce.size();
+    aead_params.pAAD = toUcharPtr(aad.data());
+    aead_params.ulAADLen = aad.size();
+
+    SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params),
+                      sizeof(aead_params)};
+
+    EncryptDecrypt(key, invalid_iv, invalid_tag, data, data_len, kMech, &params,
+                   &nonce, &aad, ct, ct_len);
+  }
 
-      std::vector<uint8_t> bogus_aad(aad, aad + aad_len);
-      bogus_aead_params.pAAD = toUcharPtr(bogus_aad.data());
-      bogus_aad[0] ^= 0xff;
+  void EncryptDecryptLegacy(const ScopedPK11SymKey& key, const bool invalid_iv,
+                            const bool invalid_tag, const uint8_t* data,
+                            size_t data_len, const uint8_t* aad_ptr,
+                            size_t aad_len, const uint8_t* iv_ptr,
+                            size_t iv_len, const uint8_t* ct = nullptr,
+                            size_t ct_len = 0) {
+    std::vector<uint8_t> nonce(iv_ptr, iv_ptr + iv_len);
+    std::vector<uint8_t> aad(aad_ptr, aad_ptr + aad_len);
+    // Prepare AEAD params.
+    CK_NSS_AEAD_PARAMS aead_params;
+    aead_params.pNonce = toUcharPtr(nonce.data());
+    aead_params.ulNonceLen = nonce.size();
+    aead_params.pAAD = toUcharPtr(aad.data());
+    aead_params.ulAADLen = aad.size();
+    aead_params.ulTagLen = kTagLen;
+
+    SECItem params = {siBuffer, reinterpret_cast<unsigned char*>(&aead_params),
+                      sizeof(aead_params)};
 
-      rv = PK11_Decrypt(key.get(), kMech, &bogus_params, decrypted.data(),
-                        &decrypted_len, data_len, encrypted.data(),
-                        encrypted.size());
-      EXPECT_EQ(rv, SECFailure);
-      EXPECT_EQ(0U, decrypted_len);
-    }
+    // Encrypt with bad parameters (TagLen is too long).
+    unsigned int encrypted_len = 0;
+    std::vector<uint8_t> encrypted(data_len + aead_params.ulTagLen);
+    aead_params.ulTagLen = 158072;
+    SECStatus rv =
+        PK11_Encrypt(key.get(), kMechLegacy, &params, encrypted.data(),
+                     &encrypted_len, encrypted.size(), data, data_len);
+    EXPECT_EQ(SECFailure, rv);
+    EXPECT_EQ(0U, encrypted_len);
+
+    // Encrypt with bad parameters (TagLen is too short).
+    aead_params.ulTagLen = 2;
+    rv = PK11_Encrypt(key.get(), kMechLegacy, &params, encrypted.data(),
+                      &encrypted_len, encrypted.size(), data, data_len);
+    EXPECT_EQ(SECFailure, rv);
+    EXPECT_EQ(0U, encrypted_len);
+
+    // Encrypt.
+    aead_params.ulTagLen = kTagLen;
+    EncryptDecrypt(key, invalid_iv, invalid_tag, data, data_len, kMechLegacy,
+                   &params, &nonce, &aad, ct, ct_len);
   }
 
   void EncryptDecrypt(const ChaChaTestVector testvector) {
     ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
     SECItem keyItem = {siBuffer, toUcharPtr(testvector.key.data()),
                        static_cast<unsigned int>(testvector.key.size())};
 
     // Import key.
@@ -198,33 +233,33 @@ class Pkcs11ChaCha20Poly1305Test
                             PRBool separateTag) {
     // Generate a random key.
     ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
     ASSERT_NE(nullptr, slot);
     ScopedPK11SymKey sym_key(
         PK11_KeyGen(slot.get(), mech, nullptr, 32, nullptr));
     ASSERT_NE(nullptr, sym_key);
 
-    int tagSize = 16;
+    int tagSize = kTagLen;
     int cipher_simulated_size;
     int output_len_message = 0;
     int output_len_simulated = 0;
     unsigned int output_len_v24 = 0;
 
     std::vector<uint8_t> plainIn(17);
     std::vector<uint8_t> plainOut_message(17);
     std::vector<uint8_t> plainOut_simulated(17);
     std::vector<uint8_t> plainOut_v24(17);
     std::vector<uint8_t> nonce(12);
     std::vector<uint8_t> cipher_message(33);
     std::vector<uint8_t> cipher_simulated(33);
     std::vector<uint8_t> cipher_v24(33);
     std::vector<uint8_t> aad(16);
-    std::vector<uint8_t> tag_message(16);
-    std::vector<uint8_t> tag_simulated(16);
+    std::vector<uint8_t> tag_message(kTagLen);
+    std::vector<uint8_t> tag_simulated(kTagLen);
 
     // Prepare AEAD v2.40 params.
     CK_SALSA20_CHACHA20_POLY1305_PARAMS chacha_params;
     chacha_params.pNonce = nonce.data();
     chacha_params.ulNonceLen = nonce.size();
     chacha_params.pAAD = aad.data();
     chacha_params.ulAADLen = aad.size();
 
@@ -382,92 +417,173 @@ TEST_F(Pkcs11ChaCha20Poly1305Test, Gener
   EXPECT_EQ(rv, SECSuccess);
 
   // Check.
   EncryptDecrypt(key, false, false, input.data(), input.size(), aad.data(),
                  aad.size(), iv.data(), iv.size());
 }
 
 TEST_F(Pkcs11ChaCha20Poly1305Test, Xor) {
-  static const uint8_t kExpected[sizeof(kData)] = {
-      0xd8, 0x15, 0xd3, 0xb3, 0xe9, 0x34, 0x3b, 0x7a,
-      0x24, 0xf6, 0x5f, 0xd7, 0x95, 0x3d, 0xd3, 0x51};
-
   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
   SECItem keyItem = {siBuffer, toUcharPtr(kKeyData),
                      static_cast<unsigned int>(sizeof(kKeyData))};
   ScopedPK11SymKey key(PK11_ImportSymKey(
       slot.get(), kMechXor, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, nullptr));
   EXPECT_TRUE(!!key);
 
-  SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce),
-                          static_cast<unsigned int>(sizeof(kCtrNonce))};
+  SECItem params = {siBuffer,
+                    toUcharPtr(reinterpret_cast<const uint8_t*>(&kXorParams)),
+                    static_cast<unsigned int>(sizeof(kXorParams))};
   uint8_t encrypted[sizeof(kData)];
   unsigned int encrypted_len = 88;  // This should be overwritten.
   SECStatus rv =
-      PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
+      PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
+                   sizeof(encrypted), kData, sizeof(kData));
+  ASSERT_EQ(SECSuccess, rv);
+  ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len));
+  EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor)));
+
+  // Decrypting has the same effect.
+  rv = PK11_Decrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
+                    sizeof(encrypted), kData, sizeof(kData));
+  ASSERT_EQ(SECSuccess, rv);
+  ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len));
+  EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor)));
+
+  // Operating in reverse too.
+  rv = PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
+                    sizeof(encrypted), kExpectedXor, sizeof(kExpectedXor));
+  ASSERT_EQ(SECSuccess, rv);
+  ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len));
+  EXPECT_EQ(0, memcmp(kData, encrypted, sizeof(kData)));
+}
+
+TEST_F(Pkcs11ChaCha20Poly1305Test, XorLegacy) {
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  SECItem keyItem = {siBuffer, toUcharPtr(kKeyData),
+                     static_cast<unsigned int>(sizeof(kKeyData))};
+  ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), kMechXorLegacy,
+                                         PK11_OriginUnwrap, CKA_ENCRYPT,
+                                         &keyItem, nullptr));
+  EXPECT_TRUE(!!key);
+
+  SECItem ctrNonceItem = {siBuffer, toUcharPtr(kXorParamsLegacy),
+                          static_cast<unsigned int>(sizeof(kXorParamsLegacy))};
+  uint8_t encrypted[sizeof(kData)];
+  unsigned int encrypted_len = 88;  // This should be overwritten.
+  SECStatus rv =
+      PK11_Encrypt(key.get(), kMechXorLegacy, &ctrNonceItem, encrypted,
                    &encrypted_len, sizeof(encrypted), kData, sizeof(kData));
   ASSERT_EQ(SECSuccess, rv);
-  ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len));
-  EXPECT_EQ(0, memcmp(kExpected, encrypted, sizeof(kExpected)));
+  ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len));
+  EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor)));
 
   // Decrypting has the same effect.
-  rv = PK11_Decrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
+  rv = PK11_Decrypt(key.get(), kMechXorLegacy, &ctrNonceItem, encrypted,
                     &encrypted_len, sizeof(encrypted), kData, sizeof(kData));
   ASSERT_EQ(SECSuccess, rv);
   ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len));
-  EXPECT_EQ(0, memcmp(kExpected, encrypted, sizeof(kExpected)));
+  EXPECT_EQ(0, memcmp(kExpectedXor, encrypted, sizeof(kExpectedXor)));
 
   // Operating in reverse too.
-  rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
-                    &encrypted_len, sizeof(encrypted), kExpected,
-                    sizeof(kExpected));
+  rv = PK11_Encrypt(key.get(), kMechXorLegacy, &ctrNonceItem, encrypted,
+                    &encrypted_len, sizeof(encrypted), kExpectedXor,
+                    sizeof(kExpectedXor));
   ASSERT_EQ(SECSuccess, rv);
-  ASSERT_EQ(sizeof(kExpected), static_cast<size_t>(encrypted_len));
+  ASSERT_EQ(sizeof(kExpectedXor), static_cast<size_t>(encrypted_len));
   EXPECT_EQ(0, memcmp(kData, encrypted, sizeof(kData)));
 }
 
 // This test just ensures that a key can be generated for use with the XOR
 // function.  The result is random and therefore cannot be checked.
 TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateXor) {
   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
-  ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr));
+  ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMechXor, nullptr, 32, nullptr));
   EXPECT_TRUE(!!key);
 
   std::vector<uint8_t> iv(16);
   SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size());
   EXPECT_EQ(SECSuccess, rv);
 
-  SECItem ctrNonceItem = {siBuffer, toUcharPtr(iv.data()),
-                          static_cast<unsigned int>(iv.size())};
+  CK_CHACHA20_PARAMS chacha_params;
+  chacha_params.pBlockCounter = iv.data();
+  chacha_params.blockCounterBits = 32;
+  chacha_params.pNonce = iv.data() + 4;
+  chacha_params.ulNonceBits = 96;
+
+  SECItem params = {
+      siBuffer, toUcharPtr(reinterpret_cast<const uint8_t*>(&chacha_params)),
+      static_cast<unsigned int>(sizeof(chacha_params))};
   uint8_t encrypted[sizeof(kData)];
   unsigned int encrypted_len = 88;  // This should be overwritten.
-  rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
+  rv = PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
+                    sizeof(encrypted), kData, sizeof(kData));
+  ASSERT_EQ(SECSuccess, rv);
+  ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len));
+}
+
+TEST_F(Pkcs11ChaCha20Poly1305Test, GenerateXorLegacy) {
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  ScopedPK11SymKey key(
+      PK11_KeyGen(slot.get(), kMechXorLegacy, nullptr, 32, nullptr));
+  EXPECT_TRUE(!!key);
+
+  std::vector<uint8_t> iv(16);
+  SECStatus rv = PK11_GenerateRandomOnSlot(slot.get(), iv.data(), iv.size());
+  EXPECT_EQ(SECSuccess, rv);
+
+  SECItem params = {siBuffer, toUcharPtr(iv.data()),
+                    static_cast<unsigned int>(iv.size())};
+  uint8_t encrypted[sizeof(kData)];
+  unsigned int encrypted_len = 88;  // This should be overwritten.
+  rv = PK11_Encrypt(key.get(), kMechXorLegacy, &params, encrypted,
                     &encrypted_len, sizeof(encrypted), kData, sizeof(kData));
   ASSERT_EQ(SECSuccess, rv);
   ASSERT_EQ(sizeof(kData), static_cast<size_t>(encrypted_len));
 }
 
 TEST_F(Pkcs11ChaCha20Poly1305Test, XorInvalidParams) {
   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
   ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr));
   EXPECT_TRUE(!!key);
 
-  SECItem ctrNonceItem = {siBuffer, toUcharPtr(kCtrNonce),
-                          static_cast<unsigned int>(sizeof(kCtrNonce)) - 1};
+  SECItem params = {siBuffer,
+                    toUcharPtr(reinterpret_cast<const uint8_t*>(&kXorParams)),
+                    static_cast<unsigned int>(sizeof(kXorParams)) - 1};
   uint8_t encrypted[sizeof(kData)];
   unsigned int encrypted_len = 88;
   SECStatus rv =
-      PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
-                   &encrypted_len, sizeof(encrypted), kData, sizeof(kData));
+      PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
+                   sizeof(encrypted), kData, sizeof(kData));
   EXPECT_EQ(SECFailure, rv);
 
-  ctrNonceItem.data = nullptr;
-  rv = PK11_Encrypt(key.get(), kMechXor, &ctrNonceItem, encrypted,
-                    &encrypted_len, sizeof(encrypted), kData, sizeof(kData));
+  params.data = nullptr;
+  rv = PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
+                    sizeof(encrypted), kData, sizeof(kData));
+  EXPECT_EQ(SECFailure, rv);
+  EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
+}
+
+TEST_F(Pkcs11ChaCha20Poly1305Test, XorLegacyInvalidParams) {
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+  ScopedPK11SymKey key(PK11_KeyGen(slot.get(), kMech, nullptr, 32, nullptr));
+  EXPECT_TRUE(!!key);
+
+  SECItem params = {siBuffer, toUcharPtr(kXorParamsLegacy),
+                    static_cast<unsigned int>(sizeof(kXorParamsLegacy)) - 1};
+  uint8_t encrypted[sizeof(kData)];
+  unsigned int encrypted_len = 88;
+  SECStatus rv =
+      PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
+                   sizeof(encrypted), kData, sizeof(kData));
+  EXPECT_EQ(SECFailure, rv);
+
+  params.data = nullptr;
+  rv = PK11_Encrypt(key.get(), kMechXor, &params, encrypted, &encrypted_len,
+                    sizeof(encrypted), kData, sizeof(kData));
   EXPECT_EQ(SECFailure, rv);
   EXPECT_EQ(SEC_ERROR_BAD_DATA, PORT_GetError());
 }
 
 TEST_P(Pkcs11ChaCha20Poly1305Test, TestVectors) { EncryptDecrypt(GetParam()); }
 
 INSTANTIATE_TEST_SUITE_P(NSSTestVector, Pkcs11ChaCha20Poly1305Test,
                          ::testing::ValuesIn(kChaCha20Vectors));
--- a/security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc
+++ b/security/nss/gtests/pk11_gtest/pk11_cipherop_unittest.cc
@@ -1,13 +1,14 @@
 // 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 "gtest/gtest.h"
+#include "nss_scoped_ptrs.h"
 
 #include <assert.h>
 #include <limits.h>
 #include <prinit.h>
 #include <nss.h>
 #include <pk11pub.h>
 
 static const size_t kKeyLen = 128 / 8;
@@ -16,114 +17,115 @@ namespace nss_test {
 
 //
 // The ciper tests using the bltest command cover a great deal of testing.
 // However, Bug 1489691 revealed a corner case which is covered here.
 // This test will make multiple calls to PK11_CipherOp using the same
 // cipher context with data that is not cipher block aligned.
 //
 
-static SECStatus GetBytes(PK11Context* ctx, uint8_t* bytes, size_t len) {
+static SECStatus GetBytes(const ScopedPK11Context& ctx, size_t len) {
   std::vector<uint8_t> in(len, 0);
 
+  uint8_t outbuf[128];
+  PORT_Assert(len <= sizeof(outbuf));
   int outlen;
-  SECStatus rv = PK11_CipherOp(ctx, bytes, &outlen, len, &in[0], len);
+  SECStatus rv = PK11_CipherOp(ctx.get(), outbuf, &outlen, len, in.data(), len);
   if (static_cast<size_t>(outlen) != len) {
-    return SECFailure;
+    EXPECT_EQ(rv, SECFailure);
   }
   return rv;
 }
 
 TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOps) {
-  PK11SlotInfo* slot;
-  PK11SymKey* key;
-  PK11Context* ctx;
-
-  NSSInitContext* globalctx =
-      NSS_InitContext("", "", "", "", NULL,
-                      NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
-                          NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT);
+  ScopedNSSInitContext globalctx(NSS_InitContext(
+      "", "", "", "", NULL, NSS_INIT_READONLY | NSS_INIT_NOCERTDB |
+                                NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN |
+                                NSS_INIT_NOROOTINIT));
+  ASSERT_TRUE(globalctx);
 
   const CK_MECHANISM_TYPE cipher = CKM_AES_CTR;
 
-  slot = PK11_GetInternalSlot();
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
   ASSERT_TRUE(slot);
 
   // Use arbitrary bytes for the AES key
   uint8_t key_bytes[kKeyLen];
   for (size_t i = 0; i < kKeyLen; i++) {
     key_bytes[i] = i;
   }
 
   SECItem keyItem = {siBuffer, key_bytes, kKeyLen};
 
   // The IV can be all zeros since we only encrypt once with
   // each AES key.
   CK_AES_CTR_PARAMS param = {128, {}};
   SECItem paramItem = {siBuffer, reinterpret_cast<unsigned char*>(&param),
                        sizeof(CK_AES_CTR_PARAMS)};
 
-  key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, CKA_ENCRYPT,
-                          &keyItem, NULL);
-  ctx = PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key, &paramItem);
+  ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap,
+                                         CKA_ENCRYPT, &keyItem, NULL));
   ASSERT_TRUE(key);
+  ScopedPK11Context ctx(
+      PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key.get(), &paramItem));
   ASSERT_TRUE(ctx);
 
-  uint8_t outbuf[128];
-  ASSERT_EQ(GetBytes(ctx, outbuf, 7), SECSuccess);
-  ASSERT_EQ(GetBytes(ctx, outbuf, 17), SECSuccess);
-
-  PK11_FreeSymKey(key);
-  PK11_FreeSlot(slot);
-  PK11_DestroyContext(ctx, PR_TRUE);
-  NSS_ShutdownContext(globalctx);
+  ASSERT_EQ(GetBytes(ctx, 7), SECSuccess);
+  ASSERT_EQ(GetBytes(ctx, 17), SECSuccess);
 }
 
-TEST(Pkcs11CipherOp, SingleCtxMultipleUnalignedCipherOpsChaCha20) {
-  PK11SlotInfo* slot;
-  PK11SymKey* key;
-  PK11Context* ctx;
+// A context can't be used for Chacha20 as the underlying
+// PK11_CipherOp operation is calling the C_EncryptUpdate function for
+// which multi-part is disabled for ChaCha20 in counter mode.
+void ChachaMulti(CK_MECHANISM_TYPE cipher, SECItem* param) {
+  ScopedNSSInitContext globalctx(NSS_InitContext(
+      "", "", "", "", NULL, NSS_INIT_READONLY | NSS_INIT_NOCERTDB |
+                                NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN |
+                                NSS_INIT_NOROOTINIT));
+  ASSERT_TRUE(globalctx);
 
-  NSSInitContext* globalctx =
-      NSS_InitContext("", "", "", "", NULL,
-                      NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
-                          NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT);
-
-  const CK_MECHANISM_TYPE cipher = CKM_NSS_CHACHA20_CTR;
-
-  slot = PK11_GetInternalSlot();
+  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
   ASSERT_TRUE(slot);
 
   // Use arbitrary bytes for the ChaCha20 key and IV
   uint8_t key_bytes[32];
   for (size_t i = 0; i < 32; i++) {
     key_bytes[i] = i;
   }
-  SECItem keyItem = {siBuffer, key_bytes, 32};
+  SECItem keyItem = {siBuffer, key_bytes, sizeof(key_bytes)};
 
+  ScopedPK11SymKey key(PK11_ImportSymKey(slot.get(), cipher, PK11_OriginUnwrap,
+                                         CKA_ENCRYPT, &keyItem, NULL));
+  ASSERT_TRUE(key);
+  ScopedSECItem param_item(PK11_ParamFromIV(cipher, param));
+  ASSERT_TRUE(param_item);
+  ScopedPK11Context ctx(PK11_CreateContextBySymKey(
+      cipher, CKA_ENCRYPT, key.get(), param_item.get()));
+  ASSERT_TRUE(ctx);
+
+  ASSERT_EQ(GetBytes(ctx, 7), SECFailure);
+}
+
+TEST(Pkcs11CipherOp, ChachaMultiLegacy) {
   uint8_t iv_bytes[16];
   for (size_t i = 0; i < 16; i++) {
-    key_bytes[i] = i;
+    iv_bytes[i] = i;
   }
-  SECItem ivItem = {siBuffer, iv_bytes, 16};
-
-  SECItem* param = PK11_ParamFromIV(cipher, &ivItem);
+  SECItem param_item = {siBuffer, iv_bytes, sizeof(iv_bytes)};
 
-  key = PK11_ImportSymKey(slot, cipher, PK11_OriginUnwrap, CKA_ENCRYPT,
-                          &keyItem, NULL);
-  ctx = PK11_CreateContextBySymKey(cipher, CKA_ENCRYPT, key, param);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(ctx);
+  ChachaMulti(CKM_NSS_CHACHA20_CTR, &param_item);
+}
 
-  uint8_t outbuf[128];
-  // This is supposed to fail for Chacha20. This is because the underlying
-  // PK11_CipherOp operation is calling the C_EncryptUpdate function for
-  // which multi-part is disabled for ChaCha20 in counter mode.
-  ASSERT_EQ(GetBytes(ctx, outbuf, 7), SECFailure);
+TEST(Pkcs11CipherOp, ChachaMulti) {
+  uint8_t iv_bytes[16];
+  for (size_t i = 0; i < 16; i++) {
+    iv_bytes[i] = i;
+  }
+  CK_CHACHA20_PARAMS chacha_params = {
+      iv_bytes, 32, iv_bytes + 4, 96,
+  };
+  SECItem param_item = {siBuffer, reinterpret_cast<uint8_t*>(&chacha_params),
+                        sizeof(chacha_params)};
 
-  PK11_FreeSymKey(key);
-  PK11_FreeSlot(slot);
-  SECITEM_FreeItem(param, PR_TRUE);
-  PK11_DestroyContext(ctx, PR_TRUE);
-  NSS_ShutdownContext(globalctx);
+  ChachaMulti(CKM_CHACHA20, &param_item);
 }
 
 }  // namespace nss_test
--- a/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc
+++ b/security/nss/gtests/pk11_gtest/pk11_module_unittest.cc
@@ -38,20 +38,20 @@ class Pkcs11ModuleTest : public ::testin
 TEST_F(Pkcs11ModuleTest, LoadUnload) {
   ScopedSECMODModule module(SECMOD_FindModule("Pkcs11ModuleTest"));
   EXPECT_NE(nullptr, module);
 }
 
 TEST_F(Pkcs11ModuleTest, ListSlots) {
   ScopedPK11SlotList slots(
       PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr));
-  EXPECT_NE(nullptr, slots);
+  ASSERT_NE(nullptr, slots);
 
   PK11SlotListElement* element = PK11_GetFirstSafe(slots.get());
-  EXPECT_NE(nullptr, element);
+  ASSERT_NE(nullptr, element);
 
   // These tokens are always present.
   const std::vector<std::string> kSlotsWithToken = {
       "NSS Internal Cryptographic Services",
       "NSS User Private Key and Certificate Services",
       "Test PKCS11 Public Certs Slot", "Test PKCS11 Slot 二"};
   std::vector<std::string> foundSlots;
 
@@ -67,18 +67,18 @@ TEST_F(Pkcs11ModuleTest, ListSlots) {
                          foundSlots.begin()));
 }
 
 TEST_F(Pkcs11ModuleTest, PublicCertificatesToken) {
   const std::string kRegularToken = "Test PKCS11 Tokeñ 2 Label";
   const std::string kPublicCertificatesToken = "Test PKCS11 Public Certs Token";
 
   ScopedPK11SlotInfo slot1(PK11_FindSlotByName(kRegularToken.c_str()));
-  EXPECT_NE(nullptr, slot1);
+  ASSERT_NE(nullptr, slot1);
   EXPECT_FALSE(PK11_IsFriendly(slot1.get()));
 
   ScopedPK11SlotInfo slot2(
       PK11_FindSlotByName(kPublicCertificatesToken.c_str()));
-  EXPECT_NE(nullptr, slot2);
+  ASSERT_NE(nullptr, slot2);
   EXPECT_TRUE(PK11_IsFriendly(slot2.get()));
 }
 
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/ssl_custext_unittest.cc
@@ -18,33 +18,33 @@ namespace nss_test {
 
 static void IncrementCounterArg(void *arg) {
   if (arg) {
     auto *called = reinterpret_cast<size_t *>(arg);
     ++*called;
   }
 }
 
-PRBool NoopExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
-                           PRUint8 *data, unsigned int *len,
-                           unsigned int maxLen, void *arg) {
+static PRBool NoopExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
+                                  PRUint8 *data, unsigned int *len,
+                                  unsigned int maxLen, void *arg) {
   IncrementCounterArg(arg);
   return PR_FALSE;
 }
 
-PRBool EmptyExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
-                            PRUint8 *data, unsigned int *len,
-                            unsigned int maxLen, void *arg) {
+static PRBool EmptyExtensionWriter(PRFileDesc *fd, SSLHandshakeType message,
+                                   PRUint8 *data, unsigned int *len,
+                                   unsigned int maxLen, void *arg) {
   IncrementCounterArg(arg);
   return PR_TRUE;
 }
 
-SECStatus NoopExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
-                               const PRUint8 *data, unsigned int len,
-                               SSLAlertDescription *alert, void *arg) {
+static SECStatus NoopExtensionHandler(PRFileDesc *fd, SSLHandshakeType message,
+                                      const PRUint8 *data, unsigned int len,
+                                      SSLAlertDescription *alert, void *arg) {
   return SECSuccess;
 }
 
 // All of the (current) set of supported extensions, plus a few extra.
 static const uint16_t kManyExtensions[] = {
     ssl_server_name_xtn,
     ssl_cert_status_xtn,
     ssl_supported_groups_xtn,
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -308,27 +308,27 @@ void TlsConnectTestBase::GenerateEchConf
   record.Truncate(0);
   record.Write(0, encoded, encoded_len);
 }
 
 void TlsConnectTestBase::SetupEch(std::shared_ptr<TlsAgent>& client,
                                   std::shared_ptr<TlsAgent>& server,
                                   HpkeKemId kem_id, bool expect_ech,
                                   bool set_client_config,
-                                  bool set_server_config) {
+                                  bool set_server_config, int max_name_len) {
   EXPECT_TRUE(set_server_config || set_client_config);
   ScopedSECKEYPublicKey pub;
   ScopedSECKEYPrivateKey priv;
   DataBuffer record;
   static const std::vector<HpkeSymmetricSuite> kDefaultSuites = {
       {HpkeKdfHkdfSha256, HpkeAeadChaCha20Poly1305},
       {HpkeKdfHkdfSha256, HpkeAeadAes128Gcm}};
 
-  GenerateEchConfig(kem_id, kDefaultSuites, "public.name", 100, record, pub,
-                    priv);
+  GenerateEchConfig(kem_id, kDefaultSuites, "public.name", max_name_len, record,
+                    pub, priv);
   ASSERT_NE(0U, record.len());
   SECStatus rv;
   if (set_server_config) {
     rv = SSL_SetServerEchConfigs(server->ssl_fd(), pub.get(), priv.get(),
                                  record.data(), record.len());
     ASSERT_EQ(SECSuccess, rv);
   }
   if (set_client_config) {
--- a/security/nss/gtests/ssl_gtest/tls_connect.h
+++ b/security/nss/gtests/ssl_gtest/tls_connect.h
@@ -151,17 +151,17 @@ class TlsConnectTestBase : public ::test
   static void GenerateEchConfig(
       HpkeKemId kem_id, const std::vector<HpkeSymmetricSuite>& cipher_suites,
       const std::string& public_name, uint16_t max_name_len, DataBuffer& record,
       ScopedSECKEYPublicKey& pubKey, ScopedSECKEYPrivateKey& privKey);
   void SetupEch(std::shared_ptr<TlsAgent>& client,
                 std::shared_ptr<TlsAgent>& server,
                 HpkeKemId kem_id = HpkeDhKemX25519Sha256,
                 bool expect_ech = true, bool set_client_config = true,
-                bool set_server_config = true);
+                bool set_server_config = true, int maxConfigSize = 100);
 
  protected:
   SSLProtocolVariant variant_;
   std::shared_ptr<TlsAgent> client_;
   std::shared_ptr<TlsAgent> server_;
   std::unique_ptr<TlsAgent> client_model_;
   std::unique_ptr<TlsAgent> server_model_;
   uint16_t version_;
--- a/security/nss/gtests/ssl_gtest/tls_ech_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/tls_ech_unittest.cc
@@ -1,24 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=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/. */
 
-// TODO: Add padding/maxNameLen tests after support is added in bug 1677181.
-
 #include "secerr.h"
 #include "ssl.h"
 
 #include "gtest_utils.h"
 #include "pk11pub.h"
 #include "tls_agent.h"
 #include "tls_connect.h"
 #include "util.h"
+#include "tls13ech.h"
 
 namespace nss_test {
 
 class TlsAgentEchTest : public TlsAgentTestClient13 {
  protected:
   void InstallEchConfig(const DataBuffer& echconfig, PRErrorCode err = 0) {
     SECStatus rv = SSL_SetClientEchConfigs(agent_->ssl_fd(), echconfig.data(),
                                            echconfig.len());
@@ -240,94 +239,105 @@ static void CheckCertVerifyPublicName(Tl
     EXPECT_NE(nullptr, agent->pre_info().echPublicName);
     if (agent->pre_info().echPublicName) {
       EXPECT_EQ(0,
                 strcmp(kPublicName.c_str(), agent->pre_info().echPublicName));
     }
   }
 }
 
-static SECStatus AuthCompleteSuccess(TlsAgent* agent, PRBool, PRBool) {
-  CheckCertVerifyPublicName(agent);
-  return SECSuccess;
-}
-
 static SECStatus AuthCompleteFail(TlsAgent* agent, PRBool, PRBool) {
   CheckCertVerifyPublicName(agent);
   return SECFailure;
 }
 
+// Given two EchConfigList structures, e.g. from GenerateEchConfig, construct
+// a single list containing all entries.
+static DataBuffer MakeEchConfigList(DataBuffer config1, DataBuffer config2) {
+  DataBuffer sizedConfigListBuffer;
+
+  sizedConfigListBuffer.Write(2, config1.data() + 2, config1.len() - 2);
+  sizedConfigListBuffer.Write(sizedConfigListBuffer.len(), config2.data() + 2,
+                              config2.len() - 2);
+  sizedConfigListBuffer.Write(0, sizedConfigListBuffer.len() - 2, 2);
+
+  PR_ASSERT(sizedConfigListBuffer.len() == config1.len() + config2.len() - 2);
+  return sizedConfigListBuffer;
+}
+
 TEST_P(TlsAgentEchTest, EchConfigsSupportedYesNo) {
   if (variant_ == ssl_variant_datagram) {
     GTEST_SKIP();
   }
-
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
   // ECHConfig 2 cipher_suites are unsupported.
-  const std::string mixed =
-      "0088FE0A004000002000203BB6D46C201B820F1AE4AFD4DEC304444156E4E04D1BF0FFDA"
-      "7783B6B457F756000800010003000100010064000B7075626C69632E6E616D650000FE0A"
-      "004000002000203BB6D46C201B820F1AE4AFD4DEC304444156E4E04D1BF0FFDA7783B6B4"
-      "57F75600080001FFFFFFFF00010064000B7075626C69632E6E616D650000";
-  std::vector<uint8_t> config = hex_string_to_bytes(mixed);
-  DataBuffer echconfig(config.data(), config.size());
-
+  DataBuffer config1;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kSuiteAes,
+                                        kPublicName, 100, config1, pub, priv);
+  DataBuffer config2;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kBogusSuite,
+                                        kPublicName, 100, config2, pub, priv);
   EnsureInit();
   EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
                                                  PR_FALSE));  // Don't GREASE
-  InstallEchConfig(echconfig, 0);
+
+  DataBuffer sizedConfigListBuffer = MakeEchConfigList(config1, config2);
+  InstallEchConfig(sizedConfigListBuffer, 0);
   auto filter = MakeTlsFilter<TlsExtensionCapture>(
       agent_, ssl_tls13_encrypted_client_hello_xtn);
   agent_->Handshake();
   ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
   ASSERT_TRUE(filter->captured());
 }
 
 TEST_P(TlsAgentEchTest, EchConfigsSupportedNoYes) {
   if (variant_ == ssl_variant_datagram) {
     GTEST_SKIP();
   }
-
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer config2;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kSuiteAes,
+                                        kPublicName, 100, config2, pub, priv);
+  DataBuffer config1;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kBogusSuite,
+                                        kPublicName, 100, config1, pub, priv);
   // ECHConfig 1 cipher_suites are unsupported.
-  const std::string mixed =
-      "0088FE0A004000002000203BB6D46C201B820F1AE4AFD4DEC304444156E4E04D1BF0FFDA"
-      "7783B6B457F75600080001FFFFFFFF00010064000B7075626C69632E6E616D650000FE0A"
-      "004000002000203BB6D46C201B820F1AE4AFD4DEC304444156E4E04D1BF0FFDA7783B6B4"
-      "57F756000800010003000100010064000B7075626C69632E6E616D650000";
-  std::vector<uint8_t> config = hex_string_to_bytes(mixed);
-  DataBuffer echconfig(config.data(), config.size());
-
+  DataBuffer sizedConfigListBuffer = MakeEchConfigList(config1, config2);
   EnsureInit();
   EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
                                                  PR_FALSE));  // Don't GREASE
-  InstallEchConfig(echconfig, 0);
+  InstallEchConfig(sizedConfigListBuffer, 0);
   auto filter = MakeTlsFilter<TlsExtensionCapture>(
       agent_, ssl_tls13_encrypted_client_hello_xtn);
   agent_->Handshake();
   ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
   ASSERT_TRUE(filter->captured());
 }
 
 TEST_P(TlsAgentEchTest, EchConfigsSupportedNoNo) {
   if (variant_ == ssl_variant_datagram) {
     GTEST_SKIP();
   }
 
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer config2;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kBogusSuite,
+                                        kPublicName, 100, config2, pub, priv);
+  DataBuffer config1;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kBogusSuite,
+                                        kPublicName, 100, config1, pub, priv);
   // ECHConfig 1 and 2 cipher_suites are unsupported.
-  const std::string unsupported =
-      "0088FE0A004000002000203BB6D46C201B820F1AE4AFD4DEC304444156E4E04D1BF0FFDA"
-      "7783B6B457F75600080001FFFF0001FFFF0064000B7075626C69632E6E616D650000FE0A"
-      "004000002000203BB6D46C201B820F1AE4AFD4DEC304444156E4E04D1BF0FFDA7783B6B4"
-      "57F7560008FFFF0003FFFF00010064000B7075626C69632E6E616D650000";
-  std::vector<uint8_t> config = hex_string_to_bytes(unsupported);
-  DataBuffer echconfig(config.data(), config.size());
-
+  DataBuffer sizedConfigListBuffer = MakeEchConfigList(config1, config2);
   EnsureInit();
   EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(agent_->ssl_fd(),
                                                  PR_FALSE));  // Don't GREASE
-  InstallEchConfig(echconfig, SEC_ERROR_INVALID_ARGS);
+  InstallEchConfig(sizedConfigListBuffer, SEC_ERROR_INVALID_ARGS);
   auto filter = MakeTlsFilter<TlsExtensionCapture>(
       agent_, ssl_tls13_encrypted_client_hello_xtn);
   agent_->Handshake();
   ASSERT_EQ(TlsAgent::STATE_CONNECTING, agent_->state());
   ASSERT_FALSE(filter->captured());
 }
 
 TEST_P(TlsAgentEchTest, ShortEchConfig) {
@@ -639,238 +649,476 @@ TEST_F(TlsConnectStreamTls13Ech, EchFixe
 // constructing ClientHello in the test that can be successfully decrypted.
 
 // Test an encoded ClientHelloInner containing an extra extensionType
 // in outer_extensions, for which there is no corresponding (uncompressed)
 // extension in ClientHelloOuter.
 TEST_F(TlsConnectStreamTls13Ech, EchOuterExtensionsReferencesMissing) {
   // Construct this by prepending 0xabcd to ssl_tls13_outer_extensions_xtn.
   std::string ch =
-      "01000200030341a6813ccf3eefc2deb9c78f7627715ae343f5236e7224f454c723c93e0b"
-      "d875000006130113031302010001d100000010000e00000b7075626c69632e6e616d65ff"
+      "010001fc030390901d039ca83262d9115a5f98f43ddb2553241a8de5c46d9f118c4c29c2"
+      "64bc000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
       "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
-      "00200573a70286658ad4bc166d8f5f237f035714ba5ae4e838c077677ccb6d619813002b"
+      "00206df5f908d1c02320e246694c765d5ec1c0f7d7aef2b1b00b17c36331623d332d002b"
       "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
-      "020101001c00024001001500aa0000000000000000000000000000000000000000000000"
+      "020101001c00024001fe0d00f900000100034d00209a4f67b0744d1fba23aa4bacfadb2a"
+      "c706562dae04d80a83ae668a6f2dd6ef2700cfab1671182341df246d66c3aca873e8c714"
+      "bc2b1c3b576653609533c486df0bdcf63ab4e4e7d0b67fadf4e3504eec96f72e6778b15d"
+      "69c9a9594a041348a7130f67a1a7cac796a0e6d6fca505438355278a9a8fd55e44218441"
+      "9927a1e084ac7d7adeb2f0c19faafba430876bf0cdf4d195b2d06428b3de13120f65748a"
+      "468f8997a2c3bf1dd7f3996a0f2c70dea6c88149df182b3c3b78a8da8bb709a9ed9d77c6"
+      "5dc09accdfeb66c90db26b99a35052a8cbaf7bb9307a1e17d90a7aa9f768f5f446559d08"
+      "69bccc83eda9d2b347a00015004200000000000000000000000000000000000000000000"
       "000000000000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000";
+  ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
+                             SSL_ERROR_RX_MALFORMED_ECH_EXTENSION,
+                             SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchOuterExtensionsInsideInner) {
+  // Construct this by appending ssl_tls13_outer_extensions_xtn to the
+  // references in
+  // ssl_tls13_outer_extensions_xtn.
+  std::string ch =
+      "010001fc03035e2268bc7133079cd33eb088253393e561d80c5ee6f9a238aff022e1e10d"
+      "4c82000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
+      "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
+      "00200e071fd982854d50236ed0e4e7981460840f03d03fd84b44c409fe486203b252002b"
+      "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
+      "020101001c00024001fe0d00f900000100034d002099a032502ea4fd3c85b858ae1c59df"
+      "6a374f3698ed6bca188cf75c432c78cf5a00cf28dde32de7ade40abb16d550c1eec3dad4"
+      "a03c85efb95ec605837deae92a419285116e5cb8223ea53cff2b605e66f28e96d37e9b4e"
+      "3035fb1cfa125fa053d6770091b5731c9fb03e872a82991dfdd24ad8399fcc76db7fadba"
+      "029e064beb02c1282684a93e777bcefbca3dd143dfc225d2e65c80dbf3819ebda288e32c"
+      "3a1f8a27bb3aa9480dee2a4307073da3e15ee03dba386223d9399ad796af80c646f85406"
+      "282c34fd9406d25752087f08140e1be834e8a149f0bebfc2b3db16ccba83c37051e2e75d"
+      "e8a4e999ef385c74c96d0015004200000000000000000000000000000000000000000000"
       "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000";
+  ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
+                             SSL_ERROR_RX_MALFORMED_ECH_EXTENSION,
+                             SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchOuterExtensionsDuplicateReference) {
+  // Construct this by altering tls13_ConstructInnerExtensionsFromOuter to have
+  // each extension inserted in the reference list twice and running the
+  // EchFixedConfig test.
+  std::string ch =
+      "010001fc0303d8717df80286fcd8b4242ed846995c6473e290678231046bb1bfc7848460"
+      "b122000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
+      "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
+      "00206f21d5fdf7bf81943939a03656c1195ad347cec453bf7a16d0773ffef481d22f002b"
+      "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
+      "020101001c00024001fe0d011900000100034d002027eb9b641ba8ffc3a4028d00d1f5bd"
+      "e190736b1ea5a79513dee0a551cc6fe55200efc2ed6bf501f100896eb91221ce512c20c3"
+      "c5c110e7be6a5d340854ff5ac0175312631b021fd5a5c9841549989f415c4041a4b384b1"
+      "dba1d6b4182cc48904f993a15eab6bf7787b267ca65acef51c019508e0c9b382086a71d8"
+      "517cf19644d66d396efc066a4d37916d67b0e5fe08d52dd94d068dd85b9a245aaffac4ff"
+      "66d9a5221fd5805473bb7584eb7f218357c00aff890d2f2edf1c092c648c888b5cba1ca6"
+      "26817fda7765fcedfbc418b90b1841d878ed443593cafb61fa8fb708c53977615b45f545"
+      "2a8236cab3ec121cdc91a2de6a79437cae9d09e781339fddcac005ce62fd65d50e33faa2"
+      "2366955a0374001500220000000000000000000000000000000000000000000000000000"
+      "0000000000000000";
+  ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
+                             SSL_ERROR_RX_MALFORMED_ECH_EXTENSION,
+                             SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchOuterExtensionsOutOfOrder) {
+  // Construct this by altering tls13_ConstructInnerExtensionsFromOuter to leave
+  // a gap at the start and insert a 'late' extension there.
+  std::string ch =
+      "010001fc0303fabff6caf4d8b1eb1db5945c96badefec4b33188b97121e6a206e82b74bd"
+      "a855000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
+      "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
+      "00208fe970fc0c908f0c51734f18467e640df1d45a6ace2948b5c4bf73ee52ab3160002b"
+      "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
+      "020101001c00024001fe0d00f900000100034d00203339239f8925c3f9b89f4ced17c3b3"
+      "1c649299d7e10b3cdbc115de2a57d90d2200cf006e62866516380e8a16763bee5b2a75a8"
+      "74e8698c459f474d0e952c2fd3300bef1decd6f259b8ac2912684ef69b7a7be2520fbf15"
+      "5e0c3f88998789976ca1fbcaa40616fc513e3353540db091da76ca98007532974550d3da"
+      "aaddb799baf60adbc5800df30e187251427fe9de707d18a270352ee44f6eb37f0d8c72a1"
+      "5f9ffb5dd4bbb6045473c8d99b7a5c2c8cc59027f346cbe6ef240d5cf1919f58a998d427"
+      "0f8c882d03d22ec4df4079e15a639452ea4c24023f6bcad89566ce6a32b1dad6ddf6b436"
+      "3e6759bd48bed1b30a840015004200000000000000000000000000000000000000000000"
       "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000fe0a0095000100034d00209c68779a77e4bac0e9f39c2974b1900de044ae1510bf"
-      "d34fb5120a2a9d039607006c76c4571099733157eb8614ef2ad6049372e9fdf740f8ad4f"
-      "d24723702c9104a38ecc366eea78b0285422b3f119fc057e2282433a74d8c56b2135c785"
-      "bd5d01f89b2dbb42aa9a609eb1c6dd89252fa04cf8fbc4097e9c85da1e3eeebc188bbe16"
-      "db1166f6df1a0c7c6e0dce71";
+      "0000000000000000";
   ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
                              SSL_ERROR_RX_MALFORMED_ECH_EXTENSION,
                              SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 // Drop supported_versions from CHInner, make sure we don't negotiate 1.2+ECH.
 TEST_F(TlsConnectStreamTls13Ech, EchVersion12Inner) {
   // Construct this by removing ssl_tls13_supported_versions_xtn entirely.
   std::string ch =
-      "010002000303baf30ea25e5056b659a4d55233922c4ee261a04e6d84c8200713edca2f55"
-      "d434000006130113031302010001d100000010000e00000b7075626c69632e6e616d65ff"
+      "010001fc030338e9ebcde2b87ef779c4d9a9b9870aef3978130b254fbf168a92644c97c1"
+      "c5cb000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
       "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
-      "002081908a3cf3ed9ebf6d6b1f7082d77bb3bf8ff309c3c1255421720c4172548762002b"
+      "002081b3ea444fd631f9264e01276bcc1a6771aed3b5a8a396446467d1c820e52b25002b"
       "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
-      "020101001c00024001001500b30000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
+      "020101001c00024001fe0d00f900000100034d00205864042b43f4d4d544558fbcba410f"
+      "ebfb78ddfc5528672a7f7d9e70abc3eb6300cf6ff3271da628139bddc4a58ee92db26170"
+      "7310dee54d88c8a96a8d998b8608d5f10260b7e201e5dc8cafa13917a3fdfdf399082959"
+      "8adf3c291decf640f696e64c4e22bafb81565587c50dd829ccad68bd00babeaba7d8a7a5"
+      "400ad3200dbae674c549953ca6d3298ed751a9bc215a33be444fe908bf1c6f374cc139f9"
+      "98339f58b8fd3510a670e4102e3f7de21586ebd70c3fb1df8bb6b9e5dbc0db147dbac6d0"
+      "72dfc6cdf17ecee5c019c311b37ef9f5ceabb7edbdf87a4a04041c4d8b512a16517c5380"
+      "e8d4f6e3b2412b4a6c030015004200000000000000000000000000000000000000000000"
       "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000fe0a008c000100034d0020305bc263a664387b90a6975b2a"
-      "3aa1e4358e80a8ca0841237035d2475628582d006352a2b49912a61543dfa045c1429582"
-      "540c8c7019968867fde698eb37667f9aa9c23d02757492a4580fb027bbe4ba7615eea118"
-      "ad3bf7f02a88f8372cfa01888e7be0c55616f846e902bbdfc7edf56994d6398f5a965d9e"
-      "c4b1bc7afc580b28b0ac91d8";
-  ReplayChWithMalformedInner(ch, kTlsAlertProtocolVersion,
+      "0000000000000000";
+  ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
                              SSL_ERROR_UNSUPPORTED_VERSION,
-                             SSL_ERROR_PROTOCOL_VERSION_ALERT);
+                             SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
 // Use CHInner supported_versions to negotiate 1.2.
 TEST_F(TlsConnectStreamTls13Ech, EchVersion12InnerSupportedVersions) {
   // Construct this by changing ssl_tls13_supported_versions_xtn to write
   // TLS 1.2 instead of TLS 1.3.
   std::string ch =
-      "0100020003036c4a7f6f6b5479a5c1f769c7b04c082ba40b514522d193df855df8bea933"
-      "b565000006130113031302010001d100000010000e00000b7075626c69632e6e616d65ff"
+      "010001fc0303f7146bdc88c399feb49c62b796db2f8b1330e25292a889edf7c65231d0be"
+      "b95f000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
       "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
-      "0020ee721b8fe89260f8987d0d21b628db136c6155793fa63f4f546b244ee5357761002b"
+      "0020d31f8eb204efba49dbdbf40bb046b1e0b90fa3f034260d60f351d4b15e614e7f002b"
       "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
-      "020101001c00024001001500ac0000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
+      "020101001c00024001fe0d00f900000100034d0020eaa25e92721e65fd405577bf2fd322"
+      "857e60f8766a595929fc404c9a01ef441200cf04992c693fbc8eac87726b336a11abc411"
+      "541ceff50d533d4cf4d6e1078479acb5446675b652f22d6db04daf0c3640ec2429ba4f51"
+      "99c00daa43e9a7d85bd6733041feeca0b38ee6ca07042c7e67d40cd3e236499f3f9d92ab"
+      "e4642e483c75d77c247b0228bc773c09551d15845c35663afd1805c5b3adb136ffa6d94f"
+      "b7cbfe93d5d33c894b2a6437ad9a2278d5863ed20db652a6084c9e95a8dfaf821d0b474a"
+      "7efc2839f110edb4a73376ecab629b26b1eea63304899c49a07157fbbee67c786686cb04"
+      "a53666a74e1e003aefc70015004200000000000000000000000000000000000000000000"
       "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "0000000000fe0a0093000100034d00205de27fe39481bfb370ee8441f12e28296bc5c8fe"
-      "b6a6198ddc6ab03b2d024638006a9e9f57c3f39c0ad1c3427549a77f301d01d718e09da4"
-      "5497df178c95fd598bf0c9098d68dfba80a05eeeabc84dc0bb3225cee4a74688d520c632"
-      "73612f98be847dea4f040a8d9b2b92bb4a44273d0cafafbfe1ee4ed69448bc243b4359c6"
-      "e1eb3971e125fbfb016245fa";
+      "0000000000000000";
   ReplayChWithMalformedInner(ch, kTlsAlertProtocolVersion,
                              SSL_ERROR_UNSUPPORTED_VERSION,
                              SSL_ERROR_PROTOCOL_VERSION_ALERT);
 }
 
-// Replay a CH for which CHInner lacks the required ech_is_inner extension.
+// Replay a CH for which CHInner lacks the required ech xtn of inner type
 TEST_F(TlsConnectStreamTls13Ech, EchInnerMissing) {
-  // Construct by omitting ssl_tls13_ech_is_inner_xtn.
+  // Construct by omitting the ech inner extension
   std::string ch =
-      "010002000303912d293136b843248ffeecdde6ef0d5bc5d0adb4d356b985c2fcec8fe2b0"
-      "8f5d000006130113031302010001d100000010000e00000b7075626c69632e6e616d65ff"
+      "010001fc0303fa9cd9cf5b77bb4083f69a1d169d44b356faea0d6a0aee6d50412de6fef7"
+      "8d22000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
       "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
-      "00209222e6b0c672fd1e564fbca5889155e9126e3b006a8ff77ff61898dd56a08429002b"
+      "0020c329f1dde4d51b50f68c21053b545290b250af527b2832d3acf2c6af9b8b8d5c002b"
       "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
-      "020101001c00024001001500b00000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
+      "020101001c00024001fe0d00f900000100034d00207e2a0397b7d2776ae468057d630243"
+      "b01388cf80680b074323adf4091aba7b4c00cff4b649fb5b3a0719c1e085c7006a95eaad"
+      "32375b717a42d009c075e6246342fdc1e847c528495f90378ff5b4912da5190f7e8bfa1c"
+      "c9744b50e9e469cd7cd12bcb5f6534b7d617459d2efa4d796ad244567c49f1d22feb08a5"
+      "8e8ebdce059c28883dd69ca401e189f3ef438c3f0bf3d377e6727a1f6abf3a8a8cc149ee"
+      "60a1aa5ba4a50e99d2519216762558e9613a238bd630b5822f549575d9402f8da066aaef"
+      "2e0e6a7a04583b041925e0ef4575107c4436f9af26e561c0ab733cd88bee6a20e6414128"
+      "ea0ba1c73612bb62c1e90015004200000000000000000000000000000000000000000000"
       "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000fe0a008f000100034d0020e1bc83c066a251621c4b055779789a2c"
-      "6ac4b9a3850366b2ea0d32a8e041181c0066a4e9cc6912b8bc6c1b54a2c6c40428ab01a3"
-      "0e4621ec65320df2beff846a606618429c108fdfe24a6fad5053f53fb36082bf7d80d6f4"
-      "73131a3d6c04e182595606070ac4e0be078ada56456c02d37a2fee7cb17accd273cbd810"
-      "30ee0dbe139e51ba1d2a471f";
+      "0000000000000000";
   ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
                              SSL_ERROR_MISSING_ECH_EXTENSION,
                              SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
-// Replay a CH for which CHInner contains both an ECH and ech_is_inner
-// extension.
+TEST_F(TlsConnectStreamTls13Ech, EchInnerWrongSize) {
+  // Construct by including ech inner with wrong size
+  std::string ch =
+      "010001fc03035f8410dab9e49b0833d13390f3fe0b3c6321d842961c9cc46b59a0b5b8e1"
+      "4e0b000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
+      "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
+      "0020526a56087d685e574accb0e87d6781bc553612479e56460fe6a497fa1cd74e2e002b"
+      "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
+      "020101001c00024001fe0d00f900000100034d00200d096bf6ac0c3bcb79d70677da0e0d"
+      "249b40bc5ba6b8727654619fe6567d0b0700cfd13e136d2d041e3cd993b252386d97e98d"
+      "c972d29d28e0281a210fa56156b95e4371a6610a0b3e65f1b842875fb456de9b9c0e03f8"
+      "aa4d1055057ac3e20e5fa45b837ccbb06ef3856c71f1f63e91b60bfb5f3415f26e9a0d3c"
+      "4d404d5d5aaa6dca8d57cf2e6b4aaf399fa7271b0c1eedbfdd85fbc9711b0446eb9c9535"
+      "a74f3e5a71e2e22dc8d89980f96233ec9b80fbe4f295ff7903bade407fc544c8d76df4fb"
+      "ce4b8d79cea0ff7e0b0736ecbeaf5a146a4f81a930e788ae144cf2219e90dc3594165a7e"
+      "2a0b64f6189a87a348840015004200000000000000000000000000000000000000000000"
+      "000000000000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000";
+  ReplayChWithMalformedInner(ch, kTlsAlertDecodeError,
+                             SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION,
+                             SSL_ERROR_DECODE_ERROR_ALERT);
+}
+
 TEST_F(TlsConnectStreamTls13Ech, InnerWithEchAndEchIsInner) {
-  // Construct by appending an empty ssl_tls13_encrypted_client_hello_xtn to
+  // Construct by appending an empty ssl_tls13_encrypted_client_hello_xtn of
+  // type outer to
   // CHInner.
   std::string ch =
-      "010002000303b690bc4090ecfd7ad167de639b1d1ea7682588ffefae1164179d370f6cd3"
-      "0864000006130113031302010001d100000010000e00000b7075626c69632e6e616d65ff"
+      "010001fc0303527df5a8dbcf390c184c5274295283fdba78d05784170d8f3cb8c7d84747"
+      "afb5000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
       "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
-      "00200c3c15b0e9884d5f593634a168b70a62ae18c8d69a68f8e29c826fbabcd99b22002b"
+      "002099461dcfcdc7804a0f34bf3ca49ac39776a7ef4d8edd30fab3599ff59b09f826002b"
       "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
-      "020101001c00024001001500a80000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
+      "020101001c00024001fe0d00f900000100034d00201da1341e8ba21ff90e025d2438d4e5"
+      "b4e8b376befc57cf8c9afb484e6f051b2f00cff747491b810705e5cc8d8a1302468000d9"
+      "8660d659d8382a6fc23ca1a582def728eabb363771328035565048213b1d725b20f757be"
+      "63d6956cd861aa9d33adcc913de2443695f70e130af96fd2b078dd662478a29bd17a4479"
+      "715c949b5fc118456d0243c9d1819cecd0f5fbd1c78dadd6fcd09abe41ca97a00c97efb3"
+      "894c9d4bab60dcd150b55608f6260723a08e112e39e6a43f645f85a08085054f27f269bc"
+      "1acb9ff5007b04eaef3414767666472e4e24c2a2953f5dc68aeb5207d556f1b872a810b6"
+      "686cf83a09db8b474df70015004200000000000000000000000000000000000000000000"
       "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "000000000000000000000000000000000000000000000000000000000000000000000000"
-      "00fe0a0097000100034d0020d46cc9042eff6efee046a5ff653d1b6a60cfd5786afef5ce"
-      "43300bc515ef5f09006ea6bf626854596df74b2d8f81a479a6d2fef13295a81e0571008a"
-      "12fc92f82170fdb899cd22ebadc33a3147c2801619f7621ffe8684c96918443e3fbe9b17"
-      "34fbf678ba0b2abad7ab6b018bccc1034b9537a5d399fdb9a5ccb92360bde4a94a2f2509"
-      "0e7313dd9254eae3603e1fee";
+      "0000000000000000";
   ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
-                             SSL_ERROR_RX_MALFORMED_CLIENT_HELLO,
+                             SSL_ERROR_RX_UNEXPECTED_EXTENSION,
                              SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
 }
 
-TEST_F(TlsConnectStreamTls13, OuterWithEchAndEchIsInner) {
-  static uint8_t empty_buf[1] = {0};
-  DataBuffer empty(empty_buf, 0);
+TEST_F(TlsConnectStreamTls13, EchWithInnerExtNotSplit) {
+  static uint8_t type_val[1] = {1};
+  DataBuffer type_buffer(type_val, sizeof(type_val));
 
   EnsureTlsSetup();
-  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_FALSE));
   MakeTlsFilter<TlsExtensionAppender>(client_, kTlsHandshakeClientHello,
-                                      ssl_tls13_ech_is_inner_xtn, empty);
+                                      ssl_tls13_encrypted_client_hello_xtn,
+                                      type_buffer);
   ConnectExpectAlert(server_, kTlsAlertIllegalParameter);
   client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
 }
 
-// Apply two ECHConfigs on the server. They are identical with the exception
-// of the public key: the first ECHConfig contains a public key for which we
-// lack the private value. Use an SSLInt function to zero all the config_ids
-// (client and server), then confirm that trial decryption works.
+/* Parameters
+ * Length of SNI for first connection
+ * Length of SNI for second connection
+ * Use GREASE for first connection?
+ * Use GREASE for second connection?
+ * For both connections, SNI length to pad to.
+ */
+class EchCHPaddingTest : public TlsConnectStreamTls13,
+                         public testing::WithParamInterface<
+                             std::tuple<int, int, bool, bool, int>> {};
+
+TEST_P(EchCHPaddingTest, EchChPaddingEqual) {
+  auto parameters = GetParam();
+  std::string name_str1 = std::string(std::get<0>(parameters), 'a');
+  std::string name_str2 = std::string(std::get<1>(parameters), 'a');
+  const char* name1 = name_str1.c_str();
+  const char* name2 = name_str2.c_str();
+  bool grease_mode1 = std::get<2>(parameters);
+  bool grease_mode2 = std::get<3>(parameters);
+  uint8_t max_name_len = std::get<4>(parameters);
+
+  // Connection 1
+  EnsureTlsSetup();
+  SSL_SetURL(client_->ssl_fd(), name1);
+  if (grease_mode1) {
+    EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+    client_->ExpectEch(false);
+    server_->ExpectEch(false);
+  } else {
+    SetupEch(client_, server_, HpkeDhKemX25519Sha256, true, true, true,
+             max_name_len);
+  }
+  auto filter1 = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  Connect();
+  size_t echXtnLen1 = filter1->extension().len();
+
+  Reset();
+
+  // Connection 2
+  EnsureTlsSetup();
+  SSL_SetURL(client_->ssl_fd(), name2);
+  if (grease_mode2) {
+    EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+    client_->ExpectEch(false);
+    server_->ExpectEch(false);
+  } else {
+    SetupEch(client_, server_, HpkeDhKemX25519Sha256, true, true, true,
+             max_name_len);
+  }
+  auto filter2 = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  Connect();
+  size_t echXtnLen2 = filter2->extension().len();
+
+  // We always expect an ECH extension.
+  ASSERT_TRUE(echXtnLen2 > 0 && echXtnLen1 > 0);
+  // We expect the ECH extension to round to the same multiple of 32.
+  // Note: It will not be 0 % 32 because we pad the Payload, but have a number
+  // of extra bytes from the rest of the ECH extension (e.g. ciphersuite)
+  ASSERT_EQ(echXtnLen1 % 32, echXtnLen2 % 32);
+  // Where both connections used the same effective maximum length and both
+  // SNIs are below that maximum, we expect the same size length.
+  PRUint8 effective_len1 =
+      grease_mode1 ? TLS13_ECH_GREASE_SNI_LEN : max_name_len;
+  PRUint8 effective_len2 =
+      grease_mode2 ? TLS13_ECH_GREASE_SNI_LEN : max_name_len;
+  if (effective_len1 == effective_len2 && name_str1.size() <= effective_len1 &&
+      name_str2.size() <= effective_len2) {
+    ASSERT_EQ(echXtnLen1, echXtnLen2);
+  }
+}
+
+#define ECH_PADDING_TEST_INSTANTIATE(name, values)                           \
+  INSTANTIATE_TEST_SUITE_P(name, EchCHPaddingTest,                           \
+                           testing::Combine(values, values, testing::Bool(), \
+                                            testing::Bool(), values))
+
+const int kExtremalSNILengths[] = {1, 128, 255};
+const int kNormalSNILengths[] = {17, 24, 100};
+const int kLongSNILengths[] = {90, 167, 214};
+
+/* Each invocation with N lengths, results in 4N^3 test cases, so we test
+ * 3 lots of (4*3^3) rather than all permutations. */
+ECH_PADDING_TEST_INSTANTIATE(extremal, testing::ValuesIn(kExtremalSNILengths));
+ECH_PADDING_TEST_INSTANTIATE(normal, testing::ValuesIn(kNormalSNILengths));
+ECH_PADDING_TEST_INSTANTIATE(lengthy, testing::ValuesIn(kLongSNILengths));
+
+// Check the server rejects ClientHellos with bad padding
+TEST_F(TlsConnectStreamTls13Ech, EchChPaddingChecked) {
+  // Generate this string by changing the padding in
+  // tls13_GenPaddingClientHelloInner
+  std::string ch =
+      "010001fc03037473367a6eb6773391081b403908fc0c0026aac706889c59ca694d0c1188"
+      "c4b3000006130113031302010001cd00000010000e00000b7075626c69632e6e616d65ff"
+      "01000100000a00140012001d00170018001901000101010201030104003300260024001d"
+      "0020f7d8ad5fea0165e115e984e11c43f1d8f255bd8f772b893432d8d7721e91785a002b"
+      "0003020304000d0018001604030503060302030804080508060401050106010201002d00"
+      "020101001c00024001fe0d00f900000100034d00207e0ad8e83f8a9c89e1ae4fd65b8091"
+      "01e496bbb5f29ce20b299ce58937e2563300cff471a787585e15ae5aff5e4fee7ec988ba"
+      "72f8a95db41e793568b0301d553251f0826dc0c3ff658e4e029ef840ae86fa80af4b11b5"
+      "3a33fab99887bf8df18bc87abbb1f578f7964848d91a2023cbe7609fcc31bd721865009c"
+      "ad68c09e438d677f7c56af76e62c168bdb373bb88962471dacc4ddf654e435cd903f6555"
+      "4c9a93ffd2541cd7bce520e7215d15495184b781ca8c138cedd573fbdef1d40e5de82c33"
+      "5c9c43370102ecb0b66dd27efc719a9a54589b6e6b599b1b0146e121eae0ab5b2070c12f"
+      "4f4f2b099808294a459f0015004200000000000000000000000000000000000000000000"
+      "000000000000000000000000000000000000000000000000000000000000000000000000"
+      "0000000000000000";
+  ReplayChWithMalformedInner(ch, kTlsAlertIllegalParameter,
+                             SSL_ERROR_RX_MALFORMED_ECH_EXTENSION,
+                             SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchConfigList) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  EnsureTlsSetup();
+
+  DataBuffer config1;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kSuiteAes,
+                                        kPublicName, 100, config1, pub, priv);
+  DataBuffer config2;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kSuiteAes,
+                                        kPublicName, 100, config2, pub, priv);
+  DataBuffer configList = MakeEchConfigList(config1, config2);
+  SECStatus rv =
+      SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                              configList.data(), configList.len());
+  printf("%u", rv);
+  ASSERT_EQ(rv, SECSuccess);
+}
+
 TEST_F(TlsConnectStreamTls13Ech, EchConfigsTrialDecrypt) {
+  // Apply two ECHConfigs on the server. They are identical with the exception
+  // of the public key: the first ECHConfig contains a public key for which we
+  // lack the private value. Use an SSLInt function to zero all the config_ids
+  // (client and server), then confirm that trial decryption works.
   ScopedSECKEYPublicKey pub;
   ScopedSECKEYPrivateKey priv;
   EnsureTlsSetup();
   ImportFixedEchKeypair(pub, priv);
-
-  const std::string two_configs_str =
-      "0080FE0A003C000020002011111111111111111111111111111111111111111111111111"
-      "111111111111110004000100010064000B7075626C69632E6E616D650000FE0A003C0000"
-      "2000208756E2580C07C1D2FFCB662F5FADC6D6FF13DA85ABD7ADFECF984AAA102C126900"
-      "04000100010064000B7075626C69632E6E616D650000";
-  const std::string second_config_str =
-      "0040FE0A003C00002000208756E2580C07C1D2FFCB662F5FADC6D6FF13DA85ABD7ADFECF"
-      "984AAA102C12690004000100010064000B7075626C69632E6E616D650000";
-  std::vector<uint8_t> two_configs = hex_string_to_bytes(two_configs_str);
-  std::vector<uint8_t> second_config = hex_string_to_bytes(second_config_str);
+  ScopedSECKEYPublicKey pub2;
+  ScopedSECKEYPrivateKey priv2;
+  DataBuffer config2;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kSuiteAes,
+                                        kPublicName, 100, config2, pub, priv);
+  DataBuffer config1;
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kSuiteAes,
+                                        kPublicName, 100, config1, pub2, priv2);
+  // Zero the config id for both, only public key differs.
+  config2.Write(7, (uint32_t)0, 1);
+  config1.Write(7, (uint32_t)0, 1);
+  // Server only knows private key for conf2
+  DataBuffer configList = MakeEchConfigList(config1, config2);
   ASSERT_EQ(SECSuccess,
             SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
-                                    two_configs.data(), two_configs.size()));
-  ASSERT_EQ(SECSuccess,
-            SSL_SetClientEchConfigs(client_->ssl_fd(), second_config.data(),
-                                    second_config.size()));
-
+                                    configList.data(), configList.len()));
+  ASSERT_EQ(SECSuccess, SSL_SetClientEchConfigs(client_->ssl_fd(),
+                                                config2.data(), config2.len()));
   client_->ExpectEch();
   server_->ExpectEch();
   Connect();
 }
 
 TEST_F(TlsConnectStreamTls13Ech, EchAcceptBasic) {
   EnsureTlsSetup();
   SetupEch(client_, server_);
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
-
   auto c_filter_sni =
       MakeTlsFilter<TlsExtensionCapture>(client_, ssl_server_name_xtn);
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
-
   Connect();
   ASSERT_TRUE(c_filter_sni->captured());
   CheckSniExtension(c_filter_sni->extension(), kPublicName);
 }
 
 TEST_F(TlsConnectStreamTls13, EchAcceptWithResume) {
   EnsureTlsSetup();
   SetupEch(client_, server_);
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
   ConfigureSessionCache(RESUME_BOTH, RESUME_TICKET);
   Connect();
   SendReceive();  // Need to read so that we absorb the session ticket.
   CheckKeys();
 
   Reset();
   EnsureTlsSetup();
   SetupEch(client_, server_);
   ExpectResumption(RESUME_TICKET);
   auto filter =
       MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_pre_shared_key_xtn);
   StartConnect();
   Handshake();
   CheckConnected();
   // Make sure that the PSK extension is only in CHInner.
-  ASSERT_FALSE(filter->captured());
+  ASSERT_TRUE(filter->captured());
 }
 
 TEST_F(TlsConnectStreamTls13, EchAcceptWithExternalPsk) {
+  static const std::string kPskId = "testing123";
   EnsureTlsSetup();
   SetupEch(client_, server_);
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
 
   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
   ASSERT_TRUE(!!slot);
   ScopedPK11SymKey key(
       PK11_KeyGen(slot.get(), CKM_HKDF_KEY_GEN, nullptr, 16, nullptr));
   ASSERT_TRUE(!!key);
-  AddPsk(key, std::string("foo"), ssl_hash_sha256);
+  AddPsk(key, kPskId, ssl_hash_sha256);
 
   // Not permitted in outer.
   auto filter =
       MakeTlsFilter<TlsExtensionCapture>(client_, ssl_tls13_pre_shared_key_xtn);
   StartConnect();
   Handshake();
   CheckConnected();
   SendReceive();
   CheckKeys(ssl_kea_ecdh, ssl_grp_ec_curve25519, ssl_auth_psk, ssl_sig_none);
-  // Make sure that the PSK extension is only in CHInner.
-  ASSERT_FALSE(filter->captured());
+  // The PSK extension is present in CHOuter.
+  ASSERT_TRUE(filter->captured());
+
+  // But the PSK in CHOuter is completely different.
+  // (Failure/collision chance means kPskId needs to be longish.)
+  uint32_t v = 0;
+  ASSERT_TRUE(filter->extension().Read(0, 2, &v));
+  ASSERT_EQ(v, kPskId.size() + 2 + 4) << "check size of identities";
+  ASSERT_TRUE(filter->extension().Read(2, 2, &v));
+  ASSERT_EQ(v, kPskId.size()) << "check size of identity";
+  bool different = false;
+  for (size_t i = 0; i < kPskId.size(); ++i) {
+    ASSERT_TRUE(filter->extension().Read(i + 4, 1, &v));
+    different |= v != static_cast<uint8_t>(kPskId[i]);
+  }
+  ASSERT_TRUE(different);
 }
 
 // If an earlier version is negotiated, False Start must be disabled.
 TEST_F(TlsConnectStreamTls13, EchDowngradeNoFalseStart) {
   EnsureTlsSetup();
   SetupEch(client_, server_, HpkeDhKemX25519Sha256, false, true, false);
   MakeTlsFilter<TlsExtensionDropper>(client_,
                                      ssl_tls13_encrypted_client_hello_xtn);
@@ -916,40 +1164,155 @@ TEST_F(TlsConnectStreamTls13, EchAcceptW
   ASSERT_EQ(SECSuccess,
             SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
                                     echconfig.data(), echconfig.len()));
   ASSERT_EQ(SECSuccess,
             SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
                                     echconfig.len()));
   client_->ExpectEch();
   server_->ExpectEch();
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
 
   size_t cb_called = 0;
   EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
                             server_->ssl_fd(), RetryEchHello, &cb_called));
 
+  auto server_hrr_ech_xtn = MakeTlsFilter<TlsExtensionCapture>(
+      server_, ssl_tls13_encrypted_client_hello_xtn);
   // Start the handshake.
   client_->StartConnect();
   server_->StartConnect();
   client_->Handshake();
   server_->Handshake();
   MakeNewServer();
   ASSERT_EQ(SECSuccess,
             SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
                                     echconfig.data(), echconfig.len()));
   client_->ExpectEch();
   server_->ExpectEch();
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
   Handshake();
+  ASSERT_TRUE(server_hrr_ech_xtn->captured());
   EXPECT_EQ(1U, cb_called);
   CheckConnected();
   SendReceive();
 }
 
+TEST_F(TlsConnectStreamTls13Ech, EchGreaseSize) {
+  EnsureTlsSetup();
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+
+  auto greased_ext = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  Connect();
+  ASSERT_TRUE(greased_ext->captured());
+
+  Reset();
+  EnsureTlsSetup();
+
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  ImportFixedEchKeypair(pub, priv);
+  SetMutualEchConfigs(pub, priv);
+
+  auto real_ext = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+
+  ASSERT_TRUE(real_ext->captured());
+  ASSERT_EQ(real_ext->extension().len(), greased_ext->extension().len());
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchGreaseClientDisable) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer echconfig;
+  EnsureTlsSetup();
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
+                                        kPublicName, 100, echconfig, pub, priv);
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_FALSE));
+
+  auto c_filter_esni = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+
+  Connect();
+  ASSERT_TRUE(!c_filter_esni->captured());
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchHrrGreaseServerDisable) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer echconfig;
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(server_->ssl_fd(), PR_FALSE));
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), RetryEchHello, &cb_called));
+
+  auto server_hrr_ech_xtn = MakeTlsFilter<TlsExtensionCapture>(
+      server_, ssl_tls13_encrypted_client_hello_xtn);
+  // Start the handshake.
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  MakeNewServer();
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(server_->ssl_fd(), PR_FALSE));
+  Handshake();
+  ASSERT_TRUE(!server_hrr_ech_xtn->captured());
+  EXPECT_EQ(1U, cb_called);
+  CheckConnected();
+  SendReceive();
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchGreaseSizePsk) {
+  // Original connection without ECH
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  Connect();
+  SendReceive();
+
+  // Resumption with only GREASE
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  ExpectResumption(RESUME_TICKET);
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+
+  auto greased_ext = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  Connect();
+  SendReceive();
+  ASSERT_TRUE(greased_ext->captured());
+
+  // Finally, resume with ECH enabled
+  // ECH state does not determine whether resumption succeeds
+  // or is attempted, so this should work fine.
+  Reset();
+  ConfigureSessionCache(RESUME_BOTH, RESUME_BOTH);
+  ExpectResumption(RESUME_TICKET, 2);
+
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  ImportFixedEchKeypair(pub, priv);
+  SetMutualEchConfigs(pub, priv);
+
+  auto real_ext = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+  ASSERT_TRUE(real_ext->captured());
+
+  ASSERT_EQ(real_ext->extension().len(), greased_ext->extension().len());
+}
+
 // Send GREASE ECH in CH1. CH2 must send exactly the same GREASE ECH contents.
 TEST_F(TlsConnectStreamTls13, GreaseEchHrrMatches) {
   ConfigureSelfEncrypt();
   EnsureTlsSetup();
   size_t cb_called = 0;
   EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
                             server_->ssl_fd(), RetryEchHello, &cb_called));
 
@@ -976,16 +1339,175 @@ TEST_F(TlsConnectStreamTls13, GreaseEchH
   EXPECT_EQ(ch1_grease, capture->extension());
 
   EXPECT_EQ(1U, cb_called);
   server_->StartConnect();
   Handshake();
   CheckConnected();
 }
 
+TEST_F(TlsConnectStreamTls13Ech, EchRejectMisizedEchXtn) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer echconfig;
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(server_->ssl_fd(), PR_TRUE));
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), RetryEchHello, &cb_called));
+  auto server_hrr_ext_xtn_fake = MakeTlsFilter<TlsExtensionResizer>(
+      server_, ssl_tls13_encrypted_client_hello_xtn, 34);
+  // Start the handshake.
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  // Process the hello retry.
+  server_->ExpectReceiveAlert(kTlsAlertDecodeError, kTlsAlertFatal);
+  client_->ExpectSendAlert(kTlsAlertDecodeError);
+  Handshake();
+  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
+  server_->CheckErrorCode(SSL_ERROR_DECODE_ERROR_ALERT);
+  EXPECT_EQ(1U, cb_called);
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchRejectDroppedEchXtn) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer echconfig;
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
+                                        kPublicName, 100, echconfig, pub, priv);
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  ASSERT_EQ(SECSuccess,
+            SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+                                    echconfig.len()));
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), RetryEchHello, &cb_called));
+  auto server_hrr_ext_xtn_fake = MakeTlsFilter<TlsExtensionDropper>(
+      server_, ssl_tls13_encrypted_client_hello_xtn);
+  // Start the handshake.
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  MakeNewServer();
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  // Process the hello retry.
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  Handshake();
+  client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+  server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+  EXPECT_EQ(1U, cb_called);
+}
+
+// Generate an HRR on CHInner. Mangle the Hrr Xtn causing client to reject ECH
+// which then causes a MAC mismatch.
+TEST_F(TlsConnectStreamTls13Ech, EchRejectMangledHrrXtn) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer echconfig;
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
+                                        kPublicName, 100, echconfig, pub, priv);
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  ASSERT_EQ(SECSuccess,
+            SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+                                    echconfig.len()));
+
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), RetryEchHello, &cb_called));
+  auto server_hrr_ech_xtn = MakeTlsFilter<TlsExtensionDamager>(
+      server_, ssl_tls13_encrypted_client_hello_xtn, 4);
+  // Start the handshake.
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  MakeNewServer();
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  client_->ExpectEch(false);
+  server_->ExpectEch(false);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  Handshake();
+  client_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+  server_->CheckErrorCode(SSL_ERROR_BAD_MAC_READ);
+  EXPECT_EQ(1U, cb_called);
+}
+
+// First capture an ECH CH Xtn.
+// Start new connection, inject ECH CH Xtn.
+// Server will respond with ECH HRR Xtn.
+// Check Client correctly panics.
+TEST_F(TlsConnectStreamTls13Ech, EchClientRejectSpuriousHrrXtn) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer echconfig;
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
+                                        kPublicName, 100, echconfig, pub, priv);
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+  client_->ExpectEch(false);
+  server_->ExpectEch(false);
+  auto client_ech_xtn_capture = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  Connect();
+  ASSERT_TRUE(client_ech_xtn_capture->captured());
+
+  // Now configure client without ECH. Server with ECH.
+  Reset();
+  EnsureTlsSetup();
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_FALSE));
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  client_->ExpectEch(false);
+  server_->ExpectEch(false);
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), RetryEchHello, &cb_called));
+
+  // Inject CH ECH Xtn into CH.
+  DataBuffer buff = DataBuffer(client_ech_xtn_capture->extension());
+  auto client_ech_xtn = MakeTlsFilter<TlsExtensionAppender>(
+      client_, kTlsHandshakeClientHello, ssl_tls13_encrypted_client_hello_xtn,
+      buff);
+
+  // Connect and check we see the HRR extension and alert.
+  auto server_hrr_ech_xtn = MakeTlsFilter<TlsExtensionCapture>(
+      server_, ssl_tls13_encrypted_client_hello_xtn);
+  server_hrr_ech_xtn->SetHandshakeTypes({kTlsHandshakeHelloRetryRequest});
+
+  ConnectExpectAlert(client_, kTlsAlertUnsupportedExtension);
+
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+  server_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_EXTENSION_ALERT);
+  ASSERT_TRUE(server_hrr_ech_xtn->captured());
+}
+
 // Fail to decrypt CH2. Unlike CH1, this generates an alert.
 TEST_F(TlsConnectStreamTls13, EchFailDecryptCH2) {
   EnsureTlsSetup();
   SetupEch(client_, server_);
   size_t cb_called = 0;
   EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
                             server_->ssl_fd(), RetryEchHello, &cb_called));
 
@@ -1066,19 +1588,19 @@ TEST_F(TlsConnectStreamTls13, EchHrrChan
                             server_->ssl_fd(), RetryEchHello, &cb_called));
   // Start the handshake and trigger HRR.
   client_->StartConnect();
   server_->StartConnect();
   client_->Handshake();
   server_->Handshake();
   MakeNewServer();
 
-  // Damage the first byte of the ciphersuite (offset 0)
+  // Damage the first byte of the ciphersuite (offset 1)
   MakeTlsFilter<TlsExtensionDamager>(client_,
-                                     ssl_tls13_encrypted_client_hello_xtn, 0);
+                                     ssl_tls13_encrypted_client_hello_xtn, 1);
 
   ExpectAlert(server_, kTlsAlertIllegalParameter);
   Handshake();
   client_->CheckErrorCode(SSL_ERROR_ILLEGAL_PARAMETER_ALERT);
   server_->CheckErrorCode(SSL_ERROR_BAD_2ND_CLIENT_HELLO);
   EXPECT_EQ(1U, cb_called);
 }
 
@@ -1147,32 +1669,130 @@ TEST_F(TlsConnectStreamTls13Ech, EchReje
   DataBuffer echconfig;
   ConfigureSelfEncrypt();
   EnsureTlsSetup();
   SetupForEchRetry();
 
   size_t cb_called = 0;
   EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
                             server_->ssl_fd(), RetryEchHello, &cb_called));
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(server_->ssl_fd(), PR_TRUE));
+  auto server_hrr_ech_xtn = MakeTlsFilter<TlsExtensionCapture>(
+      server_, ssl_tls13_encrypted_client_hello_xtn);
+  // Start the handshake.
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  MakeNewServer();
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(server_->ssl_fd(), PR_TRUE));
+  client_->ExpectEch(false);
+  server_->ExpectEch(false);
+  ExpectAlert(client_, kTlsAlertEchRequired);
+  Handshake();
+  ASSERT_TRUE(server_hrr_ech_xtn->captured());
+  client_->CheckErrorCode(SSL_ERROR_ECH_RETRY_WITHOUT_ECH);
+  server_->ExpectReceiveAlert(kTlsAlertEchRequired, kTlsAlertFatal);
+  server_->Handshake();
+  EXPECT_EQ(1U, cb_called);
+}
 
+// Server can't change its mind on ECH after HRR. We change the confirmation
+// value and the server panics accordingly.
+TEST_F(TlsConnectStreamTls13Ech, EchHrrServerYN) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer echconfig;
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+  TlsConnectTestBase::GenerateEchConfig(HpkeDhKemX25519Sha256, kDefaultSuites,
+                                        kPublicName, 100, echconfig, pub, priv);
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  ASSERT_EQ(SECSuccess,
+            SSL_SetClientEchConfigs(client_->ssl_fd(), echconfig.data(),
+                                    echconfig.len()));
+  client_->ExpectEch();
+  server_->ExpectEch();
+
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), RetryEchHello, &cb_called));
+
+  auto server_hrr_ech_xtn = MakeTlsFilter<TlsExtensionCapture>(
+      server_, ssl_tls13_encrypted_client_hello_xtn);
   // Start the handshake.
   client_->StartConnect();
   server_->StartConnect();
   client_->Handshake();
   server_->Handshake();
   MakeNewServer();
-  client_->ExpectEch(false);
-  server_->ExpectEch(false);
-  ExpectAlert(client_, kTlsAlertEchRequired);
+  ASSERT_EQ(SECSuccess,
+            SSL_SetServerEchConfigs(server_->ssl_fd(), pub.get(), priv.get(),
+                                    echconfig.data(), echconfig.len()));
+  client_->ExpectEch();
+  server_->ExpectEch();
+  client_->ExpectSendAlert(kTlsAlertIllegalParameter);
+  server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+  auto server_random_damager = MakeTlsFilter<ServerHelloRandomChanger>(server_);
   Handshake();
-  client_->CheckErrorCode(SSL_ERROR_ECH_RETRY_WITHOUT_ECH);
-  server_->ExpectReceiveAlert(kTlsAlertEchRequired, kTlsAlertFatal);
+  client_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
+  ASSERT_TRUE(server_hrr_ech_xtn->captured());
+  EXPECT_EQ(1U, cb_called);
+}
+
+// Client sends GREASE'd ECH Xtn, server reponds with HRR in GREASE mode
+// Check HRR responses are present and differ.
+TEST_F(TlsConnectStreamTls13Ech, EchHrrServerGreaseChanges) {
+  ScopedSECKEYPublicKey pub;
+  ScopedSECKEYPrivateKey priv;
+  DataBuffer echconfig;
+  ConfigureSelfEncrypt();
+  EnsureTlsSetup();
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(server_->ssl_fd(), PR_TRUE));
+  size_t cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), RetryEchHello, &cb_called));
+
+  auto server_hrr_ech_xtn_1 = MakeTlsFilter<TlsExtensionCapture>(
+      server_, ssl_tls13_encrypted_client_hello_xtn);
+  // Start the handshake.
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
   server_->Handshake();
+  ASSERT_TRUE(server_hrr_ech_xtn_1->captured());
   EXPECT_EQ(1U, cb_called);
+
+  /* Run the connection again */
+  Reset();
+  EnsureTlsSetup();
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(server_->ssl_fd(), PR_TRUE));
+  EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_TRUE));
+  cb_called = 0;
+  EXPECT_EQ(SECSuccess, SSL_HelloRetryRequestCallback(
+                            server_->ssl_fd(), RetryEchHello, &cb_called));
+
+  auto server_hrr_ech_xtn_2 = MakeTlsFilter<TlsExtensionCapture>(
+      server_, ssl_tls13_encrypted_client_hello_xtn);
+  // Start the handshake.
+  client_->StartConnect();
+  server_->StartConnect();
+  client_->Handshake();
+  server_->Handshake();
+  ASSERT_TRUE(server_hrr_ech_xtn_2->captured());
+  EXPECT_EQ(1U, cb_called);
+
+  ASSERT_TRUE(server_hrr_ech_xtn_1->extension().len() ==
+              server_hrr_ech_xtn_2->extension().len());
+  ASSERT_TRUE(memcmp(server_hrr_ech_xtn_1->extension().data(),
+                     server_hrr_ech_xtn_2->extension().data(),
+                     server_hrr_ech_xtn_1->extension().len()));
 }
 
 // Reject ECH on CH1 and CH2. PSKs are no longer allowed
 // in CHOuter, but we can still make sure the handshake succeeds.
 // This prompts an ech_required alert when the handshake completes.
 TEST_F(TlsConnectStreamTls13, EchRejectWithHrrAndPsk) {
   ScopedSECKEYPublicKey pub;
   ScopedSECKEYPrivateKey priv;
@@ -1292,17 +1912,16 @@ TEST_F(TlsConnectStreamTls13, EchZeroRtt
   EnsureTlsSetup();
   SetupEch(client_, server_);
   SetupForZeroRtt();
   client_->Set0RttEnabled(true);
   server_->Set0RttEnabled(true);
 
   // Setup ECH only on the client.
   SetupEch(client_, server_, HpkeDhKemX25519Sha256, false, true, false);
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
 
   ExpectResumption(RESUME_NONE);
   ExpectAlert(client_, kTlsAlertEchRequired);
   ZeroRttSendReceive(true, false);
   server_->Handshake();
   client_->Handshake();
   client_->CheckErrorCode(SSL_ERROR_ECH_RETRY_WITHOUT_ECH);
 
@@ -1377,32 +1996,30 @@ TEST_F(TlsConnectStreamTls13, EchRejectU
   ASSERT_EQ(TlsAgent::STATE_CONNECTING, client_->state());
   ASSERT_TRUE(filter->captured());
 }
 
 // Secure disable without ECH
 TEST_F(TlsConnectStreamTls13, EchRejectAuthCertSuccessNoRetries) {
   EnsureTlsSetup();
   SetupEch(client_, server_, HpkeDhKemX25519Sha256, false, true, false);
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
   ExpectAlert(client_, kTlsAlertEchRequired);
   ConnectExpectFailOneSide(TlsAgent::CLIENT);
   client_->CheckErrorCode(SSL_ERROR_ECH_RETRY_WITHOUT_ECH);
   server_->ExpectReceiveAlert(kTlsAlertEchRequired, kTlsAlertFatal);
   server_->Handshake();
   // Reset expectations for the TlsAgent dtor.
   server_->ExpectReceiveAlert(kTlsAlertCloseNotify, kTlsAlertWarning);
 }
 
 // When authenticating to the public name, the client MUST NOT
 // send a certificate in response to a certificate request.
 TEST_F(TlsConnectStreamTls13, EchRejectSuppressClientCert) {
   EnsureTlsSetup();
   SetupEch(client_, server_, HpkeDhKemX25519Sha256, false, true, false);
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
   client_->SetupClientAuth();
   server_->RequestClientAuth(true);
   auto cert_capture =
       MakeTlsFilter<TlsHandshakeRecorder>(client_, kTlsHandshakeCertificate);
   cert_capture->EnableDecryption();
 
   StartConnect();
   client_->ExpectSendAlert(kTlsAlertEchRequired);
@@ -1440,17 +2057,16 @@ TEST_F(TlsConnectStreamTls13, EchRejectA
 
   // Change the first ECHConfig version to one we don't understand.
   server_rec.Write(2, 0xfefe, 2);
   // Skip the ECHConfigs length, the server sender will re-encode.
   ASSERT_EQ(SECSuccess, SSLInt_SetRawEchConfigForRetry(server_->ssl_fd(),
                                                        &server_rec.data()[2],
                                                        server_rec.len() - 2));
 
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
   ExpectAlert(client_, kTlsAlertEchRequired);
   ConnectExpectFailOneSide(TlsAgent::CLIENT);
   client_->CheckErrorCode(SSL_ERROR_ECH_RETRY_WITHOUT_ECH);
   server_->ExpectReceiveAlert(kTlsAlertEchRequired, kTlsAlertFatal);
   server_->Handshake();
   // Reset expectations for the TlsAgent dtor.
   server_->ExpectReceiveAlert(kTlsAlertCloseNotify, kTlsAlertWarning);
 }
@@ -1553,17 +2169,16 @@ TEST_F(TlsConnectStreamTls13Ech, EchMism
   ASSERT_EQ(SECSuccess,
             SSL_SetServerEchConfigs(server_->ssl_fd(), server_pub.get(),
                                     server_priv.get(), server_rec.data(),
                                     server_rec.len()));
   ASSERT_EQ(SECSuccess,
             SSL_SetClientEchConfigs(client_->ssl_fd(), client_rec.data(),
                                     client_rec.len()));
 
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
   ExpectAlert(client_, kTlsAlertEchRequired);
   ConnectExpectFailOneSide(TlsAgent::CLIENT);
   client_->CheckErrorCode(SSL_ERROR_ECH_RETRY_WITH_ECH);
   server_->ExpectReceiveAlert(kTlsAlertEchRequired, kTlsAlertFatal);
   server_->Handshake();
   DoEchRetry(server_pub, server_priv, server_rec);
 }
 
@@ -1585,17 +2200,16 @@ TEST_F(TlsConnectStreamTls13Ech, EchMism
   ASSERT_EQ(SECSuccess,
             SSL_SetServerEchConfigs(server_->ssl_fd(), server_pub.get(),
                                     server_priv.get(), server_rec.data(),
                                     server_rec.len()));
   ASSERT_EQ(SECSuccess,
             SSL_SetClientEchConfigs(client_->ssl_fd(), client_rec.data(),
                                     client_rec.len()));
 
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
   client_->ExpectSendAlert(kTlsAlertEchRequired);
   ConnectExpectFailOneSide(TlsAgent::CLIENT);
   client_->CheckErrorCode(SSL_ERROR_ECH_RETRY_WITH_ECH);
   server_->ExpectReceiveAlert(kTlsAlertEchRequired, kTlsAlertFatal);
   server_->Handshake();
   DoEchRetry(server_pub, server_priv, server_rec);
 }
 
@@ -1672,47 +2286,62 @@ TEST_F(TlsConnectStreamTls13, EchBadCiph
   ConnectExpectFail();
 }
 
 // Test a tampered CHOuter (decrypt failure on AAD).
 // Expect negotiation on outer, which fails due to the tampered transcript.
 TEST_F(TlsConnectStreamTls13, EchOuterBinding) {
   EnsureTlsSetup();
   SetupEch(client_, server_);
-  client_->SetAuthCertificateCallback(AuthCompleteSuccess);
   client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
                            SSL_LIBRARY_VERSION_TLS_1_3);
 
   static const uint8_t supported_vers_13[] = {0x02, 0x03, 0x04};
   DataBuffer buf(supported_vers_13, sizeof(supported_vers_13));
   MakeTlsFilter<TlsExtensionReplacer>(client_, ssl_tls13_supported_versions_xtn,
                                       buf);
   client_->ExpectSendAlert(kTlsAlertBadRecordMac);
   server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
 }
 
+// Altering the CH after the Ech Xtn should also cause a failure.
+TEST_F(TlsConnectStreamTls13, EchOuterBindingAfterXtn) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+  client_->SetVersionRange(SSL_LIBRARY_VERSION_TLS_1_2,
+                           SSL_LIBRARY_VERSION_TLS_1_3);
+
+  static const uint8_t supported_vers_13[] = {0x02, 0x03, 0x04};
+  DataBuffer buf(supported_vers_13, sizeof(supported_vers_13));
+  MakeTlsFilter<TlsExtensionAppender>(client_, kTlsHandshakeClientHello, 5044,
+                                      buf);
+  client_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  server_->ExpectSendAlert(kTlsAlertBadRecordMac);
+  ConnectExpectFail();
+}
+
 // Test a bad (unknown) ECHCipherSuite.
 // Expect negotiation on outer, which fails due to the tampered transcript.
 TEST_F(TlsConnectStreamTls13, EchBadCiphersuite) {
   EnsureTlsSetup();
   SetupEch(client_, server_);
   /* Make KDF unknown */
   MakeTlsFilter<TlsExtensionDamager>(client_,
-                                     ssl_tls13_encrypted_client_hello_xtn, 0);
+                                     ssl_tls13_encrypted_client_hello_xtn, 1);
   client_->ExpectSendAlert(kTlsAlertBadRecordMac);
   server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
 
   Reset();
   EnsureTlsSetup();
   SetupEch(client_, server_);
   /* Make AEAD unknown */
   MakeTlsFilter<TlsExtensionDamager>(client_,
-                                     ssl_tls13_encrypted_client_hello_xtn, 3);
+                                     ssl_tls13_encrypted_client_hello_xtn, 4);
   client_->ExpectSendAlert(kTlsAlertBadRecordMac);
   server_->ExpectSendAlert(kTlsAlertBadRecordMac);
   ConnectExpectFail();
 }
 
 // Connect to a 1.2 server, it should ignore ECH.
 TEST_F(TlsConnectStreamTls13, EchToTls12Server) {
   EnsureTlsSetup();
@@ -1782,41 +2411,370 @@ TEST_F(TlsConnectStreamTls13, EchOuterEx
                                       ssl_tls13_outer_extensions_xtn,
                                       outer_buf);
 
   ConnectExpectAlert(server_, kTlsAlertUnsupportedExtension);
   client_->CheckErrorCode(SSL_ERROR_UNSUPPORTED_EXTENSION_ALERT);
   server_->CheckErrorCode(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
 }
 
+static SECStatus NoopExtensionHandler(PRFileDesc* fd, SSLHandshakeType message,
+                                      const PRUint8* data, unsigned int len,
+                                      SSLAlertDescription* alert, void* arg) {
+  return SECSuccess;
+}
+
+static PRBool EmptyExtensionWriter(PRFileDesc* fd, SSLHandshakeType message,
+                                   PRUint8* data, unsigned int* len,
+                                   unsigned int maxLen, void* arg) {
+  return true;
+}
+
+static PRBool LargeExtensionWriter(PRFileDesc* fd, SSLHandshakeType message,
+                                   PRUint8* data, unsigned int* len,
+                                   unsigned int maxLen, void* arg) {
+  unsigned int length = 1024;
+  PR_ASSERT(length <= maxLen);
+  memset(data, 0, length);
+  *len = length;
+  return true;
+}
+
+static PRBool OuterOnlyExtensionWriter(PRFileDesc* fd, SSLHandshakeType message,
+                                       PRUint8* data, unsigned int* len,
+                                       unsigned int maxLen, void* arg) {
+  if (message == ssl_hs_ech_outer_client_hello) {
+    return LargeExtensionWriter(fd, message, data, len, maxLen, arg);
+  }
+  return false;
+}
+
+static PRBool InnerOnlyExtensionWriter(PRFileDesc* fd, SSLHandshakeType message,
+                                       PRUint8* data, unsigned int* len,
+                                       unsigned int maxLen, void* arg) {
+  if (message == ssl_hs_client_hello) {
+    return LargeExtensionWriter(fd, message, data, len, maxLen, arg);
+  }
+  return false;
+}
+
+static PRBool InnerOuterDiffExtensionWriter(PRFileDesc* fd,
+                                            SSLHandshakeType message,
+                                            PRUint8* data, unsigned int* len,
+                                            unsigned int maxLen, void* arg) {
+  unsigned int length = 1024;
+  PR_ASSERT(length <= maxLen);
+  memset(data, (message == ssl_hs_client_hello) ? 1 : 0, length);
+  *len = length;
+  return true;
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchCustomExtensionWriter) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, EmptyExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchCustomExtensionWriterOuterOnly) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, OuterOnlyExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchCustomExtensionWriterInnerOnly) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, InnerOnlyExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+}
+
+// Write different values to inner and outer CH.
+TEST_F(TlsConnectStreamTls13Ech, EchCustomExtensionWriterDifferent) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  EXPECT_EQ(SECSuccess,
+            SSL_InstallExtensionHooks(client_->ssl_fd(), 62028,
+                                      InnerOuterDiffExtensionWriter, nullptr,
+                                      NoopExtensionHandler, nullptr));
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+  ASSERT_TRUE(filter->extension().len() > 1024);
+}
+
+// Test that basic compression works
+TEST_F(TlsConnectStreamTls13Ech, EchCustomExtensionWriterCompressionBasic) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  // This will be compressed.
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+  size_t echXtnLen = filter->extension().len();
+  ASSERT_TRUE(echXtnLen > 0 && echXtnLen < 1024);
+}
+
+// Test that compression works when things change.
+TEST_F(TlsConnectStreamTls13Ech,
+       EchCustomExtensionWriterCompressSomeDifferent) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  // This will be compressed.
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  // This can't be.
+  EXPECT_EQ(SECSuccess,
+            SSL_InstallExtensionHooks(client_->ssl_fd(), 62029,
+                                      InnerOuterDiffExtensionWriter, nullptr,
+                                      NoopExtensionHandler, nullptr));
+  // This will be compressed.
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62030, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+  auto echXtnLen = filter->extension().len();
+  /* Exactly one custom xtn plus change */
+  ASSERT_TRUE(echXtnLen > 1024 && echXtnLen < 2048);
+}
+
+// An outer-only extension stops compression.
+TEST_F(TlsConnectStreamTls13Ech,
+       EchCustomExtensionWriterCompressSomeOuterOnly) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  // This will be compressed.
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  // This can't be as it appears in the outer only.
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62029, OuterOnlyExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  // This will be compressed
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62030, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+  size_t echXtnLen = filter->extension().len();
+  ASSERT_TRUE(echXtnLen > 0 && echXtnLen < 1024);
+}
+
+// An inner only extension does not stop compression.
+TEST_F(TlsConnectStreamTls13Ech, EchCustomExtensionWriterCompressAllInnerOnly) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  // This will be compressed.
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  // This can't be as it appears in the inner only.
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62029, InnerOnlyExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  // This will be compressed.
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62030, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(
+      client_, ssl_tls13_encrypted_client_hello_xtn);
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+  size_t echXtnLen = filter->extension().len();
+  ASSERT_TRUE(echXtnLen > 1024 && echXtnLen < 2048);
+}
+
+TEST_F(TlsConnectStreamTls13Ech, EchAcceptCustomXtn) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            server_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(server_, 62028);
+  client_->ExpectEch();
+  server_->ExpectEch();
+  Connect();
+}
+
+// Test that we reject Outer Xtn in SH if accepting ECH Inner
+TEST_F(TlsConnectStreamTls13Ech, EchRejectOuterXtnOnInner) {
+  EnsureTlsSetup();
+  SetupEch(client_, server_);
+
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, OuterOnlyExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+
+  // Put the same extension on the Server Hello
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            server_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(server_, 62028);
+  client_->ExpectEch(false);
+  server_->ExpectEch(false);
+  client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+  // The server will be expecting an alert encrypted under a different key.
+  server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+  ConnectExpectFail();
+  ASSERT_TRUE(filter->captured());
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+}
+
+// Test that we reject Inner Xtn in SH if accepting ECH Outer
+TEST_F(TlsConnectStreamTls13Ech, EchRejectInnerXtnOnOuter) {
+  EnsureTlsSetup();
+
+  // Setup ECH only on the client
+  SetupEch(client_, server_, HpkeDhKemX25519Sha256, false, true, false);
+
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, InnerOnlyExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+
+  // Put the same extension on the Server Hello
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            server_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(server_, 62028);
+  client_->ExpectEch(false);
+  server_->ExpectEch(false);
+  client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+  // The server will be expecting an alert encrypted under a different key.
+  server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+  ConnectExpectFail();
+  ASSERT_TRUE(filter->captured());
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+}
+
+// Test that we reject an Inner Xtn in SH, if accepting Ech Inner and
+// we didn't advertise it on SH Outer.
+TEST_F(TlsConnectStreamTls13Ech, EchRejectInnerXtnNotOnOuter) {
+  EnsureTlsSetup();
+
+  // Setup ECH only on the client
+  SetupEch(client_, server_);
+
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            client_->ssl_fd(), 62028, InnerOnlyExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+
+  EXPECT_EQ(SECSuccess,
+            SSL_CallExtensionWriterOnEchInner(client_->ssl_fd(), true));
+
+  // Put the same extension on the Server Hello
+  EXPECT_EQ(SECSuccess, SSL_InstallExtensionHooks(
+                            server_->ssl_fd(), 62028, LargeExtensionWriter,
+                            nullptr, NoopExtensionHandler, nullptr));
+  auto filter = MakeTlsFilter<TlsExtensionCapture>(server_, 62028);
+  client_->ExpectEch(false);
+  server_->ExpectEch(false);
+  client_->ExpectSendAlert(kTlsAlertUnsupportedExtension);
+  // The server will be expecting an alert encrypted under a different key.
+  server_->ExpectSendAlert(kTlsAlertUnexpectedMessage);
+  ConnectExpectFail();
+  ASSERT_TRUE(filter->captured());
+  client_->CheckErrorCode(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+}
+
 // At draft-09: If a CH containing the ech_is_inner extension is received, the
 // server acts as backend server in split-mode by responding with the ECH
 // acceptance signal. The signal value itself depends on the handshake secret,
 // which we've broken by appending ech_is_inner. For now, just check that the
 // server negotiates ech_is_inner (which is what triggers sending the signal).
 TEST_F(TlsConnectStreamTls13, EchBackendAcceptance) {
   DataBuffer ch_buf;
-  static uint8_t empty_buf[1] = {0};
-  DataBuffer empty(empty_buf, 0);
+  static uint8_t inner_value[1] = {1};
+  DataBuffer inner_buffer(inner_value, sizeof(inner_value));
 
   EnsureTlsSetup();
   StartConnect();
   EXPECT_EQ(SECSuccess, SSL_EnableTls13GreaseEch(client_->ssl_fd(), PR_FALSE));
   MakeTlsFilter<TlsExtensionAppender>(client_, kTlsHandshakeClientHello,
-                                      ssl_tls13_ech_is_inner_xtn, empty);
+                                      ssl_tls13_encrypted_client_hello_xtn,
+                                      inner_buffer);
 
   EXPECT_EQ(SECSuccess, SSL_EnableTls13BackendEch(server_->ssl_fd(), PR_TRUE));
   client_->Handshake();
   server_->Handshake();
 
   ExpectAlert(client_, kTlsAlertBadRecordMac);
   client_->Handshake();
   EXPECT_EQ(TlsAgent::STATE_ERROR, client_->state());
-  EXPECT_EQ(PR_TRUE, SSLInt_ExtensionNegotiated(server_->ssl_fd(),
-                                                ssl_tls13_ech_is_inner_xtn));
+  EXPECT_EQ(PR_TRUE,
+            SSLInt_ExtensionNegotiated(server_->ssl_fd(),
+                                       ssl_tls13_encrypted_client_hello_xtn));
   server_->ExpectReceiveAlert(kTlsAlertCloseNotify, kTlsAlertWarning);
 }
 
 // A public_name that includes an IP address has to be rejected.
 TEST_F(TlsConnectStreamTls13Ech, EchPublicNameIp) {
   static const std::vector<std::string> kIps = {
       "0.0.0.0",
       "1.1.1.1",
--- a/security/nss/gtests/ssl_gtest/tls_filter.cc
+++ b/security/nss/gtests/ssl_gtest/tls_filter.cc
@@ -1219,16 +1219,27 @@ PacketFilter::Action SelectedCipherSuite
   size_t pos = 34;
   EXPECT_TRUE(input.Read(pos, 1, &temp));
   pos += 1 + temp;
 
   output->Write(pos, static_cast<uint32_t>(cipher_suite_), 2);
   return CHANGE;
 }
 
+PacketFilter::Action ServerHelloRandomChanger::FilterHandshake(
+    const HandshakeHeader& header, const DataBuffer& input,
+    DataBuffer* output) {
+  *output = input;
+  uint32_t temp = 0;
+  size_t pos = 30;
+  EXPECT_TRUE(input.Read(pos, 2, &temp));
+  output->Write(pos, (temp ^ 0xffff), 2);
+  return CHANGE;
+}
+
 PacketFilter::Action ClientHelloPreambleCapture::FilterHandshake(
     const HandshakeHeader& header, const DataBuffer& input,
     DataBuffer* output) {
   EXPECT_TRUE(header.handshake_type() == kTlsHandshakeClientHello);
 
   if (captured_) {
     return KEEP;
   }
@@ -1240,15 +1251,15 @@ PacketFilter::Action ClientHelloPreamble
   EXPECT_TRUE(parser.ReadVariable(&temp, 1));  // Session ID
   if (is_dtls_agent()) {
     EXPECT_TRUE(parser.ReadVariable(&temp, 1));  // Cookie
   }
   EXPECT_TRUE(parser.ReadVariable(&temp, 2));  // Ciphersuites
   EXPECT_TRUE(parser.ReadVariable(&temp, 1));  // Compression
 
   // Copy the preamble into a new buffer
-  data_ = DataBuffer(input);
+  data_ = input;
   data_.Truncate(parser.consumed());
 
   return KEEP;
 }
 
 }  // namespace nss_test
--- a/security/nss/gtests/ssl_gtest/tls_filter.h
+++ b/security/nss/gtests/ssl_gtest/tls_filter.h
@@ -852,11 +852,22 @@ class ClientHelloPreambleCapture : publi
                                        const DataBuffer& input,
                                        DataBuffer* output) override;
 
  private:
   bool captured_;
   DataBuffer data_;
 };
 
+class ServerHelloRandomChanger : public TlsHandshakeFilter {
+ public:
+  ServerHelloRandomChanger(const std::shared_ptr<TlsAgent>& a)
+      : TlsHandshakeFilter(a, {kTlsHandshakeServerHello}) {}
+
+ protected:
+  PacketFilter::Action FilterHandshake(const HandshakeHeader& header,
+                                       const DataBuffer& input,
+                                       DataBuffer* output) override;
+};
+
 }  // namespace nss_test
 
 #endif
--- a/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc
+++ b/security/nss/gtests/ssl_gtest/tls_hkdf_unittest.cc
@@ -171,16 +171,17 @@ class TlsHkdfTest : public ::testing::Te
     ASSERT_NE(nullptr, prk);
     VerifyKey(ScopedPK11SymKey(prk), CKM_HKDF_DERIVE, expected);
   }
 
   void HkdfExpandLabel(ScopedPK11SymKey* prk, SSLHashType base_hash,
                        const uint8_t* session_hash, size_t session_hash_len,
                        const char* label, size_t label_len,
                        const DataBuffer& expected) {
+    ASSERT_NE(nullptr, prk);
     std::cerr << "Hash = " << kHashName[base_hash] << std::endl;
 
     std::vector<uint8_t> output(expected.len());
 
     SECStatus rv = tls13_HkdfExpandLabelRaw(
         prk->get(), base_hash, session_hash, session_hash_len, label, label_len,
         ssl_variant_stream, &output[0], output.size());
     ASSERT_EQ(SECSuccess, rv);
@@ -189,25 +190,25 @@ class TlsHkdfTest : public ::testing::Te
 
     // Verify that the public API produces the same result.
     PRUint16 cs = GetSomeCipherSuiteForHash(base_hash);
     PK11SymKey* secret;
     rv = SSL_HkdfExpandLabel(SSL_LIBRARY_VERSION_TLS_1_3, cs, prk->get(),
                              session_hash, session_hash_len, label, label_len,
                              &secret);
     EXPECT_EQ(SECSuccess, rv);
-    ASSERT_NE(nullptr, prk);
+    ASSERT_NE(nullptr, secret);
     VerifyKey(ScopedPK11SymKey(secret), CKM_HKDF_DERIVE, expected);
 
     // Verify that a key can be created with a different key type and size.
     rv = SSL_HkdfExpandLabelWithMech(
         SSL_LIBRARY_VERSION_TLS_1_3, cs, prk->get(), session_hash,
         session_hash_len, label, label_len, CKM_DES3_CBC_PAD, 24, &secret);
     EXPECT_EQ(SECSuccess, rv);
-    ASSERT_NE(nullptr, prk);
+    ASSERT_NE(nullptr, secret);
     ScopedPK11SymKey with_mech(secret);
     EXPECT_EQ(static_cast<CK_MECHANISM_TYPE>(CKM_DES3_CBC_PAD),
               PK11_GetMechanism(with_mech.get()));
     // Just verify that the key is the right size.
     rv = PK11_ExtractKeyValue(with_mech.get());
     ASSERT_EQ(SECSuccess, rv);
     SECItem* key_data = PK11_GetKeyData(with_mech.get());
     ASSERT_NE(nullptr, key_data);
--- a/security/nss/lib/ckfw/builtins/builtins.gyp
+++ b/security/nss/lib/ckfw/builtins/builtins.gyp
@@ -25,17 +25,17 @@
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/ckfw/ckfw.gyp:nssckfw',
         '<(DEPTH)/lib/base/base.gyp:nssb'
       ],
       'actions': [
         {
           'msvs_cygwin_shell': 0,
           'action': [
-            'python',
+            '<(python)',
             'certdata.py',
             'certdata.txt',
             '<@(_outputs)',
           ],
           'inputs': [
             'certdata.py',
             'certdata.perl',
             'certdata.txt'
--- a/security/nss/lib/ckfw/builtins/testlib/builtins-testlib.gyp
+++ b/security/nss/lib/ckfw/builtins/testlib/builtins-testlib.gyp
@@ -25,17 +25,17 @@
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/ckfw/ckfw.gyp:nssckfw',
         '<(DEPTH)/lib/base/base.gyp:nssb'
       ],
       'actions': [
         {
           'msvs_cygwin_shell': 0,
           'action': [
-            'python',
+            '<(python)',
             '../certdata.py',
             'certdata-testlib.txt',
             '<@(_outputs)',
           ],
           'inputs': [
             '../certdata.py',
             '../certdata.perl',
             'certdata-testlib.txt'
--- a/security/nss/lib/freebl/freebl.gyp
+++ b/security/nss/lib/freebl/freebl.gyp
@@ -12,17 +12,17 @@
       'sources': [
         'intel-aes.s',
         'intel-gcm.s',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports'
       ],
       'conditions': [
-        [ 'cc_is_clang==1', {
+        [ 'cc_is_clang==1 and force_integrated_as!=1', {
           'cflags': [
             '-no-integrated-as',
           ],
           'cflags_mozilla': [
             '-no-integrated-as',
           ],
           'asflags_mozilla': [
             '-no-integrated-as',
@@ -321,17 +321,17 @@
       'type': 'static_library',
       'sources': [
         'ppc-gcm.s',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports'
       ],
       'conditions': [
-        [ 'cc_is_clang==1', {
+        [ 'cc_is_clang==1 and force_integrated_as!=1', {
           'cflags': [
             '-no-integrated-as',
           ],
           'cflags_mozilla': [
             '-no-integrated-as',
           ],
           'asflags_mozilla': [
             '-no-integrated-as',
--- a/security/nss/lib/freebl/freebl_base.gypi
+++ b/security/nss/lib/freebl/freebl_base.gypi
@@ -67,17 +67,17 @@
         [ 'target_arch=="x64"', {
           'sources': [
             'arcfour-amd64-gas.s',
             'mpi/mpi_amd64.c',
             'mpi/mpi_amd64_common.S',
             'mpi/mp_comba.c',
           ],
           'conditions': [
-            [ 'cc_is_clang==1 and fuzz!=1 and coverage!=1', {
+            [ 'cc_is_clang==1 and fuzz!=1 and coverage!=1 and force_integrated_as!=1', {
               'cflags': [
                 '-no-integrated-as',
               ],
               'cflags_mozilla': [
                 '-no-integrated-as',
               ],
               'asflags_mozilla': [
                 '-no-integrated-as',
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -17,22 +17,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION "3.74" _NSS_CUSTOMIZED
+#define NSS_VERSION "3.75" _NSS_CUSTOMIZED " Beta"
 #define NSS_VMAJOR 3
-#define NSS_VMINOR 74
+#define NSS_VMINOR 75
 #define NSS_VPATCH 0
 #define NSS_VBUILD 0
-#define NSS_BETA PR_FALSE
+#define NSS_BETA PR_TRUE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/pk11wrap/pk11mech.c
+++ b/security/nss/lib/pk11wrap/pk11mech.c
@@ -230,17 +230,17 @@ PK11_GetKeyType(CK_MECHANISM_TYPE type, 
             return CKK_CAMELLIA;
         case CKM_NSS_CHACHA20_POLY1305:
         case CKM_NSS_CHACHA20_KEY_GEN:
         case CKM_NSS_CHACHA20_CTR:
             return CKK_NSS_CHACHA20;
         case CKM_CHACHA20_POLY1305:
         case CKM_CHACHA20_KEY_GEN:
         case CKM_CHACHA20:
-            return CKK_NSS_CHACHA20;
+            return CKK_CHACHA20;
         case CKM_AES_ECB:
         case CKM_AES_CBC:
         case CKM_AES_CCM:
         case CKM_AES_CTR:
         case CKM_AES_CTS:
         case CKM_AES_GCM:
         case CKM_AES_MAC:
         case CKM_AES_MAC_GENERAL:
--- a/security/nss/lib/softoken/pkcs11.c
+++ b/security/nss/lib/softoken/pkcs11.c
@@ -417,16 +417,17 @@ static const struct mechanismList mechan
     { CKM_SEED_CBC_PAD, { 16, 16, CKF_EN_DE_WR_UN }, PR_TRUE },
 #endif
 /* ------------------------- ChaCha20 Operations ---------------------- */
 #ifndef NSS_DISABLE_CHACHAPOLY
     { CKM_NSS_CHACHA20_KEY_GEN, { 32, 32, CKF_GENERATE }, PR_TRUE },
     { CKM_NSS_CHACHA20_POLY1305, { 32, 32, CKF_EN_DE }, PR_TRUE },
     { CKM_NSS_CHACHA20_CTR, { 32, 32, CKF_EN_DE }, PR_TRUE },
     { CKM_CHACHA20_KEY_GEN, { 32, 32, CKF_GENERATE }, PR_TRUE },
+    { CKM_CHACHA20, { 32, 32, CKF_EN_DE }, PR_TRUE },
     { CKM_CHACHA20_POLY1305, { 32, 32, CKF_EN_DE_MSG }, PR_TRUE },
 #endif /* NSS_DISABLE_CHACHAPOLY */
     /* ------------------------- Hashing Operations ----------------------- */
     { CKM_MD2, { 0, 0, CKF_DIGEST }, PR_FALSE },
     { CKM_MD2_HMAC, { 1, 128, CKF_SN_VR }, PR_TRUE },
     { CKM_MD2_HMAC_GENERAL, { 1, 128, CKF_SN_VR }, PR_TRUE },
     { CKM_MD5, { 0, 0, CKF_DIGEST }, PR_FALSE },
     { CKM_MD5_HMAC, { 1, 128, CKF_SN_VR }, PR_TRUE },
--- a/security/nss/lib/softoken/pkcs11c.c
+++ b/security/nss/lib/softoken/pkcs11c.c
@@ -1258,24 +1258,32 @@ sftk_CryptInit(CK_SESSION_HANDLE hSessio
             }
             context->update = (SFTKCipher)(isEncrypt ? AES_Encrypt : AES_Decrypt);
             context->destroy = (SFTKDestroy)AES_DestroyContext;
             break;
 
         case CKM_NSS_CHACHA20_POLY1305:
         case CKM_CHACHA20_POLY1305:
             if (pMechanism->mechanism == CKM_NSS_CHACHA20_POLY1305) {
+                if (key_type != CKK_NSS_CHACHA20) {
+                    crv = CKR_KEY_TYPE_INCONSISTENT;
+                    break;
+                }
                 if ((pMechanism->pParameter == NULL) ||
                     (pMechanism->ulParameterLen != sizeof(CK_NSS_AEAD_PARAMS))) {
                     crv = CKR_MECHANISM_PARAM_INVALID;
                     break;
                 }
                 nss_aead_params_ptr = (CK_NSS_AEAD_PARAMS *)pMechanism->pParameter;
             } else {
                 CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR chacha_poly_params;
+                if (key_type != CKK_CHACHA20) {
+                    crv = CKR_KEY_TYPE_INCONSISTENT;
+                    break;
+                }
                 if ((pMechanism->pParameter == NULL) ||
                     (pMechanism->ulParameterLen !=
                      sizeof(CK_SALSA20_CHACHA20_POLY1305_PARAMS))) {
                     crv = CKR_MECHANISM_PARAM_INVALID;
                     break;
                 }
                 chacha_poly_params = (CK_SALSA20_CHACHA20_POLY1305_PARAMS_PTR)
                                          pMechanism->pParameter;
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -12,16 +12,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION "3.74" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VERSION "3.75" SOFTOKEN_ECC_STRING " Beta"
 #define SOFTOKEN_VMAJOR 3
-#define SOFTOKEN_VMINOR 74
+#define SOFTOKEN_VMINOR 75
 #define SOFTOKEN_VPATCH 0
 #define SOFTOKEN_VBUILD 0
-#define SOFTOKEN_BETA PR_FALSE
+#define SOFTOKEN_BETA PR_TRUE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/ssl/ssl3con.c
+++ b/security/nss/lib/ssl/ssl3con.c
@@ -5520,17 +5520,17 @@ ssl3_SendClientHello(sslSocket *ss, sslC
     rv = ssl3_CreateClientHelloPreamble(ss, sid, requestingResume, version,
                                         PR_FALSE, &extensionBuf, &chBuf);
     if (rv != SECSuccess) {
         goto loser; /* err set by ssl3_CreateClientHelloPreamble. */
     }
 
     if (!ss->ssl3.hs.echHpkeCtx) {
         if (extensionBuf.len) {
-            rv = tls13_MaybeGreaseEch(ss, chBuf.len, &extensionBuf);
+            rv = tls13_MaybeGreaseEch(ss, &chBuf, &extensionBuf);
             if (rv != SECSuccess) {
                 goto loser; /* err set by tls13_MaybeGreaseEch. */
             }
             rv = ssl_InsertPaddingExtension(ss, chBuf.len, &extensionBuf);
             if (rv != SECSuccess) {
                 goto loser; /* err set by ssl_InsertPaddingExtension. */
             }
 
@@ -9804,31 +9804,36 @@ ssl_ConstructServerHello(sslSocket *ss, 
     if (rv != SECSuccess) {
         return SECFailure;
     }
     rv = sslBuffer_AppendNumber(messageBuf, ssl_compression_null, 1);
     if (rv != SECSuccess) {
         return SECFailure;
     }
     if (SSL_BUFFER_LEN(extensionBuf)) {
+        /* Directly copy the extensions */
         rv = sslBuffer_AppendBufferVariable(messageBuf, extensionBuf, 2);
         if (rv != SECSuccess) {
             return SECFailure;
         }
     }
 
-    if (!helloRetry && ssl3_ExtensionNegotiated(ss, ssl_tls13_ech_is_inner_xtn)) {
+    if (ss->xtnData.ech && ss->xtnData.ech->receivedInnerXtn) {
         /* Signal ECH acceptance if we handled handled both CHOuter/CHInner (i.e.
          * in shared mode), or if we received a CHInner in split/backend mode. */
         if (ss->ssl3.hs.echAccepted || ss->opt.enableTls13BackendEch) {
-            return tls13_WriteServerEchSignal(ss, SSL_BUFFER_BASE(messageBuf),
-                                              SSL_BUFFER_LEN(messageBuf));
-        }
-    }
-
+            if (helloRetry) {
+                return tls13_WriteServerEchHrrSignal(ss, SSL_BUFFER_BASE(messageBuf),
+                                                     SSL_BUFFER_LEN(messageBuf));
+            } else {
+                return tls13_WriteServerEchSignal(ss, SSL_BUFFER_BASE(messageBuf),
+                                                  SSL_BUFFER_LEN(messageBuf));
+            }
+        }
+    }
     return SECSuccess;
 }
 
 /* The negotiated version number has been already placed in ss->version.
 **
 ** Called from:  ssl3_HandleClientHello                     (resuming session),
 **  ssl3_SendServerHelloSequence <- ssl3_HandleClientHello   (new session),
 **  ssl3_SendServerHelloSequence <- ssl3_HandleV2ClientHello (new session)
@@ -13494,16 +13499,17 @@ ssl3_InitState(sslSocket *ss)
     ss->ssl3.hs.dheSecret = NULL;
     ss->ssl3.hs.clientEarlyTrafficSecret = NULL;
     ss->ssl3.hs.clientHsTrafficSecret = NULL;
     ss->ssl3.hs.serverHsTrafficSecret = NULL;
     ss->ssl3.hs.clientTrafficSecret = NULL;
     ss->ssl3.hs.serverTrafficSecret = NULL;
     ss->ssl3.hs.echHpkeCtx = NULL;
     ss->ssl3.hs.echAccepted = PR_FALSE;
+    ss->ssl3.hs.echDecided = PR_FALSE;
 
     PORT_Assert(!ss->ssl3.hs.messages.buf && !ss->ssl3.hs.messages.space);
     ss->ssl3.hs.messages.buf = NULL;
     ss->ssl3.hs.messages.space = 0;
 
     ss->ssl3.hs.receivedNewSessionTicket = PR_FALSE;
     PORT_Memset(&ss->ssl3.hs.newSessionTicket, 0,
                 sizeof(ss->ssl3.hs.newSessionTicket));
--- a/security/nss/lib/ssl/ssl3ext.c
+++ b/security/nss/lib/ssl/ssl3ext.c
@@ -50,17 +50,16 @@ static const ssl3ExtensionHandler client
     { ssl_signed_cert_timestamp_xtn, &ssl3_ServerHandleSignedCertTimestampXtn },
     { ssl_delegated_credentials_xtn, &tls13_ServerHandleDelegatedCredentialsXtn },
     { ssl_tls13_key_share_xtn, &tls13_ServerHandleKeyShareXtn },
     { ssl_tls13_pre_shared_key_xtn, &tls13_ServerHandlePreSharedKeyXtn },
     { ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn },
     { ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn },
     { ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn },
     { ssl_tls13_post_handshake_auth_xtn, &tls13_ServerHandlePostHandshakeAuthXtn },
-    { ssl_tls13_ech_is_inner_xtn, &tls13_ServerHandleEchIsInnerXtn },
     { ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn },
     { 0, NULL }
 };
 
 /* These two tables are used by the client, to handle server hello
  * extensions. */
 static const ssl3ExtensionHandler serverHelloHandlersTLS[] = {
     { ssl_server_name_xtn, &ssl3_HandleServerNameXtn },
@@ -78,16 +77,17 @@ static const ssl3ExtensionHandler server
     { ssl_tls13_encrypted_client_hello_xtn, &tls13_ClientHandleEchXtn },
     { ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn },
     { 0, NULL }
 };
 
 static const ssl3ExtensionHandler helloRetryRequestHandlers[] = {
     { ssl_tls13_key_share_xtn, tls13_ClientHandleKeyShareXtnHrr },
     { ssl_tls13_cookie_xtn, tls13_ClientHandleHrrCookie },
+    { ssl_tls13_encrypted_client_hello_xtn, tls13_ClientHandleHrrEchXtn },
     { 0, NULL }
 };
 
 static const ssl3ExtensionHandler serverHelloHandlersSSL3[] = {
     { ssl_renegotiation_info_xtn, &ssl3_HandleRenegotiationInfoXtn },
     { 0, NULL }
 };
 
@@ -162,16 +162,17 @@ static const sslExtensionBuilder tls13_c
     { ssl_tls13_certificate_authorities_xtn, &tls13_SendCertAuthoritiesXtn },
     { 0, NULL }
 };
 
 static const sslExtensionBuilder tls13_hrr_senders[] = {
     { ssl_tls13_key_share_xtn, &tls13_ServerSendHrrKeyShareXtn },
     { ssl_tls13_cookie_xtn, &tls13_ServerSendHrrCookieXtn },
     { ssl_tls13_supported_versions_xtn, &tls13_ServerSendSupportedVersionsXtn },
+    { ssl_tls13_encrypted_client_hello_xtn, &tls13_ServerSendHrrEchXtn },
     { 0, NULL }
 };
 
 static const struct {
     SSLExtensionType type;
     SSLExtensionSupport support;
 } ssl_supported_extensions[] = {
     { ssl_server_name_xtn, ssl_ext_native_only },
@@ -272,17 +273,17 @@ SSLExp_InstallExtensionHooks(PRFileDesc 
     hook->writer = writer;
     hook->writerArg = writerArg;
     hook->handler = handler;
     hook->handlerArg = handlerArg;
     PR_APPEND_LINK(&hook->link, &ss->extensionHooks);
     return SECSuccess;