Merge try head draft
authorConnor Sheehan <sheehan@mozilla.com>
Sun, 19 Sep 2021 02:01:29 -0400
changeset 3972591 fefecd98025e592a89b67b920fa8fb4a74d7be87
parent 3972590 ce18b6567b0f68008891cf9b047d646a01b5c828 (diff)
parent 3905339 f12f0316cbeaaa53f7e7b725938148dd14487e1a (current diff)
child 3972592 f93de22b2e7a24b13cf650ac5910e7d785237e69
push id731655
push usercosheehan@mozilla.com
push dateMon, 20 Sep 2021 14:21:28 +0000
treeherdertry@b28663426ec8 [default view] [failures only]
milestone94.0a1
Merge try head
--- a/.cargo/config.in
+++ b/.cargo/config.in
@@ -1,31 +1,26 @@
 # This file contains vendoring instructions for cargo.
 # It was generated by `mach vendor rust`.
 # Please do not edit.
 
-[source."https://github.com/shravanrn/nix/"]
-git = "https://github.com/shravanrn/nix/"
-replace-with = "vendored-sources"
-rev = "4af6c367603869a30fddb5ffb0aba2b9477ba92e"
-
 [source."https://github.com/msirringhaus/minidump_writer_linux.git"]
 git = "https://github.com/msirringhaus/minidump_writer_linux.git"
 replace-with = "vendored-sources"
-rev = "85551909b95a5cf553a85dbcddfa5f117cfbbe0e"
+rev = "029ac0d54b237f27dc7d8d4e51bc0fb076e5e852"
 
 [source."https://github.com/mozilla/neqo"]
 git = "https://github.com/mozilla/neqo"
 replace-with = "vendored-sources"
-tag = "v0.4.31"
+tag = "v0.5.0"
 
 [source."https://github.com/mozilla/mp4parse-rust"]
 git = "https://github.com/mozilla/mp4parse-rust"
 replace-with = "vendored-sources"
-rev = "c6ba5afd856c158d9cfc1a447165fcfaaf2b797c"
+rev = "2dcd3d84424a02c167ac40e535a9e3231252d6c1"
 
 [source."https://github.com/mozilla/l10nregistry-rs"]
 git = "https://github.com/mozilla/l10nregistry-rs"
 replace-with = "vendored-sources"
 rev = "a69df9836b1ef536727195209013b9ad6b132618"
 
 [source."https://github.com/mozilla/cubeb-pulse-rs"]
 git = "https://github.com/mozilla/cubeb-pulse-rs"
@@ -35,31 +30,30 @@ rev = "3ad5978575f501ab10b1753626f176f1b
 [source."https://github.com/mozilla/cubeb-coreaudio-rs"]
 git = "https://github.com/mozilla/cubeb-coreaudio-rs"
 replace-with = "vendored-sources"
 rev = "4ee3b283182926f9a8bb31d603219ff2db70d2fd"
 
 [source."https://github.com/mozilla/audioipc-2"]
 git = "https://github.com/mozilla/audioipc-2"
 replace-with = "vendored-sources"
-rev = "8bb1a227fbaa5677458bcd876162b65307df38c2"
+rev = "fce878ff15f4e1dba1e5b54f82460af605fb4d14"
 
 [source."https://github.com/mozilla/application-services"]
 git = "https://github.com/mozilla/application-services"
 replace-with = "vendored-sources"
 rev = "8a576fbe79199fa8664f64285524017f74ebcc5f"
 
 [source."https://github.com/mozilla-spidermonkey/jsparagus"]
 git = "https://github.com/mozilla-spidermonkey/jsparagus"
 replace-with = "vendored-sources"
 rev = "2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 
-[source."https://github.com/kvark/spirv_cross"]
-branch = "wgpu5"
-git = "https://github.com/kvark/spirv_cross"
+[source."https://github.com/kvark/dummy-web"]
+git = "https://github.com/kvark/dummy-web"
 replace-with = "vendored-sources"
 
 [source."https://github.com/kinetiknz/mio-named-pipes"]
 git = "https://github.com/kinetiknz/mio-named-pipes"
 replace-with = "vendored-sources"
 rev = "21c26326f5f45f415c49eac4ba5bc41a2f961321"
 
 [source."https://github.com/kinetiknz/ashmem-rs"]
@@ -82,16 +76,26 @@ 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 = "302c995f91f44cf26e77dc4758ad56c3ff0153ad"
 
+[source."https://github.com/gfx-rs/wgpu"]
+git = "https://github.com/gfx-rs/wgpu"
+replace-with = "vendored-sources"
+rev = "37288a6"
+
+[source."https://github.com/gfx-rs/naga"]
+git = "https://github.com/gfx-rs/naga"
+replace-with = "vendored-sources"
+rev = "e226cf3"
+
 [source."https://github.com/bytecodealliance/wasmtime"]
 git = "https://github.com/bytecodealliance/wasmtime"
 replace-with = "vendored-sources"
 rev = "824fa69756523f2b6d49029fe25de94130b1f144"
 
 [source.crates-io]
 replace-with = "vendored-sources"
 
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -114,17 +114,16 @@ gfx/vr/service/openvr/src/openvr_api_pub
 gfx/vr/service/openvr/src/pathtools_public.cpp
 gfx/vr/service/openvr/src/pathtools_public.h
 gfx/vr/service/openvr/src/sharedlibtools_public.cpp
 gfx/vr/service/openvr/src/sharedlibtools_public.h
 gfx/vr/service/openvr/src/strtools_public.cpp
 gfx/vr/service/openvr/src/strtools_public.h
 gfx/vr/service/openvr/src/vrpathregistry_public.cpp
 gfx/vr/service/openvr/src/vrpathregistry_public.h
-gfx/wgpu/.*
 gfx/ycbcr/.*
 intl/hyphenation/hyphen/.*
 intl/icu/.*
 ipc/chromium/src/third_party/.*
 js/src/ctypes/libffi/.*
 js/src/dtoa.c.*
 js/src/editline/.*
 js/src/jit/arm64/vixl/.*
--- a/.cron.yml
+++ b/.cron.yml
@@ -117,16 +117,17 @@ jobs:
           treeherder-symbol: Searchfox
           target-tasks-method: searchfox_index
       run-on-projects:
           - mozilla-central
           - mozilla-beta
           - mozilla-release
           - mozilla-esr78
           - mozilla-esr91
+          - elm
       when:
           by-project:
               # We want to run at both of the times the nightly runs.
               mozilla-central: [{hour: 10, minute: 0}, {hour: 22, minute: 0}]
               # For all other jobs we just run once daily matching the 10 UTC
               # nightly which is designed to align with searchfox's AWS cron
               # jobs (for legacy reasons) rather than trying to align with
               # specific builds.  (Ex: mozilla-beta has a "daily-releases" job
@@ -137,16 +138,17 @@ jobs:
               # the previous day's job.  This is only beneficial in the sense
               # that there's no risk of expiration for artifacts and is a
               # trade-off to avoid stale indices.  Bug 1686981 tracks fixing
               # this.
               mozilla-beta: [{hour: 10, minute: 0}]
               mozilla-release: [{hour: 10, minute: 0}]
               mozilla-esr78: [{hour: 10, minute: 0}]
               mozilla-esr91: [{hour: 10, minute: 0}]
+              elm: [{hour: 10, minute: 0}]
 
     - name: coverity-tree-analysis
       job:
           type: decision-task
           treeherder-symbol: CoverityTA
           target-tasks-method: coverity_static_analysis_full
       run-on-projects:
           - mozilla-central
--- a/.hgtags
+++ b/.hgtags
@@ -210,8 +210,10 @@ 729eaf579f2827239182db122d59e42730ea540f
 8e850fd29a957f505e0355c1326279e06e9040bb FIREFOX_BETA_89_BASE
 a69c07c7da3a386e3baf4b7d604312d3fa974273 FIREFOX_NIGHTLY_89_END
 fafcc4a3b16a3b45f7abcc174e46bdc4a46888ca FIREFOX_BETA_90_BASE
 b724ec4440e0fec4dd6e02ac019f43b2e0b32f85 FIREFOX_NIGHTLY_90_END
 5dee15cf3f2810ccc3bb9ec24c652f6f9d72b62d FIREFOX_BETA_91_BASE
 41ae2b104b93c1779db0f34ba1c045e3e696898b FIREFOX_NIGHTLY_91_END
 03637911a02a3d366a3f66869bbb76c7626a55ee FIREFOX_BETA_92_BASE
 82f33bb4e9dbcb837a484109a607d28eac7837dd FIREFOX_NIGHTLY_92_END
+1b49e7328ae43c6565d167f4c391430575097fd3 FIREFOX_BETA_93_BASE
+63d82db58e2ced524acb4dab5687b273ca57f153 FIREFOX_NIGHTLY_93_END
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -82,25 +82,31 @@ version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
 
 [[package]]
 name = "arrayvec"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "ash"
-version = "0.32.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06063a002a77d2734631db74e8f4ce7148b77fe522e6bca46f2ae7774fd48112"
+version = "0.33.0+1.2.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2142f1fa77cc4d24ffd2f24dc84f88ce5b1e588d524f10fb473a04b93aef14f"
 dependencies = [
  "libloading 0.7.0",
 ]
 
 [[package]]
 name = "ashmem"
 version = "0.1.0"
 source = "git+https://github.com/kinetiknz/ashmem-rs?rev=e47f470a54193532d60057ec54f864e06aeaff36#e47f470a54193532d60057ec54f864e06aeaff36"
@@ -164,17 +170,17 @@ dependencies = [
  "log",
  "mach",
  "winapi",
 ]
 
 [[package]]
 name = "audioipc"
 version = "0.2.5"
-source = "git+https://github.com/mozilla/audioipc-2?rev=8bb1a227fbaa5677458bcd876162b65307df38c2#8bb1a227fbaa5677458bcd876162b65307df38c2"
+source = "git+https://github.com/mozilla/audioipc-2?rev=fce878ff15f4e1dba1e5b54f82460af605fb4d14#fce878ff15f4e1dba1e5b54f82460af605fb4d14"
 dependencies = [
  "ashmem",
  "audio_thread_priority",
  "bincode",
  "bytes 0.4.12",
  "cc",
  "cubeb",
  "error-chain",
@@ -193,31 +199,31 @@ dependencies = [
  "tokio-io",
  "tokio-reactor",
  "winapi",
 ]
 
 [[package]]
 name = "audioipc-client"
 version = "0.4.0"
-source = "git+https://github.com/mozilla/audioipc-2?rev=8bb1a227fbaa5677458bcd876162b65307df38c2#8bb1a227fbaa5677458bcd876162b65307df38c2"
+source = "git+https://github.com/mozilla/audioipc-2?rev=fce878ff15f4e1dba1e5b54f82460af605fb4d14#fce878ff15f4e1dba1e5b54f82460af605fb4d14"
 dependencies = [
  "audio_thread_priority",
  "audioipc",
  "cubeb-backend",
  "futures 0.1.31",
  "futures-cpupool",
  "log",
  "tokio 0.1.11",
 ]
 
 [[package]]
 name = "audioipc-server"
 version = "0.2.3"
-source = "git+https://github.com/mozilla/audioipc-2?rev=8bb1a227fbaa5677458bcd876162b65307df38c2#8bb1a227fbaa5677458bcd876162b65307df38c2"
+source = "git+https://github.com/mozilla/audioipc-2?rev=fce878ff15f4e1dba1e5b54f82460af605fb4d14#fce878ff15f4e1dba1e5b54f82460af605fb4d14"
 dependencies = [
  "audio_thread_priority",
  "audioipc",
  "cubeb-core",
  "error-chain",
  "futures 0.1.31",
  "log",
  "once_cell",
@@ -269,16 +275,22 @@ dependencies = [
 
 [[package]]
 name = "base64"
 version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
 
 [[package]]
+name = "base64"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+
+[[package]]
 name = "bench-collections-gtest"
 version = "0.1.0"
 dependencies = [
  "fnv",
  "fxhash",
 ]
 
 [[package]]
@@ -332,16 +344,26 @@ checksum = "349f9b6a179ed607305526ca489b
 
 [[package]]
 name = "bitflags"
 version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 
 [[package]]
+name = "bitflags_serde_shim"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25c3d626f0280ec39b33a6fc5c6c1067432b4c41e94aee40ded197a6649bf025"
+dependencies = [
+ "bitflags",
+ "serde",
+]
+
+[[package]]
 name = "bitreader"
 version = "0.3.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9178181a7d44239c6c8eaafa8688558a2ab5fa04b8855381f2681e9591fb941b"
 dependencies = [
  "cfg-if 1.0.0",
 ]
 
@@ -388,17 +410,17 @@ dependencies = [
 
 [[package]]
 name = "blake2b_simd"
 version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b83b7baab1e671718d78204225800d6b170e648188ac7dc992e9d6bddf87d0c0"
 dependencies = [
  "arrayref",
- "arrayvec",
+ "arrayvec 0.5.2",
  "constant_time_eq",
 ]
 
 [[package]]
 name = "block"
 version = "0.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
@@ -505,19 +527,16 @@ dependencies = [
  "xpcom",
 ]
 
 [[package]]
 name = "cc"
 version = "1.0.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
-dependencies = [
- "jobserver",
-]
 
 [[package]]
 name = "cert_storage"
 version = "0.0.1"
 dependencies = [
  "base64 0.10.1",
  "byteorder",
  "crossbeam-utils 0.8.5",
@@ -634,31 +653,16 @@ name = "cmake"
 version = "0.1.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
 dependencies = [
  "cc",
 ]
 
 [[package]]
-name = "cocoa-foundation"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
-dependencies = [
- "bitflags",
- "block",
- "core-foundation",
- "core-graphics-types",
- "foreign-types",
- "libc",
- "objc",
-]
-
-[[package]]
 name = "codespan-reporting"
 version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
 dependencies = [
  "termcolor",
  "unicode-width",
 ]
@@ -856,57 +860,71 @@ version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
 dependencies = [
  "cfg-if 1.0.0",
 ]
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
-dependencies = [
- "crossbeam-utils 0.7.2",
- "maybe-uninit",
-]
-
-[[package]]
-name = "crossbeam-channel"
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
 dependencies = [
  "cfg-if 1.0.0",
  "crossbeam-utils 0.8.5",
 ]
 
 [[package]]
 name = "crossbeam-deque"
 version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed"
 dependencies = [
- "crossbeam-epoch",
+ "crossbeam-epoch 0.8.2",
  "crossbeam-utils 0.7.2",
  "maybe-uninit",
 ]
 
 [[package]]
+name = "crossbeam-deque"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+dependencies = [
+ "cfg-if 1.0.0",
+ "crossbeam-epoch 0.9.5",
+ "crossbeam-utils 0.8.5",
+]
+
+[[package]]
 name = "crossbeam-epoch"
 version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
 dependencies = [
  "autocfg",
  "cfg-if 0.1.10",
  "crossbeam-utils 0.7.2",
  "lazy_static",
  "maybe-uninit",
- "memoffset",
+ "memoffset 0.5.6",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
+dependencies = [
+ "cfg-if 1.0.0",
+ "crossbeam-utils 0.8.5",
+ "lazy_static",
+ "memoffset 0.6.4",
  "scopeguard",
 ]
 
 [[package]]
 name = "crossbeam-queue"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b"
@@ -942,19 +960,19 @@ source = "registry+https://github.com/ru
 checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
 dependencies = [
  "cfg-if 1.0.0",
  "lazy_static",
 ]
 
 [[package]]
 name = "cssparser"
-version = "0.28.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1db8599a9761b371751fbf13e076fa03c6e1a78f8c5288e6ab9467f10a2322c1"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a3a85949f6cbf49a4e1ab98a98e0f9aabfe7d827a7f156da36c98a86dede2df"
 dependencies = [
  "cssparser-macros",
  "dtoa-short",
  "itoa",
  "matches",
  "phf",
  "proc-macro2",
  "quote",
@@ -1047,19 +1065,19 @@ source = "registry+https://github.com/ru
 checksum = "eaedd862c6194a52c45efc3b5fd43e42e11fe3cd915520add4d1a5d03a818796"
 dependencies = [
  "cmake",
  "pkg-config",
 ]
 
 [[package]]
 name = "d3d12"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "091ed1b25fe47c7ff129fc440c23650b6114f36aa00bc7212cc8041879294428"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2daefd788d1e96e0a9d66dee4b828b883509bc3ea9ce30665f04c3246372690c"
 dependencies = [
  "bitflags",
  "libloading 0.7.0",
  "winapi",
 ]
 
 [[package]]
 name = "darling"
@@ -1215,25 +1233,16 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "268360cf7696c0c2c83061edb6af090cfea85cbde7d1b8425d6e4ffe9f1c0ec9"
 dependencies = [
  "log",
  "smallbitvec",
 ]
 
 [[package]]
-name = "drm-fourcc"
-version = "2.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebbf3a5ed4671aabffefce172ff43d69c1f27dd2c6aea28e5212a70f32ada0cf"
-dependencies = [
- "serde",
-]
-
-[[package]]
 name = "dtoa"
 version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
 
 [[package]]
 name = "dtoa-short"
 version = "0.3.3"
@@ -1345,26 +1354,16 @@ version = "0.22.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "da96828553a086d7b18dcebfc579bd9628b016f86590d7453c115e490fa74b80"
 dependencies = [
  "num-traits",
  "serde",
 ]
 
 [[package]]
-name = "external-memory"
-version = "0.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4dfe8d292b014422776a8c516862d2bff8a81b223a4461dfdc45f3862dc9d39"
-dependencies = [
- "bitflags",
- "drm-fourcc",
-]
-
-[[package]]
 name = "fake-simd"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
 
 [[package]]
 name = "fallible"
 version = "0.0.1"
@@ -1411,19 +1410,19 @@ source = "registry+https://github.com/ru
 checksum = "b12c2c8d7d9f04d7952cc33bac89b7425fb3cf4c44773b06ea49ac3df259ac57"
 dependencies = [
  "comedy",
  "winapi",
 ]
 
 [[package]]
 name = "fixedbitset"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e"
 
 [[package]]
 name = "flate2"
 version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
 dependencies = [
  "cfg-if 1.0.0",
@@ -1840,158 +1839,16 @@ source = "registry+https://github.com/ru
 checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
 dependencies = [
  "cfg-if 0.1.10",
  "libc",
  "wasi",
 ]
 
 [[package]]
-name = "gfx-auxil"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1694991b11d642680e82075a75c7c2bd75556b805efa7660b705689f05b1ab1c"
-dependencies = [
- "fxhash",
- "gfx-hal",
- "spirv_cross",
-]
-
-[[package]]
-name = "gfx-backend-dx11"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f9e453baf3aaef2b0c354ce0b3d63d76402e406a59b64b7182d123cfa6635ae"
-dependencies = [
- "arrayvec",
- "bitflags",
- "gfx-auxil",
- "gfx-hal",
- "gfx-renderdoc",
- "libloading 0.7.0",
- "log",
- "parking_lot",
- "range-alloc",
- "raw-window-handle",
- "smallvec",
- "spirv_cross",
- "thunderdome",
- "winapi",
- "wio",
-]
-
-[[package]]
-name = "gfx-backend-dx12"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21506399f64a3c4d389182a89a30073856ae33eb712315456b4fd8f39ee7682a"
-dependencies = [
- "arrayvec",
- "bit-set",
- "bitflags",
- "d3d12",
- "gfx-auxil",
- "gfx-hal",
- "gfx-renderdoc",
- "log",
- "parking_lot",
- "range-alloc",
- "raw-window-handle",
- "smallvec",
- "spirv_cross",
- "thunderdome",
- "winapi",
-]
-
-[[package]]
-name = "gfx-backend-empty"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c8f813c47791918aa00dc9c9ddf961d23fa8c2a5d869e6cb8ea84f944820f4"
-dependencies = [
- "gfx-hal",
- "log",
- "raw-window-handle",
-]
-
-[[package]]
-name = "gfx-backend-metal"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0de85808e2a98994c6af925253f8a9593bc57180ef1ea137deab6d35cc949517"
-dependencies = [
- "arrayvec",
- "bitflags",
- "block",
- "cocoa-foundation",
- "copyless",
- "core-graphics-types",
- "foreign-types",
- "fxhash",
- "gfx-auxil",
- "gfx-hal",
- "log",
- "metal",
- "naga",
- "objc",
- "parking_lot",
- "profiling",
- "range-alloc",
- "raw-window-handle",
- "spirv_cross",
- "storage-map",
-]
-
-[[package]]
-name = "gfx-backend-vulkan"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9861ec855acbbc65c0e4f966d761224886e811dc2c6d413a4776e9293d0e5c0"
-dependencies = [
- "arrayvec",
- "ash",
- "byteorder",
- "core-graphics-types",
- "gfx-hal",
- "gfx-renderdoc",
- "inplace_it",
- "log",
- "naga",
- "objc",
- "parking_lot",
- "raw-window-handle",
- "smallvec",
- "winapi",
-]
-
-[[package]]
-name = "gfx-hal"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fbb575ea793dd0507b3082f4f2cde62dc9f3cebd98f5cd49ba2a4da97a976fd"
-dependencies = [
- "bitflags",
- "external-memory",
- "naga",
- "raw-window-handle",
- "thiserror",
-]
-
-[[package]]
-name = "gfx-renderdoc"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8027995e247e2426d3a00d13f5191dd56c314bff02dc4b54cbf727f1ba9c40a"
-dependencies = [
- "libloading 0.7.0",
- "log",
- "renderdoc-sys",
-]
-
-[[package]]
 name = "gkrust"
 version = "0.1.0"
 dependencies = [
  "gkrust-shared",
  "mozglue-static",
  "mozilla-central-workspace-hack",
  "stylo_tests",
  "swgl",
@@ -2104,39 +1961,39 @@ version = "0.13.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "992c40589d382354958af7c60910026ed4cc3c58751355db21a4c2fbef15727a"
 dependencies = [
  "gl_generator",
 ]
 
 [[package]]
 name = "glean"
-version = "40.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4467acdfed9d396d5c8c1f47c658c6781476cfbb2e73a3c985e40204a9f4d350"
+version = "40.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e5a8f84d2fde6fd00464853df398213c29eab988406d6dd24ba610a24de96f5"
 dependencies = [
  "chrono",
- "crossbeam-channel 0.5.1",
+ "crossbeam-channel",
  "glean-core",
  "inherent",
  "log",
  "once_cell",
  "serde",
  "serde_json",
  "thiserror",
  "time",
  "uuid",
  "whatsys",
 ]
 
 [[package]]
 name = "glean-core"
-version = "40.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a78e1fedc285fbf042f8f4f48281799c87f7715ad1ad9dabbfb57b09094cbaf5"
+version = "40.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b80bf28ad601449d6d159ddceb4ee5bb30fc229e6027f2c1488c248232e8c143"
 dependencies = [
  "bincode",
  "chrono",
  "ffi-support",
  "flate2",
  "log",
  "once_cell",
  "rkv",
@@ -2144,19 +2001,19 @@ dependencies = [
  "serde_json",
  "time",
  "uuid",
  "zeitstempel",
 ]
 
 [[package]]
 name = "glean-ffi"
-version = "40.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0da7ce4e6115c834a8da23c9978e007c047284eb136689d76538ecb0ef6ff69c"
+version = "40.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58e68d41f1a8a06fe66cc8537f6286bf7a9e2f7868e1f776aaa4295f555d4390"
 dependencies = [
  "android_logger",
  "env_logger",
  "ffi-support",
  "glean-core",
  "libc",
  "log",
  "once_cell",
@@ -2168,16 +2025,28 @@ 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.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f04649123493bc2483cbef4daddb45d40bbdae5adb221a63a23efdb0cc99520"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
 name = "glsl"
 version = "4.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "766443890761b3c4edcce86cafaac97971b200662fbdd0446eb7c6b99b4401ea"
 dependencies = [
  "nom",
 ]
 
@@ -2223,38 +2092,38 @@ dependencies = [
  "storage_variant",
  "sync15-traits",
  "thin-vec",
  "xpcom",
 ]
 
 [[package]]
 name = "gpu-alloc"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbc1b6ca374e81862526786d9cb42357ce03706ed1b8761730caafd02ab91f3a"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c481459c44304a1dfed23bd650bb3912e12c9f77d7871f86d7ed7c9730a52e79"
 dependencies = [
  "bitflags",
  "gpu-alloc-types",
 ]
 
 [[package]]
 name = "gpu-alloc-types"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "gpu-descriptor"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8a70f1e87a3840ed6a3e99e02c2b861e4dbdf26f0d07e38f42ea5aff46cfce2"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7a237f0419ab10d17006d55c62ac4f689a6bf52c75d3f38b8361d249e8d4b0b"
 dependencies = [
  "bitflags",
  "gpu-descriptor-types",
  "hashbrown",
 ]
 
 [[package]]
 name = "gpu-descriptor-types"
@@ -2349,16 +2218,22 @@ name = "hermit-abi"
 version = "0.1.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
 dependencies = [
  "libc",
 ]
 
 [[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
 name = "http"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26"
 dependencies = [
  "bytes 0.5.6",
  "fnv",
  "itoa",
@@ -2391,17 +2266,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "http_sfv"
 version = "0.1.0"
 dependencies = [
  "nserror",
  "nsstring",
- "sfv",
+ "sfv 0.8.0",
  "thin-vec",
  "xpcom",
 ]
 
 [[package]]
 name = "httparse"
 version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2565,16 +2440,21 @@ name = "jobserver"
 version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "972f5ae5d1cb9c6ae417789196c803205313edde988685da5e3aae0827b9e7fd"
 dependencies = [
  "libc",
 ]
 
 [[package]]
+name = "js-sys"
+version = "0.3.100"
+source = "git+https://github.com/kvark/dummy-web#5731e569d865a1ebaf116f48dad781f355a99243"
+
+[[package]]
 name = "jsparagus"
 version = "0.1.0"
 source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 dependencies = [
  "jsparagus-ast",
  "jsparagus-emitter",
  "jsparagus-generated-parser",
  "jsparagus-json-log",
@@ -2620,17 +2500,17 @@ name = "jsparagus-json-log"
 version = "0.1.0"
 source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 
 [[package]]
 name = "jsparagus-parser"
 version = "0.1.0"
 source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.5.2",
  "bumpalo",
  "jsparagus-ast",
  "jsparagus-generated-parser",
  "jsparagus-json-log",
 ]
 
 [[package]]
 name = "jsparagus-scope"
@@ -2668,16 +2548,26 @@ dependencies = [
  "encoding_c",
  "encoding_c_mem",
  "mozglue-static",
  "mozilla-central-workspace-hack",
  "smoosh",
 ]
 
 [[package]]
+name = "khronos-egl"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3"
+dependencies = [
+ "libc",
+ "libloading 0.7.0",
+]
+
+[[package]]
 name = "khronos_api"
 version = "3.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
 
 [[package]]
 name = "kvstore"
 version = "0.1.0"
@@ -2992,26 +2882,16 @@ dependencies = [
 
 [[package]]
 name = "memchr"
 version = "2.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
 
 [[package]]
-name = "memmap"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
 name = "memmap2"
 version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "723e3ebdcdc5c023db1df315364573789f8857c11b631a2fdfad7c00f5c046b4"
 dependencies = [
  "libc",
 ]
 
@@ -3029,20 +2909,29 @@ name = "memoffset"
 version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
+name = "memoffset"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
 name = "metal"
-version = "0.23.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79d7d769f1c104b8388294d6594d491d2e21240636f5f94d37f8a0f3d7904450"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0514f491f4cc03632ab399ee01e2c1c1b12d3e1cf2d667c1ff5f87d6dcd2084"
 dependencies = [
  "bitflags",
  "block",
  "core-graphics-types",
  "foreign-types",
  "log",
  "objc",
 ]
@@ -3061,23 +2950,23 @@ checksum = "2684d4c2e97d99848d30b324b00c
 dependencies = [
  "mime",
  "unicase",
 ]
 
 [[package]]
 name = "minidump_writer_linux"
 version = "0.1.0"
-source = "git+https://github.com/msirringhaus/minidump_writer_linux.git?rev=85551909b95a5cf553a85dbcddfa5f117cfbbe0e#85551909b95a5cf553a85dbcddfa5f117cfbbe0e"
+source = "git+https://github.com/msirringhaus/minidump_writer_linux.git?rev=029ac0d54b237f27dc7d8d4e51bc0fb076e5e852#029ac0d54b237f27dc7d8d4e51bc0fb076e5e852"
 dependencies = [
  "byteorder",
  "goblin",
  "libc",
  "memmap2 0.2.3",
- "memoffset",
+ "memoffset 0.5.6",
  "nix",
  "tempfile",
  "thiserror",
 ]
 
 [[package]]
 name = "miniz_oxide"
 version = "0.4.4"
@@ -3186,17 +3075,17 @@ dependencies = [
  "uuid",
  "walkdir",
 ]
 
 [[package]]
 name = "mozglue-static"
 version = "0.1.0"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.5.2",
  "cc",
  "rustc_version",
 ]
 
 [[package]]
 name = "mozilla-central-workspace-hack"
 version = "0.1.0"
 dependencies = [
@@ -3259,17 +3148,17 @@ dependencies = [
  "serde_json",
  "uuid",
  "winapi",
 ]
 
 [[package]]
 name = "mp4parse"
 version = "0.11.5"
-source = "git+https://github.com/mozilla/mp4parse-rust?rev=c6ba5afd856c158d9cfc1a447165fcfaaf2b797c#c6ba5afd856c158d9cfc1a447165fcfaaf2b797c"
+source = "git+https://github.com/mozilla/mp4parse-rust?rev=2dcd3d84424a02c167ac40e535a9e3231252d6c1#2dcd3d84424a02c167ac40e535a9e3231252d6c1"
 dependencies = [
  "bitreader",
  "byteorder",
  "env_logger",
  "fallible_collections",
  "log",
  "num-traits",
  "static_assertions",
@@ -3277,17 +3166,17 @@ dependencies = [
 
 [[package]]
 name = "mp4parse-gtest"
 version = "0.1.0"
 
 [[package]]
 name = "mp4parse_capi"
 version = "0.11.5"
-source = "git+https://github.com/mozilla/mp4parse-rust?rev=c6ba5afd856c158d9cfc1a447165fcfaaf2b797c#c6ba5afd856c158d9cfc1a447165fcfaaf2b797c"
+source = "git+https://github.com/mozilla/mp4parse-rust?rev=2dcd3d84424a02c167ac40e535a9e3231252d6c1#2dcd3d84424a02c167ac40e535a9e3231252d6c1"
 dependencies = [
  "byteorder",
  "fallible_collections",
  "log",
  "mp4parse",
  "num-traits",
 ]
 
@@ -3304,90 +3193,91 @@ 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.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef670817eef03d356d5a509ea275e7dd3a78ea9e24261ea3cb2dfed1abb08f64"
+version = "0.6.0"
+source = "git+https://github.com/gfx-rs/naga?rev=e226cf3#e226cf3f1d67d0bf91587e392879f0aee109ec42"
 dependencies = [
  "bit-set",
  "bitflags",
  "codespan-reporting",
  "fxhash",
+ "hexf-parse",
  "log",
  "num-traits",
  "petgraph",
- "rose_tree",
- "spirv_headers",
+ "serde",
+ "spirv",
  "thiserror",
 ]
 
 [[package]]
 name = "neqo-common"
-version = "0.4.31"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.31#37bb62fa7b43ac624406090ba4f94f4cc082790b"
+version = "0.5.0"
+source = "git+https://github.com/mozilla/neqo?tag=v0.5.0#40d8c760d0dd252c3f8c019c1cadfd4c4dad1e95"
 dependencies = [
  "chrono",
  "env_logger",
  "lazy_static",
  "log",
  "qlog",
  "winapi",
 ]
 
 [[package]]
 name = "neqo-crypto"
-version = "0.4.31"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.31#37bb62fa7b43ac624406090ba4f94f4cc082790b"
+version = "0.5.0"
+source = "git+https://github.com/mozilla/neqo?tag=v0.5.0#40d8c760d0dd252c3f8c019c1cadfd4c4dad1e95"
 dependencies = [
  "bindgen",
  "log",
  "neqo-common",
  "serde",
  "serde_derive",
  "toml",
 ]
 
 [[package]]
 name = "neqo-http3"
-version = "0.4.31"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.31#37bb62fa7b43ac624406090ba4f94f4cc082790b"
+version = "0.5.0"
+source = "git+https://github.com/mozilla/neqo?tag=v0.5.0#40d8c760d0dd252c3f8c019c1cadfd4c4dad1e95"
 dependencies = [
  "log",
  "neqo-common",
  "neqo-crypto",
  "neqo-qpack",
  "neqo-transport",
  "qlog",
+ "sfv 0.9.1",
  "smallvec",
 ]
 
 [[package]]
 name = "neqo-qpack"
-version = "0.4.31"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.31#37bb62fa7b43ac624406090ba4f94f4cc082790b"
+version = "0.5.0"
+source = "git+https://github.com/mozilla/neqo?tag=v0.5.0#40d8c760d0dd252c3f8c019c1cadfd4c4dad1e95"
 dependencies = [
  "lazy_static",
  "log",
  "neqo-common",
  "neqo-crypto",
  "neqo-transport",
  "qlog",
  "static_assertions",
 ]
 
 [[package]]
 name = "neqo-transport"
-version = "0.4.31"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.31#37bb62fa7b43ac624406090ba4f94f4cc082790b"
+version = "0.5.0"
+source = "git+https://github.com/mozilla/neqo?tag=v0.5.0#40d8c760d0dd252c3f8c019c1cadfd4c4dad1e95"
 dependencies = [
  "indexmap",
  "lazy_static",
  "log",
  "neqo-common",
  "neqo-crypto",
  "qlog",
  "smallvec",
@@ -3433,18 +3323,19 @@ dependencies = [
 [[package]]
 name = "new_debug_unreachable"
 version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
 
 [[package]]
 name = "nix"
-version = "0.13.1"
-source = "git+https://github.com/shravanrn/nix/?rev=4af6c367603869a30fddb5ffb0aba2b9477ba92e#4af6c367603869a30fddb5ffb0aba2b9477ba92e"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
 dependencies = [
  "bitflags",
  "cc",
  "cfg-if 0.1.10",
  "libc",
  "void",
 ]
 
@@ -3729,19 +3620,19 @@ checksum = "19b17cddbe7ec3f8bc800887bab5
 [[package]]
 name = "percent-encoding"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 [[package]]
 name = "petgraph"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f"
 dependencies = [
  "fixedbitset",
  "indexmap",
 ]
 
 [[package]]
 name = "phf"
 version = "0.8.0"
@@ -3967,17 +3858,17 @@ dependencies = [
  "syn",
 ]
 
 [[package]]
 name = "profiler_helper"
 version = "0.1.0"
 dependencies = [
  "goblin",
- "memmap",
+ "memmap2 0.3.0",
  "object",
  "rustc-demangle",
  "thin-vec",
  "uuid",
 ]
 
 [[package]]
 name = "profiling"
@@ -4122,35 +4013,35 @@ version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "rayon"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
 dependencies = [
  "autocfg",
- "crossbeam-deque",
+ "crossbeam-deque 0.8.1",
  "either",
  "rayon-core",
 ]
 
 [[package]]
 name = "rayon-core"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
-dependencies = [
- "crossbeam-channel 0.4.4",
- "crossbeam-deque",
- "crossbeam-utils 0.7.2",
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque 0.8.1",
+ "crossbeam-utils 0.8.5",
  "lazy_static",
  "num_cpus",
 ]
 
 [[package]]
 name = "redox_syscall"
 version = "0.1.56"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4266,35 +4157,26 @@ dependencies = [
  "serde_derive",
  "thiserror",
  "url",
  "uuid",
 ]
 
 [[package]]
 name = "ron"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8a58080b7bb83b2ea28c3b7a9a994fd5e310330b7c8ca5258d99b98128ecfe4"
-dependencies = [
- "base64 0.12.3",
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45005aa836116903a49cf3461474da697cfe66221762c6e95871092009ec86d6"
+dependencies = [
+ "base64 0.13.0",
  "bitflags",
  "serde",
 ]
 
 [[package]]
-name = "rose_tree"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "284de9dae38774e2813aaabd7e947b4a6fe9b8c58c2309f754a487cdd50de1c2"
-dependencies = [
- "petgraph",
-]
-
-[[package]]
 name = "rsclientcerts"
 version = "0.1.0"
 dependencies = [
  "byteorder",
  "pkcs11",
 ]
 
 [[package]]
@@ -4359,17 +4241,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "rust_decimal"
 version = "1.14.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9787e62372fc0c5a0f3af64c392652db72d3ec1cc0cff1becc175d2c11e6fbcc"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.5.2",
  "num-traits",
  "serde",
 ]
 
 [[package]]
 name = "rust_minidump_writer_linux"
 version = "0.1.0"
 dependencies = [
@@ -4598,16 +4480,27 @@ source = "registry+https://github.com/ru
 checksum = "13ed1dd5a626253083678d21b5c38dd94f8717b961d4b7469eb96b41173cc148"
 dependencies = [
  "data-encoding",
  "indexmap",
  "rust_decimal",
 ]
 
 [[package]]
+name = "sfv"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abf6b3512f87bb7d949bf0fb4a0ac5977a843dd6d3dc718701484e79de37018b"
+dependencies = [
+ "data-encoding",
+ "indexmap",
+ "rust_decimal",
+]
+
+[[package]]
 name = "sha-1"
 version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
 dependencies = [
  "block-buffer",
  "digest",
  "fake-simd",
@@ -4644,16 +4537,21 @@ version = "0.0.1"
 
 [[package]]
 name = "slab"
 version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
 
 [[package]]
+name = "slotmap"
+version = "1.100.0"
+source = "git+https://github.com/kvark/dummy-web#5731e569d865a1ebaf116f48dad781f355a99243"
+
+[[package]]
 name = "smallbitvec"
 version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "797a4eaffb90d896f29698d45676f9f940a71936d7574996a7df54593ba209fa"
 
 [[package]]
 name = "smallvec"
 version = "1.6.1"
@@ -4680,36 +4578,20 @@ source = "registry+https://github.com/ru
 checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
 dependencies = [
  "cfg-if 1.0.0",
  "libc",
  "winapi",
 ]
 
 [[package]]
-name = "spirv-cross-internal"
-version = "0.1.0"
-source = "git+https://github.com/kvark/spirv_cross?branch=wgpu5#a5a90d38ab1f82ad8327b48e161dbfe556ef6c6e"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "spirv_cross"
-version = "0.23.0"
-source = "git+https://github.com/kvark/spirv_cross?branch=wgpu5#a5a90d38ab1f82ad8327b48e161dbfe556ef6c6e"
-dependencies = [
- "spirv-cross-internal",
-]
-
-[[package]]
-name = "spirv_headers"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f5b132530b1ac069df335577e3581765995cba5a13995cdbbdbc8fb057c532c"
+name = "spirv"
+version = "0.2.0+1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
 dependencies = [
  "bitflags",
  "num-traits",
 ]
 
 [[package]]
 name = "sql-support"
 version = "0.1.0"
@@ -4745,25 +4627,16 @@ dependencies = [
  "libc",
  "nserror",
  "nsstring",
  "storage_variant",
  "xpcom",
 ]
 
 [[package]]
-name = "storage-map"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418bb14643aa55a7841d5303f72cf512cfb323b8cc221d51580500a1ca75206c"
-dependencies = [
- "lock_api",
-]
-
-[[package]]
 name = "storage_variant"
 version = "0.1.0"
 dependencies = [
  "libc",
  "nserror",
  "nsstring",
  "xpcom",
 ]
@@ -4774,17 +4647,17 @@ version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
 
 [[package]]
 name = "style"
 version = "0.0.1"
 dependencies = [
  "app_units",
- "arrayvec",
+ "arrayvec 0.5.2",
  "atomic_refcell",
  "bindgen",
  "bitflags",
  "byteorder",
  "cssparser",
  "derive_more",
  "euclid",
  "fallible",
@@ -5035,22 +4908,16 @@ dependencies = [
 
 [[package]]
 name = "threadbound"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2b5b2bd897775cb425729882f0710639eb69f3d784db834ee85941ae9c35bb83"
 
 [[package]]
-name = "thunderdome"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87b4947742c93ece24a0032141d9caa3d853752e694a57e35029dd2bd08673e0"
-
-[[package]]
 name = "time"
 version = "0.1.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
 dependencies = [
  "libc",
  "winapi",
 ]
@@ -5205,17 +5072,17 @@ dependencies = [
 ]
 
 [[package]]
 name = "tokio-threadpool"
 version = "0.1.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f0c32ffea4827978e9aa392d2f743d973c1dfa3730a2ed3f22ce1e6984da848c"
 dependencies = [
- "crossbeam-deque",
+ "crossbeam-deque 0.7.4",
  "crossbeam-queue",
  "crossbeam-utils 0.6.6",
  "futures 0.1.31",
  "lazy_static",
  "log",
  "num_cpus",
  "slab",
  "tokio-executor",
@@ -5332,17 +5199,17 @@ source = "registry+https://github.com/ru
 checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
 
 [[package]]
 name = "uluru"
 version = "1.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6ee6f45294ecb6e220e76b8afddb50a8b69d433f31ad9eb2d16fb44029ef5db"
 dependencies = [
- "arrayvec",
+ "arrayvec 0.5.2",
 ]
 
 [[package]]
 name = "unic-langid"
 version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5"
 dependencies = [
@@ -5535,16 +5402,21 @@ dependencies = [
 
 [[package]]
 name = "wasi"
 version = "0.9.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 
 [[package]]
+name = "wasm-bindgen"
+version = "0.2.100"
+source = "git+https://github.com/kvark/dummy-web#5731e569d865a1ebaf116f48dad781f355a99243"
+
+[[package]]
 name = "wasmparser"
 version = "0.78.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "52144d4c78e5cf8b055ceab8e5fa22814ce4315d6002ad32cfd914f37c12fd65"
 
 [[package]]
 name = "wast"
 version = "37.0.0"
@@ -5559,16 +5431,21 @@ name = "wat"
 version = "1.0.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2ab2cc8d9a69d1ab28a41d9149bb06bb927aba8fc9d56625f8b597a564c83f50"
 dependencies = [
  "wast",
 ]
 
 [[package]]
+name = "web-sys"
+version = "0.3.100"
+source = "git+https://github.com/kvark/dummy-web#5731e569d865a1ebaf116f48dad781f355a99243"
+
+[[package]]
 name = "webdriver"
 version = "0.43.1"
 dependencies = [
  "base64 0.12.3",
  "bytes 0.5.6",
  "cookie",
  "http",
  "log",
@@ -5670,17 +5547,17 @@ dependencies = [
 name = "webrender_api"
 version = "0.61.0"
 dependencies = [
  "app_units",
  "bitflags",
  "byteorder",
  "core-foundation",
  "core-graphics",
- "crossbeam-channel 0.5.1",
+ "crossbeam-channel",
  "derive_more",
  "euclid",
  "malloc_size_of_derive",
  "peek-poke",
  "serde",
  "serde_bytes",
  "serde_derive",
  "time",
@@ -5732,59 +5609,89 @@ source = "registry+https://github.com/ru
 checksum = "98db6ff463a94d727ee7c1188bab33146468add6dfb94df30a1f4a3495a700d9"
 dependencies = [
  "log",
  "url",
 ]
 
 [[package]]
 name = "wgpu-core"
-version = "0.9.2"
-dependencies = [
- "arrayvec",
+version = "0.10.0"
+source = "git+https://github.com/gfx-rs/wgpu?rev=37288a6#37288a657f022ce508f3b1c92eb6cdbe20f4cca9"
+dependencies = [
+ "arrayvec 0.7.1",
  "bitflags",
  "cfg_aliases",
  "copyless",
  "fxhash",
- "gfx-backend-dx11",
- "gfx-backend-dx12",
- "gfx-backend-empty",
- "gfx-backend-metal",
- "gfx-backend-vulkan",
- "gfx-hal",
- "gpu-alloc",
- "gpu-descriptor",
  "log",
  "naga",
  "parking_lot",
  "profiling",
  "ron",
  "serde",
  "smallvec",
  "thiserror",
+ "wgpu-hal",
  "wgpu-types",
 ]
 
 [[package]]
+name = "wgpu-hal"
+version = "0.10.1"
+source = "git+https://github.com/gfx-rs/wgpu?rev=37288a6#37288a657f022ce508f3b1c92eb6cdbe20f4cca9"
+dependencies = [
+ "arrayvec 0.7.1",
+ "ash",
+ "bit-set",
+ "bitflags",
+ "block",
+ "core-graphics-types",
+ "d3d12",
+ "foreign-types",
+ "fxhash",
+ "glow",
+ "gpu-alloc",
+ "gpu-descriptor",
+ "inplace_it",
+ "khronos-egl",
+ "libloading 0.7.0",
+ "log",
+ "metal",
+ "naga",
+ "objc",
+ "parking_lot",
+ "range-alloc",
+ "raw-window-handle",
+ "renderdoc-sys",
+ "thiserror",
+ "wgpu-types",
+ "winapi",
+]
+
+[[package]]
 name = "wgpu-types"
-version = "0.9.0"
+version = "0.10.0"
+source = "git+https://github.com/gfx-rs/wgpu?rev=37288a6#37288a657f022ce508f3b1c92eb6cdbe20f4cca9"
 dependencies = [
  "bitflags",
+ "bitflags_serde_shim",
  "serde",
 ]
 
 [[package]]
 name = "wgpu_bindings"
 version = "0.1.0"
 dependencies = [
  "bincode",
  "log",
  "parking_lot",
  "serde",
  "wgpu-core",
+ "wgpu-hal",
  "wgpu-types",
 ]
 
 [[package]]
 name = "whatsys"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c24fff5aa1e0973964ba23a995e8b10fa2cdeae507e0cbbbd36f8403242a765d"
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,29 +25,32 @@ exclude = [
   "build/liblowercase",
 
   # Exclude third-party code vendored into mozilla-central.
   "servo",
   "third_party/rust",
 
   # Excluded because these crates have their own Cargo workspaces so they can't
   # be included in the top-level one.
-  "gfx/wgpu",
   "gfx/wr",
 
   # Excluded because they are used only as dependencies, not top-level targets,
   # so we don't need to vendor their dev-dependencies.
   "gfx/webrender_bindings",
   "media/mp4parse-rust/mp4parse",
   "media/mp4parse-rust/mp4parse_capi",
   "xpcom/rust/gkrust_utils",
   "tools/lint/test/files/clippy",
   "tools/fuzzing/rust",
 ]
 
+# Use the new dependency resolver to reduce some of the platform-specific dependencies.
+# This is required for 'third_party/rust/wgpu-hal'
+resolver = "2"
+
 # Explicitly specify what our profiles use.  The opt-level setting here is
 # a total fiction; see the setup of MOZ_RUST_DEFAULT_FLAGS for what the
 # opt-level setting will be as a result of various other configure flags.
 [profile.dev]
 opt-level = 1
 rpath = false
 lto = false
 debug-assertions = true
@@ -74,20 +77,23 @@ opt-level = 2
 [profile.release.package.glsl]
 opt-level = 2
 
 [patch.crates-io]
 chardetng = { git = "https://github.com/hsivonen/chardetng", rev="302c995f91f44cf26e77dc4758ad56c3ff0153ad" }
 chardetng_c = { git = "https://github.com/hsivonen/chardetng_c", rev="ed8a4c6f900a90d4dbc1d64b856e61490a1c3570" }
 libudev-sys = { path = "dom/webauthn/libudev-sys" }
 packed_simd = { git = "https://github.com/hsivonen/packed_simd", rev="6a16f954950401b92b4e220fbf2dfaf6f00e1fb2" }
-nix = { git = "https://github.com/shravanrn/nix/", rev="4af6c367603869a30fddb5ffb0aba2b9477ba92e" }
-spirv_cross = { git = "https://github.com/kvark/spirv_cross", branch = "wgpu5" }
-minidump_writer_linux = { git = "https://github.com/msirringhaus/minidump_writer_linux.git", rev = "85551909b95a5cf553a85dbcddfa5f117cfbbe0e" }
-
+minidump_writer_linux = { git = "https://github.com/msirringhaus/minidump_writer_linux.git", rev = "029ac0d54b237f27dc7d8d4e51bc0fb076e5e852" }
+# The following overrides point to dummy projects, as a temporary measure until this is resolved:
+# https://github.com/rust-lang/cargo/issues/6179
+js-sys = { git = "https://github.com/kvark/dummy-web" }
+slotmap = { git = "https://github.com/kvark/dummy-web" }
+wasm-bindgen = { git = "https://github.com/kvark/dummy-web" }
+web-sys = { git = "https://github.com/kvark/dummy-web" }
 
 [patch.crates-io.cranelift-codegen]
 git = "https://github.com/bytecodealliance/wasmtime"
 rev = "824fa69756523f2b6d49029fe25de94130b1f144"
 
 [patch.crates-io.cranelift-wasm]
 git = "https://github.com/bytecodealliance/wasmtime"
 rev = "824fa69756523f2b6d49029fe25de94130b1f144"
--- a/accessible/android/AccessibleWrap.cpp
+++ b/accessible/android/AccessibleWrap.cpp
@@ -131,17 +131,17 @@ nsresult AccessibleWrap::HandleAccEvent(
 
   // The accessible can become defunct if we have an xpcom event listener
   // which decides it would be fun to change the DOM and flush layout.
   if (accessible->IsDefunct() || !accessible->IsBoundToParent()) {
     return NS_OK;
   }
 
   if (doc) {
-    if (!nsCoreUtils::IsContentDocument(doc->DocumentNode())) {
+    if (!doc->DocumentNode()->IsContentDocument()) {
       return NS_OK;
     }
   }
 
   RefPtr<SessionAccessibility> sessionAcc =
       SessionAccessibility::GetInstanceFor(accessible);
   if (!sessionAcc) {
     return NS_OK;
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -546,23 +546,18 @@ static void MaybeFireNameChange(AtkObjec
   free(aAtkObj->name);
   aAtkObj->name = strdup(newNameUTF8.get());
 
   if (notify) g_object_notify(G_OBJECT(aAtkObj), "accessible-name");
 }
 
 const gchar* getDescriptionCB(AtkObject* aAtkObj) {
   nsAutoString uniDesc;
-  AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
-  if (accWrap) {
-    if (accWrap->IsDefunct()) return nullptr;
-
-    accWrap->Description(uniDesc);
-  } else if (RemoteAccessible* proxy = GetProxy(aAtkObj)) {
-    proxy->Description(uniDesc);
+  if (Accessible* acc = GetInternalObj(aAtkObj)) {
+    acc->Description(uniDesc);
   } else {
     return nullptr;
   }
 
   NS_ConvertUTF8toUTF16 objDesc(aAtkObj->description);
   if (!uniDesc.Equals(objDesc)) {
     atk_object_set_description(aAtkObj, NS_ConvertUTF16toUTF8(uniDesc).get());
   }
--- a/accessible/atk/nsMaiInterfaceValue.cpp
+++ b/accessible/atk/nsMaiInterfaceValue.cpp
@@ -14,49 +14,61 @@
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 extern "C" {
 
 static void getCurrentValueCB(AtkValue* obj, GValue* value) {
   Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+  if (!acc) {
+    return;
+  }
 
   memset(value, 0, sizeof(GValue));
   double accValue = acc->CurValue();
   if (IsNaN(accValue)) return;
 
   g_value_init(value, G_TYPE_DOUBLE);
   g_value_set_double(value, accValue);
 }
 
 static void getMaximumValueCB(AtkValue* obj, GValue* value) {
   Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+  if (!acc) {
+    return;
+  }
 
   memset(value, 0, sizeof(GValue));
   double accValue = acc->MaxValue();
   if (IsNaN(accValue)) return;
 
   g_value_init(value, G_TYPE_DOUBLE);
   g_value_set_double(value, accValue);
 }
 
 static void getMinimumValueCB(AtkValue* obj, GValue* value) {
   Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+  if (!acc) {
+    return;
+  }
 
   memset(value, 0, sizeof(GValue));
   double accValue = acc->MinValue();
   if (IsNaN(accValue)) return;
 
   g_value_init(value, G_TYPE_DOUBLE);
   g_value_set_double(value, accValue);
 }
 
 static void getMinimumIncrementCB(AtkValue* obj, GValue* minimumIncrement) {
   Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+  if (!acc) {
+    return;
+  }
 
   memset(minimumIncrement, 0, sizeof(GValue));
   double accValue = acc->Step();
   if (IsNaN(accValue)) {
     accValue = 0;  // zero if the minimum increment is undefined
   }
 
   g_value_init(minimumIncrement, G_TYPE_DOUBLE);
--- a/accessible/base/AccAttributes.cpp
+++ b/accessible/base/AccAttributes.cpp
@@ -30,26 +30,41 @@ void AccAttributes::StringFromValueAndNa
         aValueString.AppendFloat(val * 100);
         aValueString.Append(u"%");
       },
       [&aValueString](const double& val) { aValueString.AppendFloat(val); },
       [&aValueString](const int32_t& val) { aValueString.AppendInt(val); },
       [&aValueString](const RefPtr<nsAtom>& val) {
         val->ToString(aValueString);
       },
+      [&aValueString](const CopyableTArray<int32_t>& val) {
+        for (size_t i = 0; i < val.Length() - 1; i++) {
+          aValueString.AppendInt(val[i]);
+          aValueString.Append(u", ");
+        }
+        aValueString.AppendInt(val[val.Length() - 1]);
+      },
       [&aValueString](const CSSCoord& val) {
         aValueString.AppendFloat(val);
         aValueString.Append(u"px");
       },
       [&aValueString](const FontSize& val) {
         aValueString.AppendInt(val.mValue);
         aValueString.Append(u"pt");
       },
       [&aValueString](const Color& val) {
         StyleInfo::FormatColor(val.mValue, aValueString);
+      },
+      [&aValueString](const DeleteEntry& val) {
+        aValueString.Append(u"-delete-entry-");
       });
 }
 
 void AccAttributes::Update(AccAttributes* aOther) {
   for (auto entry : *aOther) {
+    if (entry.mValue->is<DeleteEntry>()) {
+      mData.Remove(entry.mName);
+      continue;
+    }
+
     mData.InsertOrUpdate(entry.mName, *entry.mValue);
   }
 }
--- a/accessible/base/AccAttributes.h
+++ b/accessible/base/AccAttributes.h
@@ -30,21 +30,28 @@ namespace a11y {
 struct FontSize {
   int32_t mValue;
 };
 
 struct Color {
   nscolor mValue;
 };
 
+// A special type. If an entry has a value of this type, it instructs the
+// target instance of an Update to remove the entry with the same key value.
+struct DeleteEntry {
+  DeleteEntry() : mValue(true) {}
+  bool mValue;
+};
+
 class AccAttributes {
   friend struct IPC::ParamTraits<AccAttributes*>;
-
-  using AttrValueType = Variant<bool, float, double, int32_t, RefPtr<nsAtom>,
-                                CSSCoord, FontSize, Color>;
+  using AttrValueType =
+      Variant<bool, float, double, int32_t, RefPtr<nsAtom>, CopyableTArray<int32_t>,
+              CSSCoord, FontSize, Color, DeleteEntry>;
   static_assert(sizeof(AttrValueType) <= 16);
   using AtomVariantMap = nsTHashMap<nsRefPtrHashKey<nsAtom>, AttrValueType>;
 
  protected:
   ~AccAttributes() = default;
 
  public:
   AccAttributes() = default;
--- a/accessible/base/CacheConstants.h
+++ b/accessible/base/CacheConstants.h
@@ -7,18 +7,30 @@
 #ifndef _CacheConstants_h_
 #define _CacheConstants_h_
 
 namespace mozilla {
 namespace a11y {
 
 class CacheDomain {
  public:
-  static constexpr uint64_t Name = ((uint64_t)0x1) << 0;
+  static constexpr uint64_t NameAndDescription = ((uint64_t)0x1) << 0;
   static constexpr uint64_t Value = ((uint64_t)0x1) << 1;
   static constexpr uint64_t Bounds = ((uint64_t)0x1) << 2;
   static constexpr uint64_t All = ~((uint64_t)0x0);
 };
 
+enum class CacheUpdateType {
+  /*
+   * An initial cache push of a loaded document or inserted subtree.
+   */
+  Initial,
+
+  /*
+   * An incremental cache push of one or more fields that have changed.
+   */
+  Update,
+};
+
 }  // namespace a11y
 }  // namespace mozilla
 
 #endif
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -241,17 +241,17 @@ DocManager::OnStateChange(nsIWebProgress
 #endif
 
     // Figure out an event type to notify the document has been loaded.
     uint32_t eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED;
 
     // Some XUL documents get start state and then stop state with failure
     // status when everything is ok. Fire document load complete event in this
     // case.
-    if (NS_SUCCEEDED(aStatus) || !nsCoreUtils::IsContentDocument(document)) {
+    if (NS_SUCCEEDED(aStatus) || !document->IsContentDocument()) {
       eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE;
     }
 
     // If end consumer has been retargeted for loaded content then do not fire
     // any event because it means no new document has been loaded, for example,
     // it happens when user clicks on file link.
     if (aRequest) {
       uint32_t loadFlags = 0;
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -111,17 +111,17 @@ static void LogDocShellState(dom::Docume
   }
   if (busyFlags & nsIDocShell::BUSY_FLAGS_PAGE_LOADING) {
     printf(", 'page loading'");
   }
 }
 
 static void LogDocType(dom::Document* aDocumentNode) {
   if (aDocumentNode->IsActive()) {
-    bool isContent = nsCoreUtils::IsContentDocument(aDocumentNode);
+    bool isContent = aDocumentNode->IsContentDocument();
     printf("%s document", (isContent ? "content" : "chrome"));
   } else {
     printf("document type: [failed]");
   }
 }
 
 static void LogDocShellTree(dom::Document* aDocumentNode) {
   if (aDocumentNode->IsActive()) {
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -351,23 +351,16 @@ bool nsCoreUtils::IsRootDocument(Documen
   NS_ASSERTION(docShellTreeItem, "No document shell for document!");
 
   nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
   docShellTreeItem->GetInProcessParent(getter_AddRefs(parentTreeItem));
 
   return !parentTreeItem;
 }
 
-bool nsCoreUtils::IsContentDocument(Document* aDocument) {
-  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = aDocument->GetDocShell();
-  NS_ASSERTION(docShellTreeItem, "No document shell tree item for document!");
-
-  return (docShellTreeItem->ItemType() == nsIDocShellTreeItem::typeContent);
-}
-
 bool nsCoreUtils::IsTopLevelContentDocInProcess(Document* aDocumentNode) {
   mozilla::dom::BrowsingContext* bc = aDocumentNode->GetBrowsingContext();
   return bc->IsContent() && (
                                 // Tab document.
                                 bc->IsTop() ||
                                 // Out-of-process iframe.
                                 !bc->GetParent()->IsInProcess());
 }
--- a/accessible/base/nsCoreUtils.h
+++ b/accessible/base/nsCoreUtils.h
@@ -194,21 +194,16 @@ class nsCoreUtils {
   static already_AddRefed<nsIDocShell> GetDocShellFor(nsINode* aNode);
 
   /**
    * Return true if the given document is root document.
    */
   static bool IsRootDocument(Document* aDocument);
 
   /**
-   * Return true if the given document is content document (not chrome).
-   */
-  static bool IsContentDocument(Document* aDocument);
-
-  /**
    * Return true if the given document is a top level content document in this
    * process.
    * This will be true for tab documents and out-of-process iframe documents.
    */
   static bool IsTopLevelContentDocInProcess(Document* aDocumentNode);
 
   /**
    * Return true if the given document is an error page.
--- a/accessible/basetypes/Accessible.h
+++ b/accessible/basetypes/Accessible.h
@@ -110,16 +110,21 @@ class Accessible {
 
   // Methods that potentially access a cache.
 
   /*
    * Get the name of this accessible.
    */
   virtual ENameValueFlag Name(nsString& aName) const = 0;
 
+  /*
+   * Get the description of this accessible.
+   */
+  virtual void Description(nsString& aDescription) const = 0;
+
   virtual double CurValue() const = 0;
   virtual double MinValue() const = 0;
   virtual double MaxValue() const = 0;
   virtual double Step() const = 0;
 
   // Type "is" methods
 
   bool IsDoc() const { return HasGenericType(eDocument); }
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -53,17 +53,17 @@ ENameValueFlag ApplicationAccessible::Na
     NS_WARNING("brandShortName not found, using default app name");
     appName.AssignLiteral("Gecko based application");
   }
 
   aName.Assign(appName);
   return eNameOK;
 }
 
-void ApplicationAccessible::Description(nsString& aDescription) {
+void ApplicationAccessible::Description(nsString& aDescription) const {
   aDescription.Truncate();
 }
 
 void ApplicationAccessible::Value(nsString& aValue) const { aValue.Truncate(); }
 
 uint64_t ApplicationAccessible::State() {
   return IsDefunct() ? states::DEFUNCT : 0;
 }
--- a/accessible/generic/ApplicationAccessible.h
+++ b/accessible/generic/ApplicationAccessible.h
@@ -34,17 +34,17 @@ class ApplicationAccessible : public Acc
   // LocalAccessible
   virtual void Shutdown() override;
   virtual nsIntRect Bounds() const override;
   virtual nsRect BoundsInAppUnits() const override;
   virtual already_AddRefed<AccAttributes> NativeAttributes() override;
   virtual GroupPos GroupPosition() override;
   virtual ENameValueFlag Name(nsString& aName) const override;
   virtual void ApplyARIAState(uint64_t* aState) const override;
-  virtual void Description(nsString& aDescription) override;
+  virtual void Description(nsString& aDescription) const override;
   virtual void Value(nsString& aValue) const override;
   virtual mozilla::a11y::role NativeRole() const override;
   virtual uint64_t State() override;
   virtual uint64_t NativeState() const override;
   virtual Relation RelationByType(RelationType aType) const override;
 
   virtual LocalAccessible* LocalChildAtPoint(
       int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) override;
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -203,17 +203,17 @@ role DocAccessible::NativeRole() const {
     } else if (itemType == nsIDocShellTreeItem::typeContent) {
       return roles::DOCUMENT;
     }
   }
 
   return roles::PANE;  // Fall back;
 }
 
-void DocAccessible::Description(nsString& aDescription) {
+void DocAccessible::Description(nsString& aDescription) const {
   if (mParent) mParent->Description(aDescription);
 
   if (HasOwnContent() && aDescription.IsEmpty()) {
     nsTextEquivUtils::GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
                                              aDescription);
   }
 }
 
@@ -2449,18 +2449,17 @@ void DocAccessible::ARIAActiveDescendant
 }
 
 void DocAccessible::SetRoleMapEntryForDoc(dom::Element* aElement) {
   const nsRoleMapEntry* entry = aria::GetRoleMap(aElement);
   if (!entry || entry->role == roles::APPLICATION ||
       entry->role == roles::DIALOG ||
       // Role alert isn't valid on the body element according to the ARIA spec,
       // but it's useful for our UI; e.g. the WebRTC sharing indicator.
-      (entry->role == roles::ALERT &&
-       !nsCoreUtils::IsContentDocument(mDocumentNode))) {
+      (entry->role == roles::ALERT && !mDocumentNode->IsContentDocument())) {
     SetRoleMapEntry(entry);
     return;
   }
   // No other ARIA roles are valid on body elements.
   SetRoleMapEntry(nullptr);
 }
 
 LocalAccessible* DocAccessible::GetAccessible(nsINode* aNode) const {
--- a/accessible/generic/DocAccessible.h
+++ b/accessible/generic/DocAccessible.h
@@ -64,17 +64,17 @@ class DocAccessible : public HyperTextAc
   // LocalAccessible
   virtual void Init();
   virtual void Shutdown() override;
   virtual nsIFrame* GetFrame() const override;
   virtual nsINode* GetNode() const override;
   Document* DocumentNode() const { return mDocumentNode; }
 
   virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override;
-  virtual void Description(nsString& aDescription) override;
+  virtual void Description(nsString& aDescription) const override;
   virtual LocalAccessible* FocusedChild() override;
   virtual mozilla::a11y::role NativeRole() const override;
   virtual uint64_t NativeState() const override;
   virtual uint64_t NativeInteractiveState() const override;
   virtual bool NativelyUnavailable() const override;
   virtual void ApplyARIAState(uint64_t* aState) const override;
   virtual already_AddRefed<AccAttributes> Attributes() override;
 
--- a/accessible/generic/LocalAccessible.cpp
+++ b/accessible/generic/LocalAccessible.cpp
@@ -164,17 +164,17 @@ ENameValueFlag LocalAccessible::Name(nsS
     }
   }
 
   if (nameFlag != eNoNameOnPurpose) aName.SetIsVoid(true);
 
   return nameFlag;
 }
 
-void LocalAccessible::Description(nsString& aDescription) {
+void LocalAccessible::Description(nsString& aDescription) const {
   // There are 4 conditions that make an accessible have no accDescription:
   // 1. it's a text node; or
   // 2. It has no ARIA describedby or description property
   // 3. it doesn't have an accName; or
   // 4. its title attribute already equals to its accName nsAutoString name;
 
   if (!HasOwnContent() || mContent->IsText()) return;
 
@@ -952,18 +952,19 @@ nsresult LocalAccessible::HandleAccEvent
                     ? 0
                     : reinterpret_cast<uint64_t>(end->UniqueID()),
                 range.StartOffset(), range.EndOffset()));
           }
           ipcDoc->SendTextSelectionChangeEvent(id, textRangeData);
           break;
         }
 #endif
+        case nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE:
         case nsIAccessibleEvent::EVENT_NAME_CHANGE: {
-          SendCacheUpdate(CacheDomain::Name);
+          SendCacheUpdate(CacheDomain::NameAndDescription);
           ipcDoc->SendEvent(id, aEvent->GetEventType());
           break;
         }
         case nsIAccessibleEvent::EVENT_VALUE_CHANGE: {
           SendCacheUpdate(CacheDomain::Value);
           ipcDoc->SendEvent(id, aEvent->GetEventType());
           break;
         }
@@ -2346,17 +2347,17 @@ ENameValueFlag LocalAccessible::NativeNa
       }
     }
   }
 
   return eNameOK;
 }
 
 // LocalAccessible protected
-void LocalAccessible::NativeDescription(nsString& aDescription) {
+void LocalAccessible::NativeDescription(nsString& aDescription) const {
   bool isXUL = mContent->IsXULElement();
   if (isXUL) {
     // Try XUL <description control="[id]">description text</description>
     XULDescriptionIterator iter(Document(), mContent);
     LocalAccessible* descr = nullptr;
     while ((descr = iter.Next())) {
       nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
                                                    &aDescription);
@@ -3002,32 +3003,50 @@ void LocalAccessible::SendCacheUpdate(ui
 
   if (!IPCAccessibilityActive() || !Document()) {
     return;
   }
 
   DocAccessibleChild* ipcDoc = mDoc->IPCDoc();
   MOZ_ASSERT(ipcDoc);
 
-  RefPtr<AccAttributes> fields = BundleFieldsForCache(aCacheDomain);
+  RefPtr<AccAttributes> fields =
+      BundleFieldsForCache(aCacheDomain, CacheUpdateType::Update);
   nsTArray<CacheData> data;
   data.AppendElement(
       CacheData(IsDoc() ? 0 : reinterpret_cast<uint64_t>(UniqueID()), fields));
-  ipcDoc->SendCache(1, data, true);
+  ipcDoc->SendCache(CacheUpdateType::Update, data, true);
 }
 
 already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
-    uint64_t aCacheDomain) {
+    uint64_t aCacheDomain, CacheUpdateType aUpdateType) {
   RefPtr<AccAttributes> fields = new AccAttributes();
 
-  if (aCacheDomain & CacheDomain::Name) {
+  if (aCacheDomain & CacheDomain::NameAndDescription) {
     nsAutoString name;
     int32_t nameFlag = Name(name);
-    fields->SetAttribute(nsGkAtoms::explicit_name, nameFlag);
-    fields->SetAttribute(nsGkAtoms::name, name);
+    if (nameFlag != eNameOK) {
+      fields->SetAttribute(nsGkAtoms::explicit_name, nameFlag);
+    } else if (aUpdateType == CacheUpdateType::Update) {
+      fields->SetAttribute(nsGkAtoms::explicit_name, DeleteEntry());
+    }
+
+    if (!name.IsEmpty()) {
+      fields->SetAttribute(nsGkAtoms::name, name);
+    } else if (aUpdateType == CacheUpdateType::Update) {
+      fields->SetAttribute(nsGkAtoms::name, DeleteEntry());
+    }
+
+    nsAutoString description;
+    Description(description);
+    if (!description.IsEmpty()) {
+      fields->SetAttribute(nsGkAtoms::description, description);
+    } else if (aUpdateType == CacheUpdateType::Update) {
+      fields->SetAttribute(nsGkAtoms::description, DeleteEntry());
+    }
   }
 
   if ((aCacheDomain & CacheDomain::Value) && HasNumericValue()) {
     fields->SetAttribute(nsGkAtoms::value, CurValue());
     fields->SetAttribute(nsGkAtoms::max, MaxValue());
     fields->SetAttribute(nsGkAtoms::min, MinValue());
     fields->SetAttribute(nsGkAtoms::step, Step());
   }
--- a/accessible/generic/LocalAccessible.h
+++ b/accessible/generic/LocalAccessible.h
@@ -51,16 +51,18 @@ class RemoteAccessible;
 class Relation;
 class RootAccessible;
 class TableAccessible;
 class TableCellAccessible;
 class TextLeafAccessible;
 class XULLabelAccessible;
 class XULTreeAccessible;
 
+enum class CacheUpdateType;
+
 #ifdef A11Y_LOG
 namespace logging {
 typedef const char* (*GetTreePrefix)(void* aData, LocalAccessible*);
 void Tree(const char* aTitle, const char* aMsgText, LocalAccessible* aRoot,
           GetTreePrefix aPrefixFunc, void* GetTreePrefixData);
 void TreeSize(const char* aTitle, const char* aMsgText, LocalAccessible* aRoot);
 };  // namespace logging
 #endif
@@ -154,17 +156,17 @@ class LocalAccessible : public nsISuppor
   /**
    * Return language associated with the accessible.
    */
   void Language(nsAString& aLocale);
 
   /**
    * Get the description of this accessible.
    */
-  virtual void Description(nsString& aDescription);
+  virtual void Description(nsString& aDescription) const override;
 
   /**
    * Get the value of this accessible.
    */
   virtual void Value(nsString& aValue) const;
 
   /**
    * Get help string for the accessible.
@@ -878,32 +880,33 @@ class LocalAccessible : public nsISuppor
   /**
    * Fire a focusable state change event if the previous state
    * was different.
    */
   void MaybeFireFocusableStateChange(bool aPreviouslyFocusable);
 
   virtual bool IsRemote() const override { return false; }
 
-  already_AddRefed<AccAttributes> BundleFieldsForCache(uint64_t aCacheDomain);
+  already_AddRefed<AccAttributes> BundleFieldsForCache(
+      uint64_t aCacheDomain, CacheUpdateType aUpdateType);
 
  protected:
   virtual ~LocalAccessible();
 
   /**
    * Return the accessible name provided by native markup. It doesn't take
    * into account ARIA markup used to specify the name.
    */
   virtual mozilla::a11y::ENameValueFlag NativeName(nsString& aName) const;
 
   /**
    * Return the accessible description provided by native markup. It doesn't
    * take into account ARIA markup used to specify the description.
    */
-  virtual void NativeDescription(nsString& aDescription);
+  void NativeDescription(nsString& aDescription) const;
 
   /**
    * Return object attributes provided by native markup. It doesn't take into
    * account ARIA.
    */
   virtual already_AddRefed<AccAttributes> NativeAttributes();
 
   /**
--- a/accessible/html/HTMLImageMapAccessible.cpp
+++ b/accessible/html/HTMLImageMapAccessible.cpp
@@ -128,17 +128,17 @@ ENameValueFlag HTMLAreaAccessible::Nativ
   if (!mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::alt,
                                       aName)) {
     Value(aName);
   }
 
   return eNameOK;
 }
 
-void HTMLAreaAccessible::Description(nsString& aDescription) {
+void HTMLAreaAccessible::Description(nsString& aDescription) const {
   aDescription.Truncate();
 
   // Still to do - follow IE's standard here
   RefPtr<dom::HTMLAreaElement> area =
       dom::HTMLAreaElement::FromNodeOrNull(mContent);
   if (area) area->GetShape(aDescription);
 }
 
--- a/accessible/html/HTMLImageMapAccessible.h
+++ b/accessible/html/HTMLImageMapAccessible.h
@@ -49,17 +49,17 @@ class HTMLImageMapAccessible final : pub
 /**
  * Accessible for image map areas - must be child of image.
  */
 class HTMLAreaAccessible final : public HTMLLinkAccessible {
  public:
   HTMLAreaAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // LocalAccessible
-  virtual void Description(nsString& aDescription) override;
+  virtual void Description(nsString& aDescription) const override;
   virtual LocalAccessible* LocalChildAtPoint(
       int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) override;
   virtual nsRect RelativeBounds(nsIFrame** aBoundingFrame) const override;
 
   // HyperLinkAccessible
   virtual uint32_t StartOffset() override;
   virtual uint32_t EndOffset() override;
 
--- a/accessible/html/HTMLSelectAccessible.cpp
+++ b/accessible/html/HTMLSelectAccessible.cpp
@@ -346,17 +346,17 @@ uint64_t HTMLComboboxAccessible::NativeS
   } else {
     state |= states::COLLAPSED;
   }
 
   state |= states::HASPOPUP;
   return state;
 }
 
-void HTMLComboboxAccessible::Description(nsString& aDescription) {
+void HTMLComboboxAccessible::Description(nsString& aDescription) const {
   aDescription.Truncate();
   // First check to see if combo box itself has a description, perhaps through
   // tooltip (title attribute) or via aria-describedby
   LocalAccessible::Description(aDescription);
   if (!aDescription.IsEmpty()) return;
 
   // Otherwise use description of selected option.
   LocalAccessible* option = SelectedOption();
--- a/accessible/html/HTMLSelectAccessible.h
+++ b/accessible/html/HTMLSelectAccessible.h
@@ -157,17 +157,17 @@ class HTMLComboboxAccessible final : pub
  public:
   enum { eAction_Click = 0 };
 
   HTMLComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc);
   virtual ~HTMLComboboxAccessible() {}
 
   // LocalAccessible
   virtual void Shutdown() override;
-  virtual void Description(nsString& aDescription) override;
+  virtual void Description(nsString& aDescription) const override;
   virtual void Value(nsString& aValue) const override;
   virtual a11y::role NativeRole() const override;
   virtual uint64_t NativeState() const override;
   virtual bool RemoveChild(LocalAccessible* aChild) override;
   virtual bool IsAcceptableChild(nsIContent* aEl) const override;
 
   // ActionAccessible
   virtual uint8_t ActionCount() const override;
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -804,17 +804,17 @@ nsresult HTMLTableAccessible::RemoveRows
     return tableSelection->RestrictCellsToSelection(
         mContent, startRowIdx, startColIdx, endRowIdx, endColIdx);
   }
 
   return tableSelection->RemoveCellsFromSelection(
       mContent, startRowIdx, startColIdx, endRowIdx, endColIdx);
 }
 
-void HTMLTableAccessible::Description(nsString& aDescription) {
+void HTMLTableAccessible::Description(nsString& aDescription) const {
   // Helpful for debugging layout vs. data tables
   aDescription.Truncate();
   LocalAccessible::Description(aDescription);
   if (!aDescription.IsEmpty()) return;
 
   // Use summary as description if it weren't used as a name.
   // XXX: get rid code duplication with NameInternal().
   LocalAccessible* caption = Caption();
--- a/accessible/html/HTMLTableAccessible.h
+++ b/accessible/html/HTMLTableAccessible.h
@@ -159,17 +159,17 @@ class HTMLTableAccessible : public Hyper
   virtual void SelectCol(uint32_t aColIdx) override;
   virtual void SelectRow(uint32_t aRowIdx) override;
   virtual void UnselectCol(uint32_t aColIdx) override;
   virtual void UnselectRow(uint32_t aRowIdx) override;
   virtual LocalAccessible* AsAccessible() override { return this; }
 
   // LocalAccessible
   virtual TableAccessible* AsTable() override { return this; }
-  virtual void Description(nsString& aDescription) override;
+  virtual void Description(nsString& aDescription) const override;
   virtual a11y::role NativeRole() const override;
   virtual uint64_t NativeState() const override;
   virtual already_AddRefed<AccAttributes> NativeAttributes() override;
   virtual Relation RelationByType(RelationType aRelationType) const override;
 
   virtual bool InsertChildAt(uint32_t aIndex, LocalAccessible* aChild) override;
 
  protected:
--- a/accessible/ipc/DocAccessibleChildBase.cpp
+++ b/accessible/ipc/DocAccessibleChildBase.cpp
@@ -75,22 +75,24 @@ void DocAccessibleChildBase::InsertIntoI
   ShowEventData data(parentID, aIdxInParent,
                      nsTArray<AccessibleData>(shownTree.Length()),
                      aSuppressShowEvent);
   SerializeTree(shownTree, data.NewTree());
   MaybeSendShowEvent(data, false);
   if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
     nsTArray<CacheData> cache(shownTree.Length());
     for (LocalAccessible* acc : shownTree) {
-      uint64_t id = reinterpret_cast<uint64_t>(acc->UniqueID());
       RefPtr<AccAttributes> fields =
-          acc->BundleFieldsForCache(CacheDomain::All);
-      cache.AppendElement(CacheData(id, fields));
+          acc->BundleFieldsForCache(CacheDomain::All, CacheUpdateType::Initial);
+      if (fields->Count()) {
+        uint64_t id = reinterpret_cast<uint64_t>(acc->UniqueID());
+        cache.AppendElement(CacheData(id, fields));
+      }
     }
-    Unused << SendCache(0, cache, true);
+    Unused << SendCache(CacheUpdateType::Initial, cache, true);
   }
 }
 
 void DocAccessibleChildBase::ShowEvent(AccShowEvent* aShowEvent) {
   LocalAccessible* child = aShowEvent->GetAccessible();
   InsertIntoIpcTree(aShowEvent->LocalParent(), child, child->IndexInParent(),
                     false);
 }
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -469,18 +469,18 @@ mozilla::ipc::IPCResult DocAccessiblePar
       new xpcAccScrollingEvent(aType, xpcAcc, doc, node, fromUser, aScrollX,
                                aScrollY, aMaxScrollX, aMaxScrollY);
   nsCoreUtils::DispatchAccEvent(std::move(event));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult DocAccessibleParent::RecvCache(
-    const uint8_t& aUpdateType, nsTArray<CacheData>&& aData,
-    const bool& aFinal) {
+    const mozilla::a11y::CacheUpdateType& aUpdateType,
+    nsTArray<CacheData>&& aData, const bool& aFinal) {
   for (auto& entry : aData) {
     RemoteAccessible* remote = GetAccessible(entry.ID());
     if (!remote) {
       MOZ_ASSERT_UNREACHABLE("No remote found!");
       continue;
     }
 
     remote->ApplyCache(aUpdateType, entry.Fields());
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -131,19 +131,19 @@ class DocAccessibleParent : public Remot
       const int32_t& aNewEndOffset, const int16_t& aReason,
       const int16_t& aBoundaryType, const bool& aFromUser) override;
 
   virtual mozilla::ipc::IPCResult RecvScrollingEvent(
       const uint64_t& aID, const uint64_t& aType, const uint32_t& aScrollX,
       const uint32_t& aScrollY, const uint32_t& aMaxScrollX,
       const uint32_t& aMaxScrollY) override;
 
-  virtual mozilla::ipc::IPCResult RecvCache(const uint8_t& aUpdateType,
-                                            nsTArray<CacheData>&& aData,
-                                            const bool& aFinal) override;
+  virtual mozilla::ipc::IPCResult RecvCache(
+      const mozilla::a11y::CacheUpdateType& aUpdateType,
+      nsTArray<CacheData>&& aData, const bool& aFinal) override;
 
 #if !defined(XP_WIN)
   virtual mozilla::ipc::IPCResult RecvAnnouncementEvent(
       const uint64_t& aID, const nsString& aAnnouncement,
       const uint16_t& aPriority) override;
 
   virtual mozilla::ipc::IPCResult RecvTextSelectionChangeEvent(
       const uint64_t& aID, nsTArray<TextRangeData>&& aSelection) override;
--- a/accessible/ipc/IPCTypes.h
+++ b/accessible/ipc/IPCTypes.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_IPCTypes_h
 #define mozilla_a11y_IPCTypes_h
 
 #ifdef ACCESSIBILITY
 #  include "mozilla/a11y/AccAttributes.h"
 #  include "mozilla/a11y/AccTypes.h"
+#  include "mozilla/a11y/CacheConstants.h"
 #  include "mozilla/a11y/Role.h"
 #  include "mozilla/GfxMessageUtils.h"
 #  include "ipc/EnumSerializer.h"
 #  include "ipc/IPCMessageUtilsSpecializations.h"
 
 namespace IPC {
 
 template <>
@@ -32,30 +33,50 @@ struct ParamTraits<mozilla::a11y::AccTyp
 
 template <>
 struct ParamTraits<mozilla::a11y::AccGenericType>
     : public BitFlagsEnumSerializer<
           mozilla::a11y::AccGenericType,
           mozilla::a11y::AccGenericType::eAllGenericTypes> {};
 
 template <>
+struct ParamTraits<mozilla::a11y::CacheUpdateType>
+    : public ContiguousEnumSerializerInclusive<
+          mozilla::a11y::CacheUpdateType, mozilla::a11y::CacheUpdateType::Initial,
+          mozilla::a11y::CacheUpdateType::Update> {};
+
+template <>
 struct ParamTraits<mozilla::a11y::FontSize> {
   typedef mozilla::a11y::FontSize paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     WriteParam(aMsg, aParam.mValue);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
     return ReadParam(aMsg, aIter, &(aResult->mValue));
   }
 };
 
 template <>
+struct ParamTraits<mozilla::a11y::DeleteEntry> {
+  typedef mozilla::a11y::DeleteEntry paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam) {
+    WriteParam(aMsg, aParam.mValue);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter,
+                   paramType* aResult) {
+    return ReadParam(aMsg, aIter, &(aResult->mValue));
+  }
+};
+
+template <>
 struct ParamTraits<mozilla::a11y::Color> {
   typedef mozilla::a11y::Color paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     WriteParam(aMsg, aParam.mValue);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
--- a/accessible/ipc/RemoteAccessibleBase.cpp
+++ b/accessible/ipc/RemoteAccessibleBase.cpp
@@ -171,16 +171,23 @@ ENameValueFlag RemoteAccessibleBase<Deri
         mCachedFields->GetAttribute<int32_t>(nsGkAtoms::explicit_name);
     return nameFlag ? static_cast<ENameValueFlag>(*nameFlag) : eNameOK;
   }
 
   return eNameOK;
 }
 
 template <class Derived>
+void RemoteAccessibleBase<Derived>::Description(nsString& aDescription) const {
+  if (mCachedFields) {
+    mCachedFields->GetAttribute(nsGkAtoms::description, aDescription);
+  }
+}
+
+template <class Derived>
 double RemoteAccessibleBase<Derived>::CurValue() const {
   if (auto value = mCachedFields->GetAttribute<double>(nsGkAtoms::value)) {
     return *value;
   }
 
   return UnspecifiedNaN<double>();
 }
 
--- a/accessible/ipc/RemoteAccessibleBase.h
+++ b/accessible/ipc/RemoteAccessibleBase.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_RemoteAccessibleBase_h
 #define mozilla_a11y_RemoteAccessibleBase_h
 
 #include "mozilla/a11y/Accessible.h"
+#include "mozilla/a11y/CacheConstants.h"
 #include "mozilla/a11y/Role.h"
 #include "AccAttributes.h"
 #include "nsIAccessibleText.h"
 #include "nsIAccessibleTypes.h"
 #include "nsTArray.h"
 #include "nsRect.h"
 #include "LocalAccessible.h"
 
@@ -162,16 +163,17 @@ class RemoteAccessibleBase : public Acce
     // XXX: We combine the aria and native "has numeric value" field
     // when we serialize the local accessible into eNumericValue.
     return HasGenericType(eNumericValue);
   }
 
   // Methods that potentially access a cache.
 
   virtual ENameValueFlag Name(nsString& aName) const override;
+  virtual void Description(nsString& aDescription) const override;
 
   virtual double CurValue() const override;
   virtual double MinValue() const override;
   virtual double MaxValue() const override;
   virtual double Step() const override;
 
   /**
    * Allow the platform to store a pointers worth of data on us.
@@ -187,20 +189,27 @@ class RemoteAccessibleBase : public Acce
   /**
    * Return the document containing this proxy, or the proxy itself if it is a
    * document.
    */
   DocAccessibleParent* Document() const { return mDoc; }
 
   DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
 
-  void ApplyCache(uint8_t aUpdateType, AccAttributes* aFields) {
-    if (aUpdateType == 0 || !mCachedFields) {
+  void ApplyCache(CacheUpdateType aUpdateType, AccAttributes* aFields) {
+    if (aUpdateType == CacheUpdateType::Initial) {
       mCachedFields = aFields;
     } else {
+      if (!mCachedFields) {
+        // The fields cache can be uninitialized if there were no cache-worthy
+        // fields in the initial cache push.
+        // We don't do a simple assign because we don't want to store the
+        // DeleteEntry entries.
+        mCachedFields = new AccAttributes();
+      }
       mCachedFields->Update(aFields);
     }
   }
 
  protected:
   RemoteAccessibleBase(uint64_t aID, Derived* aParent,
                        DocAccessibleParent* aDoc, role aRole, AccType aType,
                        AccGenericType aGenericTypes, uint8_t aRoleMapEntryIndex)
--- a/accessible/ipc/RemoteAccessibleShared.h
+++ b/accessible/ipc/RemoteAccessibleShared.h
@@ -36,17 +36,17 @@ void Value(nsString& aValue) const;
 /*
  * Set aHelp to the help string of the proxied accessible.
  */
 void Help(nsString& aHelp) const;
 
 /**
  * Set aDesc to the description of the proxied accessible.
  */
-void Description(nsString& aDesc) const;
+void Description(nsString& aDesc) const override;
 
 /**
  * Get the set of attributes on the proxied accessible.
  */
 void Attributes(RefPtr<AccAttributes>* aAttributes) const;
 
 /**
  * Return set of targets of given relation type.
--- a/accessible/ipc/other/PDocAccessible.ipdl
+++ b/accessible/ipc/other/PDocAccessible.ipdl
@@ -12,16 +12,17 @@ include DocAccessibleTypes;
 
 include "mozilla/GfxMessageUtils.h";
 
 using nsIntRect from "nsRect.h";
 using mozilla::a11y::role from "mozilla/a11y/IPCTypes.h";
 using mozilla::a11y::AccType from "mozilla/a11y/IPCTypes.h";
 using mozilla::a11y::AccGenericType from "mozilla/a11y/IPCTypes.h";
 [RefCounted] using mozilla::a11y::AccAttributes from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::CacheUpdateType from "mozilla/a11y/IPCTypes.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
 
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
@@ -121,22 +122,20 @@ parent:
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
   // Android
   async Batch(uint64_t aBatchType, BatchData[] aData);
 
   /*
    * Cache The World
-   * XXX: aUpdateType should be an enum, for now it is
-   * 0 for initial push, and 1 for partial update.
    * aFinal is false when paginating cache into several pushes,
    * and then true on the final push.
    */
-  async Cache(uint8_t aUpdateType, CacheData[] aData, bool aFinal);
+  async Cache(CacheUpdateType aUpdateType, CacheData[] aData, bool aFinal);
 
 child:
   /*
    * Notify the content process that the PDocAccessible has finished being
    * constructed in the parent process.
    */
   async ConstructedInParentProcess();
 
--- a/accessible/ipc/other/RemoteAccessible.cpp
+++ b/accessible/ipc/other/RemoteAccessible.cpp
@@ -45,16 +45,21 @@ void RemoteAccessible::Value(nsString& a
   Unused << mDoc->SendValue(mID, &aValue);
 }
 
 void RemoteAccessible::Help(nsString& aHelp) const {
   Unused << mDoc->SendHelp(mID, &aHelp);
 }
 
 void RemoteAccessible::Description(nsString& aDesc) const {
+  if (mCachedFields) {
+    RemoteAccessibleBase<RemoteAccessible>::Description(aDesc);
+    return;
+  }
+
   Unused << mDoc->SendDescription(mID, &aDesc);
 }
 
 void RemoteAccessible::Attributes(RefPtr<AccAttributes>* aAttributes) const {
   Unused << mDoc->SendAttributes(mID, aAttributes);
 }
 
 nsTArray<RemoteAccessible*> RemoteAccessible::RelationByType(
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -11,16 +11,17 @@ include DocAccessibleTypes;
 
 include "mozilla/GfxMessageUtils.h";
 
 using mozilla::a11y::role from "mozilla/a11y/IPCTypes.h";
 using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h";
 using mozilla::a11y::IDispatchHolder from "mozilla/a11y/IPCTypes.h";
 using mozilla::a11y::AccType from "mozilla/a11y/IPCTypes.h";
 using mozilla::a11y::AccGenericType from "mozilla/a11y/IPCTypes.h";
+using mozilla::a11y::CacheUpdateType from "mozilla/a11y/IPCTypes.h";
 using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
 using mozilla::LayoutDeviceIntRect from "Units.h";
 
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
@@ -79,22 +80,20 @@ parent:
   /*
    * Tell the parent document to bind the existing document as a new child
    * document.
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
   /*
    * Cache The World
-   * XXX: aUpdateType should be an enum, for now it is
-   * 0 for initial push, and 1 for partial update.
    * aFinal is false when paginating cache into several pushes,
    * and then true on the final push.
    */
-  async Cache(uint8_t aUpdateType, CacheData[] aData, bool aFinal);
+  async Cache(CacheUpdateType aUpdateType, CacheData[] aData, bool aFinal);
 
 child:
   /**
    * We use IDispatchHolder instead of IAccessibleHolder for the following two
    * methods because of sandboxing. IAccessible::get_accParent returns the parent
    * as an IDispatch. COM is not smart enough to understand that IAccessible is
    * derived from IDispatch, so during marshaling it attempts to QueryInterface
    * on the parent's proxy for IDispatch. This will fail with E_ACCESSDENIED
--- a/accessible/ipc/win/RemoteAccessible.cpp
+++ b/accessible/ipc/win/RemoteAccessible.cpp
@@ -175,16 +175,20 @@ double RemoteAccessible::Step() const {
   if (FAILED(hr)) {
     return 0;
   }
 
   return increment;
 }
 
 void RemoteAccessible::Description(nsString& aDesc) const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::Description(aDesc);
+  }
+
   aDesc.Truncate();
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return;
   }
 
   BSTR result;
   HRESULT hr = acc->get_accDescription(kChildIdSelf, &result);
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -624,21 +624,17 @@ struct RoleDescrComparator {
 }
 
 - (NSString*)moxHelp {
   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
 
   // What needs to go here is actually the accDescription of an item.
   // The MSAA acc_help method has nothing to do with this one.
   nsAutoString helpText;
-  if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) {
-    acc->Description(helpText);
-  } else {
-    mGeckoAccessible->AsRemote()->Description(helpText);
-  }
+  mGeckoAccessible->Description(helpText);
 
   return nsCocoaUtils::ToNSString(helpText);
 
   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
 }
 
 - (NSWindow*)moxWindow {
   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
--- a/accessible/tests/browser/e10s/browser_caching_description.js
+++ b/accessible/tests/browser/e10s/browser_caching_description.js
@@ -38,17 +38,17 @@ const tests = [
       "No description from @aria-describedby since it is the same as the " +
       "@alt attribute which is used as the name",
     attrs: [
       {
         attr: "alt",
         value: "aria description",
       },
     ],
-    waitFor: [[EVENT_REORDER, matchContentDoc]],
+    waitFor: [[EVENT_NAME_CHANGE, "image"]],
     expected: "",
   },
   {
     desc:
       "Description from @aria-describedby attribute when @alt and " +
       "@aria-describedby are not the same",
     attrs: [
       {
@@ -67,17 +67,17 @@ const tests = [
       {
         attr: "alt",
       },
       {
         attr: "title",
         value: "title",
       },
     ],
-    waitFor: [[EVENT_REORDER, matchContentDoc]],
+    waitFor: [[EVENT_NAME_CHANGE, "image"]],
     expected: "another description",
   },
   {
     desc:
       "No description from @aria-describedby since it is the same as the " +
       "@title attribute which is used as the name",
     attrs: [
       {
@@ -103,17 +103,17 @@ const tests = [
       "Description from @title attribute when @alt and @atitle are not the " +
       "same",
     attrs: [
       {
         attr: "alt",
         value: "aria description",
       },
     ],
-    waitFor: [[EVENT_REORDER, matchContentDoc]],
+    waitFor: [[EVENT_NAME_CHANGE, "image"]],
     expected: "another description",
   },
   {
     desc:
       "No description from @title since it is the same as the @alt " +
       "attribute which is used as the name",
     attrs: [
       {
@@ -181,17 +181,17 @@ const tests = [
 
 /**
  * Test caching of accessible object description
  */
 addAccessibleTask(
   `
   <p id="description">aria description</p>
   <p id="description2">another description</p>
-  <img id="image" />`,
+  <img id="image" src="http://example.com/a11y/accessible/tests/mochitest/moz.png" />`,
   async function(browser, accDoc) {
     let imgAcc = findAccessibleChildByID(accDoc, "image");
 
     for (let { desc, waitFor, attrs, expected } of tests) {
       info(desc);
       let onUpdate;
       if (waitFor) {
         onUpdate = waitForOrderedEvents(waitFor);
--- a/accessible/tests/mochitest/layout.js
+++ b/accessible/tests/mochitest/layout.js
@@ -273,39 +273,40 @@ function getBounds(aID, aDPR = window.de
     height = {};
   let xInCSS = {},
     yInCSS = {},
     widthInCSS = {},
     heightInCSS = {};
   accessible.getBounds(x, y, width, height);
   accessible.getBoundsInCSSPixels(xInCSS, yInCSS, widthInCSS, heightInCSS);
 
+  info(`DPR is: ${aDPR}`);
   isWithin(
     x.value / aDPR,
     xInCSS.value,
     1,
-    "Heights in CSS pixels is calculated correctly"
+    "X in CSS pixels is calculated correctly"
   );
   isWithin(
     y.value / aDPR,
     yInCSS.value,
     1,
-    "Heights in CSS pixels is calculated correctly"
+    "Y in CSS pixels is calculated correctly"
   );
   isWithin(
     width.value / aDPR,
     widthInCSS.value,
     1,
-    "Heights in CSS pixels is calculated correctly"
+    "Width in CSS pixels is calculated correctly"
   );
   isWithin(
     height.value / aDPR,
     heightInCSS.value,
     1,
-    "Heights in CSS pixels is calculated correctly"
+    "Height in CSS pixels is calculated correctly"
   );
 
   return [x.value, y.value, width.value, height.value];
 }
 
 function getRangeExtents(aID, aStartOffset, aEndOffset, aCoordOrigin) {
   var hyperText = getAccessible(aID, [nsIAccessibleText]);
   var x = {},
--- a/accessible/tests/mochitest/tree/test_txtctrl.xhtml
+++ b/accessible/tests/mochitest/tree/test_txtctrl.xhtml
@@ -41,16 +41,17 @@
            { GROUPING: [
              { ENTRY: [ { TEXT_LEAF: [] } ] },
            ] };
       } else {
          accTree =
            { GROUPING: [
              { ENTRY: [ { TEXT_LEAF: [] } ] },
              { PUSHBUTTON: [] },
+             { PUSHBUTTON: [] },
            ] };
       }
 
       testAccessibleTree("txc_search_searchbutton", accTree);
 
       SimpleTest.finish();
     }
 
--- a/accessible/windows/msaa/MsaaAccessible.cpp
+++ b/accessible/windows/msaa/MsaaAccessible.cpp
@@ -1025,22 +1025,19 @@ MsaaAccessible::get_accDescription(VARIA
   HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
   if (FAILED(hr)) {
     return hr;
   }
 
   if (accessible) {
     return accessible->get_accDescription(kVarChildIdSelf, pszDescription);
   }
-  if (mAcc->IsRemote()) {
-    return E_NOTIMPL;  // XXX Not supported for RemoteAccessible yet.
-  }
 
   nsAutoString description;
-  LocalAcc()->Description(description);
+  Acc()->Description(description);
 
   *pszDescription =
       ::SysAllocStringLen(description.get(), description.Length());
   return *pszDescription ? S_OK : E_OUTOFMEMORY;
 }
 
 STDMETHODIMP
 MsaaAccessible::get_accRole(
--- a/accessible/xpcom/xpcAccessible.cpp
+++ b/accessible/xpcom/xpcAccessible.cpp
@@ -251,21 +251,17 @@ xpcAccessible::GetName(nsAString& aName)
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessible::GetDescription(nsAString& aDescription) {
   if (!IntlGeneric()) return NS_ERROR_FAILURE;
 
   nsAutoString desc;
-  if (RemoteAccessible* proxy = IntlGeneric()->AsRemote()) {
-    proxy->Description(desc);
-  } else {
-    Intl()->Description(desc);
-  }
+  IntlGeneric()->Description(desc);
 
   aDescription.Assign(desc);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessible::GetLanguage(nsAString& aLanguage) {
--- a/accessible/xul/XULComboboxAccessible.cpp
+++ b/accessible/xul/XULComboboxAccessible.cpp
@@ -49,17 +49,17 @@ uint64_t XULComboboxAccessible::NativeSt
     } else {
       state |= states::COLLAPSED;
     }
   }
 
   return state | states::HASPOPUP;
 }
 
-void XULComboboxAccessible::Description(nsString& aDescription) {
+void XULComboboxAccessible::Description(nsString& aDescription) const {
   aDescription.Truncate();
   // Use description of currently focused option
   nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
   if (!menuListElm) return;
 
   nsCOMPtr<dom::Element> focusedOptionItem;
   menuListElm->GetSelectedItem(getter_AddRefs(focusedOptionItem));
   if (focusedOptionItem && mDoc) {
--- a/accessible/xul/XULComboboxAccessible.h
+++ b/accessible/xul/XULComboboxAccessible.h
@@ -16,17 +16,17 @@ namespace a11y {
  */
 class XULComboboxAccessible : public AccessibleWrap {
  public:
   enum { eAction_Click = 0 };
 
   XULComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // LocalAccessible
-  virtual void Description(nsString& aDescription) override;
+  virtual void Description(nsString& aDescription) const override;
   virtual void Value(nsString& aValue) const override;
   virtual a11y::role NativeRole() const override;
   virtual uint64_t NativeState() const override;
 
   // ActionAccessible
   virtual uint8_t ActionCount() const override;
   virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
   virtual bool DoAction(uint8_t aIndex) const override;
--- a/accessible/xul/XULListboxAccessible.cpp
+++ b/accessible/xul/XULListboxAccessible.cpp
@@ -433,17 +433,17 @@ LocalAccessible* XULListitemAccessible::
   if (!listElement) return nullptr;
 
   return mDoc->GetAccessible(listElement);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULListitemAccessible LocalAccessible
 
-void XULListitemAccessible::Description(nsString& aDesc) {
+void XULListitemAccessible::Description(nsString& aDesc) const {
   AccessibleWrap::Description(aDesc);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // XULListitemAccessible: LocalAccessible
 
 /**
  * Get the name from GetXULName.
--- a/accessible/xul/XULListboxAccessible.h
+++ b/accessible/xul/XULListboxAccessible.h
@@ -101,17 +101,17 @@ class XULListitemAccessible : public XUL
   enum { eAction_Click = 0 };
 
   NS_INLINE_DECL_REFCOUNTING_INHERITED(XULListitemAccessible,
                                        XULMenuitemAccessible)
 
   XULListitemAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // LocalAccessible
-  virtual void Description(nsString& aDesc) override;
+  virtual void Description(nsString& aDesc) const override;
   virtual a11y::role NativeRole() const override;
   virtual uint64_t NativeState() const override;
   virtual uint64_t NativeInteractiveState() const override;
 
   // Actions
   virtual void ActionNameAt(uint8_t index, nsAString& aName) override;
 
   // Widgets
--- a/accessible/xul/XULMenuAccessible.cpp
+++ b/accessible/xul/XULMenuAccessible.cpp
@@ -124,17 +124,17 @@ uint64_t XULMenuitemAccessible::NativeIn
   return states::FOCUSABLE | states::SELECTABLE;
 }
 
 ENameValueFlag XULMenuitemAccessible::NativeName(nsString& aName) const {
   mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label, aName);
   return eNameOK;
 }
 
-void XULMenuitemAccessible::Description(nsString& aDescription) {
+void XULMenuitemAccessible::Description(nsString& aDescription) const {
   mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::description,
                                  aDescription);
 }
 
 KeyBinding XULMenuitemAccessible::AccessKey() const {
   // Return menu accesskey: N or Alt+F.
   static int32_t gMenuAccesskeyModifier =
       -1;  // magic value of -1 indicates unitialized state
--- a/accessible/xul/XULMenuAccessible.h
+++ b/accessible/xul/XULMenuAccessible.h
@@ -17,17 +17,17 @@ namespace a11y {
  */
 class XULMenuitemAccessible : public AccessibleWrap {
  public:
   enum { eAction_Click = 0 };
 
   XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // LocalAccessible
-  virtual void Description(nsString& aDescription) override;
+  virtual void Description(nsString& aDescription) const override;
   virtual a11y::role NativeRole() const override;
   virtual uint64_t NativeState() const override;
   virtual uint64_t NativeInteractiveState() const override;
   virtual int32_t GetLevelInternal() override;
 
   // ActionAccessible
   virtual uint8_t ActionCount() const override;
   virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
--- a/browser/actors/AboutNewTabChild.jsm
+++ b/browser/actors/AboutNewTabChild.jsm
@@ -78,13 +78,13 @@ class AboutNewTabChild extends JSWindowA
         (!contentWindowPrivate ||
           (contentWindowPrivate &&
             PrivateBrowsingUtils.permanentPrivateBrowsing))
       ) {
         this.sendAsyncMessage("AboutNewTabVisible");
 
         // Note: newtab feature info is currently being loaded in PrefsFeed.jsm,
         // But we're recording exposure events here.
-        NimbusFeatures.newtab.recordExposureEvent();
+        NimbusFeatures.newtab.recordExposureEvent({ once: true });
       }
     }
   }
 }
--- a/browser/actors/AboutPocketParent.jsm
+++ b/browser/actors/AboutPocketParent.jsm
@@ -42,26 +42,26 @@ class AboutPocketParent extends JSWindow
       }
       case "PKT_close": {
         this.browsingContext.topChromeWindow?.pktUI.closePanel();
         break;
       }
       case "PKT_openTabWithUrl": {
         this.browsingContext.topChromeWindow?.pktUI.onOpenTabWithUrl(
           message.data,
-          this.browsingContext.embedderElement.contentDocument.nodePrincipal,
-          this.browsingContext.embedderElement.contentDocument.csp
+          this.browsingContext.embedderElement.contentPrincipal,
+          this.browsingContext.embedderElement.csp
         );
         break;
       }
       case "PKT_openTabWithPocketUrl": {
         this.browsingContext.topChromeWindow?.pktUI.onOpenTabWithPocketUrl(
           message.data,
-          this.browsingContext.embedderElement.contentDocument.nodePrincipal,
-          this.browsingContext.embedderElement.contentDocument.csp
+          this.browsingContext.embedderElement.contentPrincipal,
+          this.browsingContext.embedderElement.csp
         );
         break;
       }
       case "PKT_resizePanel": {
         this.browsingContext.topChromeWindow?.pktUI.resizePanel(message.data);
         this.sendResponseMessageToPanel("PKT_resizePanel");
         break;
       }
--- a/browser/actors/AboutPrivateBrowsingChild.jsm
+++ b/browser/actors/AboutPrivateBrowsingChild.jsm
@@ -42,19 +42,18 @@ class AboutPrivateBrowsingChild extends 
       featureId: "privatebrowsing",
     });
     if (experiment) {
       Services.telemetry.recordEvent("aboutprivatebrowsing", "click", source);
     }
   }
 
   PrivateBrowsingFeatureConfig() {
-    const config = NimbusFeatures.privatebrowsing.getAllVariables({
-      sendExposureEvent: true,
-    });
+    const config = NimbusFeatures.privatebrowsing.getAllVariables();
+    NimbusFeatures.privatebrowsing.recordExposureEvent();
 
     // Format urls if any are defined
     ["infoLinkUrl", "promoLinkUrl"].forEach(key => {
       if (config[key]) {
         config[key] = Services.urlFormatter.formatURL(config[key]);
       }
     });
 
--- a/browser/actors/AboutPrivateBrowsingParent.jsm
+++ b/browser/actors/AboutPrivateBrowsingParent.jsm
@@ -160,16 +160,17 @@ class AboutPrivateBrowsingParent extends
         Services.prefs.setIntPref(SHOWN_PREF, MAX_SEARCH_BANNER_SHOW_COUNT);
         break;
       }
       case "ShouldShowVPNPromo": {
         const homeRegion = Region.home || "";
         const currentRegion = Region.current || "";
         return (
           homeRegion.toLowerCase() !== "cn" &&
-          currentRegion.toLowerCase() !== "cn"
+          currentRegion.toLowerCase() !== "cn" &&
+          Services.policies.status !== Services.policies.ACTIVE
         );
       }
     }
 
     return undefined;
   }
 }
--- a/browser/actors/AboutProtectionsParent.jsm
+++ b/browser/actors/AboutProtectionsParent.jsm
@@ -385,30 +385,24 @@ class AboutProtectionsParent extends JSW
           origin: "about-protections",
         });
         break;
       case "OpenSyncPreferences":
         win.openTrustedLinkIn("about:preferences#sync", "tab");
         break;
       case "FetchContentBlockingEvents":
         let dataToSend = {};
-        let weekdays = Services.intl.getDisplayNames(undefined, {
-          style: "short",
-          keys: [
-            "dates/gregorian/weekdays/sunday",
-            "dates/gregorian/weekdays/monday",
-            "dates/gregorian/weekdays/tuesday",
-            "dates/gregorian/weekdays/wednesday",
-            "dates/gregorian/weekdays/thursday",
-            "dates/gregorian/weekdays/friday",
-            "dates/gregorian/weekdays/saturday",
-            "dates/gregorian/weekdays/sunday",
-          ],
+        let displayNames = new Services.intl.DisplayNames(undefined, {
+          type: "weekday",
+          style: "abbreviated",
+          calendar: "gregory",
         });
-        weekdays = Object.values(weekdays.values);
+
+        // Weekdays starting Sunday (7) to Saturday (6).
+        let weekdays = [7, 1, 2, 3, 4, 5, 6].map(day => displayNames.of(day));
         dataToSend.weekdays = weekdays;
 
         if (PrivateBrowsingUtils.isWindowPrivate(win)) {
           dataToSend.isPrivate = true;
           return dataToSend;
         }
         let sumEvents = await TrackingDBService.sumAllEvents();
         let earliestDate = await TrackingDBService.getEarliestRecordedDate();
--- a/browser/actors/ContextMenuChild.jsm
+++ b/browser/actors/ContextMenuChild.jsm
@@ -773,31 +773,24 @@ class ContextMenuChild extends JSWindowA
     context.screenX = aEvent.screenX;
     context.screenY = aEvent.screenY;
     context.mozInputSource = aEvent.mozInputSource;
 
     let node = aEvent.composedTarget;
 
     // Set the node to containing <video>/<audio>/<embed>/<object> if the node
     // is in the videocontrols UA Widget.
-    if (this.contentWindow.ShadowRoot) {
-      let n = node;
-      while (n) {
-        if (n instanceof this.contentWindow.ShadowRoot) {
-          if (
-            n.host instanceof this.contentWindow.HTMLMediaElement ||
-            n.host instanceof this.contentWindow.HTMLEmbedElement ||
-            n.host instanceof this.contentWindow.HTMLObjectElement
-          ) {
-            node = n.host;
-            break;
-          }
-          break;
-        }
-        n = n.parentNode;
+    if (node.containingShadowRoot?.isUAWidget()) {
+      const host = node.containingShadowRoot.host;
+      if (
+        host instanceof this.contentWindow.HTMLMediaElement ||
+        host instanceof this.contentWindow.HTMLEmbedElement ||
+        host instanceof this.contentWindow.HTMLObjectElement
+      ) {
+        node = host;
       }
     }
 
     const XUL_NS =
       "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
     context.shouldDisplay = true;
 
@@ -1160,17 +1153,17 @@ class ContextMenuChild extends JSWindowA
 
           if (bgImgUrl) {
             context.hasBGImage = true;
             context.bgImageURL = this._makeURLAbsolute(elem.baseURI, bgImgUrl);
           }
         }
       }
 
-      elem = elem.parentNode;
+      elem = elem.flattenedTreeParentNode;
     }
 
     // See if the user clicked in a frame.
     const docDefaultView = context.target.ownerGlobal;
 
     if (docDefaultView != docDefaultView.top) {
       context.inFrame = true;
 
--- a/browser/actors/WebRTCParent.jsm
+++ b/browser/actors/WebRTCParent.jsm
@@ -865,16 +865,21 @@ function prompt(aActor, aBrowser, aReque
               let item = addDeviceToList(
                 menupopup,
                 stringBundle.getString(sawcStringId),
                 i,
                 type
               );
               item.deviceId = device.id;
               item.mediaSource = type;
+
+              // In this case the OS sharing dialog will be the only option and
+              // can be safely pre-selected.
+              menupopup.parentNode.selectedItem = item;
+              menupopup.parentNode.disabled = true;
               break;
             }
             if (type == "application") {
               // The application names returned by the platform are of the form:
               // <window count>\x1e<application name>
               let sepIndex = name.indexOf("\x1e");
               let count = name.slice(0, sepIndex);
               let sawcStringId =
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -336,16 +336,24 @@ int main(int argc, char* argv[], char* e
     return result;
   }
 #endif
 
 #ifdef HAS_DLL_BLOCKLIST
   DllBlocklist_Initialize(gBlocklistInitFlags);
 #endif
 
+// We will likely only ever support this as a command line argument on Windows
+// and OSX, so we're ifdefing here just to not create any expectations.
+#if defined(XP_WIN) || defined(XP_MACOSX)
+  if (argc > 1 && IsArg(argv[1], "silentmode")) {
+    ::putenv(const_cast<char*>("MOZ_APP_SILENT_START=1"));
+  }
+#endif
+
 #if defined(XP_WIN)
 
   // Ideally, we would be able to set our DPI awareness in firefox.exe.manifest
   // Unfortunately, that would cause Win32k calls when user32.dll gets loaded,
   // which would be incompatible with Win32k Lockdown
   //
   // MSDN says that it's allowed-but-not-recommended to initialize DPI
   // programatically, as long as it's done before any HWNDs are created.
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -187,16 +187,25 @@ pref("app.update.langpack.enabled", true
   pref("app.update.background.experimental", true);
 #else
   pref("app.update.background.scheduling.enabled", false);
 #endif
   // By default, check for updates when the browser is not running every 7 hours.
   pref("app.update.background.interval", 25200);
 #endif
 
+#ifdef XP_MACOSX
+  // If set to true, Firefox will automatically restart if it is left running
+  // with no browser windows open.
+  pref("app.update.noWindowAutoRestart.enabled", true);
+  // How long to wait after all browser windows are closed before restarting,
+  // in milliseconds. 5 min = 300000 ms
+  pref("app.update.noWindowAutoRestart.delayMs", 300000);
+#endif
+
 #if defined(MOZ_BACKGROUNDTASKS)
   // The amount of time, in seconds, before background tasks time out and exit.
   // Tasks can override this default (10 minutes).
   pref("toolkit.backgroundtasks.defaultTimeoutSec", 600);
 #endif
 
 // Symmetric (can be overridden by individual extensions) update preferences.
 // e.g.
@@ -311,16 +320,23 @@ pref("browser.startup.upgradeDialog.enab
 // Don't create the hidden window during startup on
 // platforms that don't always need it (Win/Linux).
 pref("toolkit.lazyHiddenWindow", true);
 
 pref("browser.chrome.site_icons", true);
 // browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
 pref("browser.warnOnQuit", true);
 
+// Whether to warn when quitting when using the shortcut key.
+#if defined(XP_WIN)
+  pref("browser.warnOnQuitShortcut", false);
+#else
+  pref("browser.warnOnQuitShortcut", true);
+#endif
+
 // TODO bug 1702563: Renable fullscreen autohide by default on macOS.
 #ifdef XP_MACOSX
   pref("browser.fullscreen.autohide", false);
 #else
   pref("browser.fullscreen.autohide", true);
 #endif
 
 pref("browser.overlink-delay", 80);
@@ -359,16 +375,17 @@ pref("browser.urlbar.maxHistoricalSearch
 // of the match filters with each additional filter adding more results (union).
 pref("browser.urlbar.suggest.bookmark",             true);
 pref("browser.urlbar.suggest.history",              true);
 pref("browser.urlbar.suggest.openpage",             true);
 pref("browser.urlbar.suggest.searches",             true);
 pref("browser.urlbar.suggest.topsites",             true);
 pref("browser.urlbar.suggest.engines",              true);
 pref("browser.urlbar.suggest.quicksuggest",         false);
+pref("browser.urlbar.suggest.quicksuggest.sponsored", false);
 pref("browser.urlbar.suggest.calculator",           false);
 
 // Whether the QuickSuggest experiment is enabled.
 pref("browser.urlbar.quicksuggest.enabled", false);
 
 // Whether to show the QuickSuggest onboarding dialog.
 pref("browser.urlbar.quicksuggest.shouldShowOnboardingDialog", true);
 
@@ -378,16 +395,20 @@ pref("browser.urlbar.quicksuggest.showOn
 // The indexes of the sponsored and non-sponsored quick suggest results within
 // the general results group.
 pref("browser.urlbar.quicksuggest.sponsoredIndex", -1);
 pref("browser.urlbar.quicksuggest.nonSponsoredIndex", -1);
 
 // Whether Remote Settings is enabled as a quick suggest source.
 pref("browser.urlbar.quicksuggest.remoteSettings.enabled", true);
 
+// The Firefox Suggest scenario in which the user is enrolled, one of:
+// "history", "offline", "online"
+pref("browser.urlbar.quicksuggest.scenario", "history");
+
 // Whether unit conversion is enabled.
 #ifdef NIGHTLY_BUILD
 pref("browser.urlbar.unitConversion.enabled", true);
 #else
 pref("browser.urlbar.unitConversion.enabled", false);
 #endif
 
 // Whether to show search suggestions before general results like history and
@@ -1025,19 +1046,16 @@ pref("browser.sessionstore.upgradeBackup
 pref("browser.sessionstore.upgradeBackup.maxUpgradeBackups", 3);
 // End-users should not run sessionstore in debug mode
 pref("browser.sessionstore.debug", false);
 // Forget closed windows/tabs after two weeks
 pref("browser.sessionstore.cleanup.forget_closed_after", 1209600000);
 // Amount of failed SessionFile writes until we restart the worker.
 pref("browser.sessionstore.max_write_failures", 5);
 
-// Whether to warn the user when quitting, even though their tabs will be restored.
-pref("browser.sessionstore.warnOnQuit", false);
-
 // Don't quit the browser when Ctrl + Q is pressed.
 pref("browser.quitShortcut.disabled", false);
 
 // allow META refresh by default
 pref("accessibility.blockautorefresh", false);
 
 // Whether history is enabled or not.
 pref("places.history.enabled", true);
@@ -1311,17 +1329,16 @@ pref("services.sync.prefs.sync.browser.n
 pref("services.sync.prefs.sync.browser.newtabpage.pinned", true);
 pref("services.sync.prefs.sync.browser.offline-apps.notify", true);
 pref("services.sync.prefs.sync.browser.safebrowsing.downloads.enabled", true);
 pref("services.sync.prefs.sync.browser.safebrowsing.downloads.remote.block_potentially_unwanted", true);
 pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true);
 pref("services.sync.prefs.sync.browser.safebrowsing.phishing.enabled", true);
 pref("services.sync.prefs.sync.browser.search.update", true);
 pref("services.sync.prefs.sync.browser.search.widget.inNavBar", true);
-pref("services.sync.prefs.sync.browser.sessionstore.warnOnQuit", true);
 pref("services.sync.prefs.sync.browser.startup.homepage", true);
 pref("services.sync.prefs.sync.browser.startup.page", true);
 pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true);
 pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
 pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
 pref("services.sync.prefs.sync.browser.taskbar.previews.enable", true);
 pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true);
 pref("services.sync.prefs.sync.browser.urlbar.showSearchSuggestionsFirst", true);
@@ -1425,22 +1442,16 @@ pref("prompts.contentPromptSubDialog", t
 
 // Whether to show window-modal dialogs opened for browser windows
 // in a SubDialog inside their parent, instead of an OS level window.
 pref("prompts.windowPromptSubDialog", true);
 
 // Activates preloading of the new tab url.
 pref("browser.newtab.preload", true);
 
-// Preference to enable the entire new newtab experience at once.
-pref("browser.newtabpage.activity-stream.newNewtabExperience.enabled", true);
-
-// A preference which allows us to enable the fly out customization overlay
-// on the newtab page.
-pref("browser.newtabpage.activity-stream.customizationMenu.enabled", false);
 pref("browser.newtabpage.activity-stream.newNewtabExperience.colors", "#0090ED,#FF4F5F,#2AC3A2,#FF7139,#A172FF,#FFA437,#FF2A8A");
 
 // Activity Stream prefs that control to which page to redirect
 #ifndef RELEASE_OR_BETA
   pref("browser.newtabpage.activity-stream.debug", false);
 #endif
 
 // The remote FxA root content URL for the Activity Stream firstrun page.
@@ -1452,17 +1463,17 @@ pref("browser.newtabpage.activity-stream
 // ASRouter provider configuration
 pref("browser.newtabpage.activity-stream.asrouter.providers.cfr", "{\"id\":\"cfr\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"cfr\",\"updateCycleInMs\":3600000}");
 pref("browser.newtabpage.activity-stream.asrouter.providers.whats-new-panel", "{\"id\":\"whats-new-panel\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"whats-new-panel\",\"updateCycleInMs\":3600000}");
 pref("browser.newtabpage.activity-stream.asrouter.providers.message-groups", "{\"id\":\"message-groups\",\"enabled\":true,\"type\":\"remote-settings\",\"bucket\":\"message-groups\",\"updateCycleInMs\":3600000}");
 // This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
 // this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
 // repackager of this code using an alternate snippet url, please keep your users safe
 pref("browser.newtabpage.activity-stream.asrouter.providers.snippets", "{\"id\":\"snippets\",\"enabled\":false,\"type\":\"remote\",\"url\":\"https://snippets.cdn.mozilla.net/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/\",\"updateCycleInMs\":14400000}");
-pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "{\"id\":\"messaging-experiments\",\"enabled\":true,\"type\":\"remote-experiments\",\"messageGroups\":[\"cfr\",\"whats-new-panel\",\"moments-page\",\"aboutwelcome\",\"infobar\"],\"updateCycleInMs\":3600000}");
+pref("browser.newtabpage.activity-stream.asrouter.providers.messaging-experiments", "{\"id\":\"messaging-experiments\",\"enabled\":true,\"type\":\"remote-experiments\",\"messageGroups\":[\"cfr\",\"whats-new-panel\",\"moments-page\",\"aboutwelcome\",\"infobar\",\"spotlight\"],\"updateCycleInMs\":3600000}");
 
 // ASRouter user prefs
 pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons", true);
 pref("browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features", true);
 
 // The pref that controls if ASRouter uses the remote fluent files.
 // It's enabled by default, but could be disabled to force ASRouter to use the local files.
 pref("browser.newtabpage.activity-stream.asrouter.useRemoteL10n", true);
@@ -1762,32 +1773,35 @@ pref("browser.contentblocking.state-part
 //     "cm": cryptomining blocking enabled
 //     "-cm": cryptomining blocking disabled
 //   Social Tracking Protection:
 //     "stp": social tracking protection enabled
 //     "-stp": social tracking protection disabled
 //   Level 2 Tracking list:
 //     "lvl2": Level 2 tracking list enabled
 //     "-lvl2": Level 2 tracking list disabled
+//   Restrict relaxing default referrer policy:
+//     "rp": Restrict relaxing default referrer policy enabled
+//     "-rp": Restrict relaxing default referrer policy disabled
 //   Cookie behavior:
 //     "cookieBehavior0": cookie behaviour BEHAVIOR_ACCEPT
 //     "cookieBehavior1": cookie behaviour BEHAVIOR_REJECT_FOREIGN
 //     "cookieBehavior2": cookie behaviour BEHAVIOR_REJECT
 //     "cookieBehavior3": cookie behaviour BEHAVIOR_LIMIT_FOREIGN
 //     "cookieBehavior4": cookie behaviour BEHAVIOR_REJECT_TRACKER
 //     "cookieBehavior5": cookie behaviour BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
 //   Cookie behavior for private windows:
 //     "cookieBehaviorPBM0": cookie behaviour BEHAVIOR_ACCEPT
 //     "cookieBehaviorPBM1": cookie behaviour BEHAVIOR_REJECT_FOREIGN
 //     "cookieBehaviorPBM2": cookie behaviour BEHAVIOR_REJECT
 //     "cookieBehaviorPBM3": cookie behaviour BEHAVIOR_LIMIT_FOREIGN
 //     "cookieBehaviorPBM4": cookie behaviour BEHAVIOR_REJECT_TRACKER
 //     "cookieBehaviorPBM5": cookie behaviour BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
 // One value from each section must be included in the browser.contentblocking.features.strict pref.
-pref("browser.contentblocking.features.strict", "tp,tpPrivate,cookieBehavior5,cookieBehaviorPBM5,cm,fp,stp,lvl2");
+pref("browser.contentblocking.features.strict", "tp,tpPrivate,cookieBehavior5,cookieBehaviorPBM5,cm,fp,stp,lvl2,rp");
 
 // Hide the "Change Block List" link for trackers/tracking content in the custom
 // Content Blocking/ETP panel. By default, it will not be visible. There is also
 // an UI migration in place to set this pref to true if a user has a custom block
 // lists enabled.
 pref("browser.contentblocking.customBlockList.preferences.ui.enabled", false);
 
 pref("browser.contentblocking.reportBreakage.url", "https://tracking-protection-issues.herokuapp.com/new");
--- a/browser/base/content/appmenu-viewcache.inc.xhtml
+++ b/browser/base/content/appmenu-viewcache.inc.xhtml
@@ -61,39 +61,39 @@
       <toolbarseparator/>
       <toolbarbutton id="appMenu-bookmarks-button"
                      class="subviewbutton subviewbutton-nav"
                      data-l10n-id="library-bookmarks-menu"
                      closemenu="none"
                      oncommand="BookmarkingUI.showSubView(this);"/>
       <toolbarbutton id="appMenu-history-button"
                      class="subviewbutton subviewbutton-nav"
-                     label="&historyMenu.label;"
+                     data-l10n-id="appmenuitem-history"
                      closemenu="none"
                      oncommand="PanelUI.showSubView('PanelUI-history', this)"/>
       <toolbarbutton id="appMenu-downloads-button"
                      class="subviewbutton"
-                     label="&libraryDownloads.label;"
+                     data-l10n-id="appmenuitem-downloads"
                      key="key_openDownloads"
                      command="Tools:Downloads"/>
       <toolbarbutton id="appMenu-passwords-button"
                      class="subviewbutton"
                      data-l10n-id="appmenuitem-passwords"
                      oncommand="LoginHelper.openPasswordManager(window, { entryPoint: 'mainmenu' })"
                      />
       <toolbarbutton id="appMenu-extensions-themes-button"
                      class="subviewbutton"
                      data-l10n-id="appmenuitem-addons-and-themes"
                      key="key_openAddons"
                      command="Tools:Addons"
                      />
       <toolbarseparator/>
       <toolbarbutton id="appMenu-print-button2"
                      class="subviewbutton"
-                     label="&printCmd.label;"
+                     data-l10n-id="appmenuitem-print"
                      key="printKb"
 #ifdef XP_MACOSX
                      command="cmd_print"
 #else
                      command="cmd_printPreview"
 #endif
                      />
       <toolbarbutton id="appMenu-save-file-button2"
@@ -102,17 +102,17 @@
                      key="key_savePage"
                      command="Browser:SavePage"/>
       <toolbarbutton id="appMenu-find-button2"
                      class="subviewbutton"
                      data-l10n-id="appmenuitem-find-in-page"
                      key="key_find"
                      command="cmd_find"/>
       <toolbaritem id="appMenu-zoom-controls2" class="subviewbutton toolbaritem-combined-buttons" closemenu="none">
-        <label class="toolbarbutton-text" value="&fullZoom.label;"/>
+        <label class="toolbarbutton-text" data-l10n-id="appmenuitem-zoom"/>
         <toolbarbutton id="appMenu-zoomReduce-button2"
                        class="subviewbutton subviewbutton-iconic"
                        command="cmd_fullZoomReduce"
                        data-l10n-id="appmenuitem-zoom-reduce"
                        tooltip="dynamic-shortcut-tooltip"/>
         <toolbarbutton id="appMenu-zoomReset-button2"
                        class="subviewbutton"
                        command="cmd_fullZoomReset"
@@ -399,17 +399,20 @@
   <panelview id="PanelUI-developer-tools" flex="1" class="PanelUI-subview">
     <vbox id="PanelUI-developer-tools-view"
           class="panel-subview-body"
           role="group">
       <!-- Developer Tools menu items are inserted here -->
     </vbox>
   </panelview>
 
-  <panelview id="PanelUI-savetopocket" flex="1" class="PanelUI-subView">
+  <panelview id="PanelUI-savetopocket"
+             flex="1" class="PanelUI-subView"
+             remote="true"
+             closemenu="none">
     <vbox class="PanelUI-savetopocket-container">
     </vbox>
   </panelview>
 
   <panelview id="PanelUI-remotetabs" flex="1" class="PanelUI-subView"
              descriptionheightworkaround="true">
     <vbox class="panel-subview-body">
       <!-- this widget has 3 boxes in the body, but only 1 is ever visible -->
@@ -420,25 +423,25 @@
                          align="center"
                          class="subviewbutton"
                          oncommand="gSync.doSync();"
                          onmouseover="gSync.refreshSyncButtonsTooltip();"
                          closemenu="none">
             <hbox flex="1">
               <image class="syncNowBtn"/>
               <label class="syncnow-label"
-                     data-l10n-id="remote-tabs-sync-now"
-                     sync-now-data-l10n-id="remote-tabs-sync-now"
+                     data-l10n-id="appmenuitem-fxa-toolbar-sync-now2"
+                     sync-now-data-l10n-id="appmenuitem-fxa-toolbar-sync-now2"
                      syncing-data-l10n-id="fxa-toolbar-sync-syncing2"
                      crop="end"/>
             </hbox>
           </toolbarbutton>
           <toolbarbutton id="PanelUI-remotetabs-view-managedevices"
                          class="subviewbutton"
-                         data-l10n-id="remote-tabs-manage-account"
+                         data-l10n-id="appmenuitem-fxa-manage-account"
                          oncommand="gSync.openDevicesManagementPage('syncedtabs-menupanel');">
                          <observes element="sidebar-box" attribute="positionend"/>
           </toolbarbutton>
           <toolbarseparator id="PanelUI-remotetabs-separator"/>
         </vbox>
         <deck id="PanelUI-remotetabs-deck">
           <!-- Sync is ready to Sync and the "tabs" engine is enabled -->
           <vbox id="PanelUI-remotetabs-tabspane">
@@ -542,18 +545,17 @@
     <vbox id="PanelUI-fxa-menu" class="panel-subview-body">
       <toolbarbutton id="fxa-manage-account-button"
                      align="center"
                      class="subviewbutton"
                      oncommand="gSync.clickFxAMenuHeaderButton(this);">
         <vbox flex="1">
           <label id="fxa-menu-header-title"
                  crop="end"
-                 value="&fxa.menu.signin.label;"
-                 defaultLabel="&fxa.menu.signin.label;"/>
+                 data-l10n-id="appmenuitem-fxa-sign-in"/>
           <label id="fxa-menu-header-description"
                  crop="end"
                  data-l10n-id="fxa-menu-turn-on-sync"/>
         </vbox>
       </toolbarbutton>
       <toolbarbutton id="PanelUI-fxa-menu-syncnow-button"
                      align="center"
                      class="subviewbutton"
@@ -609,58 +611,58 @@
       </deck>
     </vbox>
   </panelview>
 
   <!-- This panelview is used to contain the dynamically created buttons for send tab to devices -->
   <panelview id="PanelUI-sendTabToDevice" flex="1" class="PanelUI-subView">
     <vbox class="panel-subview-body">
       <toolbarbutton id="PanelUI-sendTabToDevice-syncingDevices" class="subviewbutton subviewbutton-iconic pageAction-sendToDevice-notReady"
-                     label="&sendToDevice.syncNotReady.label;"
+                     data-l10n-id="fxa-menu-send-tab-to-device-syncnotready"
                      disabled="true"/>
     </vbox>
   </panelview>
 
   <panelview id="PanelUI-fxa-menu-sendtab-not-configured" flex="1" class="PanelUI-subView">
     <vbox id="PanelUI-fxa-sendtab-not-configured" align="center" class="panel-subview-body">
       <image class="fxaSendToDeviceLogo" role="presentation"/>
-      <label class="PanelUI-fxa-service-description-label">&fxa.service.sendTab.description;</label>
+      <label class="PanelUI-fxa-service-description-label" data-l10n-id="fxa-menu-send-tab-to-device-description"></label>
       <toolbarbutton id="PanelUI-fxa-menu-sendtab-not-configured-button"
                      class="PanelUI-fxa-signin-button"
-                     label="&fxa.menu.signin.label;"
+                     data-l10n-id="appmenuitem-fxa-sign-in"
                      oncommand="gSync.openPrefsFromFxaMenu('send_tab', this);"/>
     </vbox>
   </panelview>
 
   <panelview id="PanelUI-fxa-menu-sendtab-no-devices" flex="1" class="PanelUI-subView">
     <vbox id="PanelUI-fxa-sendtab-no-devices" align="center" class="panel-subview-body">
       <image class="fxaSendToDeviceLogo" role="presentation"/>
-      <label class="PanelUI-fxa-service-description-label">&fxa.service.sendTab.description;</label>
+      <label class="PanelUI-fxa-service-description-label" data-l10n-id="fxa-menu-send-tab-to-device-description"></label>
       <toolbarbutton id="PanelUI-fxa-menu-sendtab-connect-device-button"
                      class="PanelUI-fxa-signin-button"
                      data-l10n-id="appmenu-remote-tabs-connectdevice"
                      oncommand="gSync.openConnectAnotherDeviceFromFxaMenu(this);"/>
     </vbox>
   </panelview>
 
   <panelview id="appMenu-libraryView" class="PanelUI-subView">
     <vbox class="panel-subview-body">
       <toolbarbutton id="appMenu-library-bookmarks-button"
                      class="subviewbutton subviewbutton-nav"
                      data-l10n-id="library-bookmarks-menu"
                      closemenu="none"
                      oncommand="BookmarkingUI.showSubView(this);"/>
       <toolbarbutton id="appMenu-library-history-button"
                      class="subviewbutton subviewbutton-nav"
-                     label="&historyMenu.label;"
+                     data-l10n-id="appmenuitem-history"
                      closemenu="none"
                      oncommand="PanelUI.showSubView('PanelUI-history', this)"/>
       <toolbarbutton id="appMenu-library-downloads-button"
                      class="subviewbutton"
-                     label="&libraryDownloads.label;"
+                     data-l10n-id="appmenuitem-downloads"
                      oncommand="DownloadsPanel.showDownloadsHistory();"/>
     </vbox>
   </panelview>
 
   <panelview id="PanelUI-whatsNew" class="PanelUI-subView">
     <vbox class="panel-subview-body">
       <box id="PanelUI-whatsNew-title" class="panel-header">
         <label data-l10n-id="whatsnew-panel-header"/>
--- a/browser/base/content/browser-box.inc.xhtml
+++ b/browser/base/content/browser-box.inc.xhtml
@@ -11,17 +11,17 @@
         <image id="sidebar-switcher-arrow"/>
       </toolbarbutton>
       <image id="sidebar-throbber"/>
 # To ensure the button label's intrinsic width doesn't expand the sidebar
 # if the label is long, the button needs flex=1.
 # To ensure the button doesn't expand unnecessarily for short labels, the
 # spacer should significantly out-flex the button.
       <spacer flex="1000"/>
-      <toolbarbutton id="sidebar-close" class="close-icon tabbable" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="SidebarUI.hide();"/>
+      <toolbarbutton id="sidebar-close" class="close-icon tabbable" data-l10n-id="sidebar-close-button" oncommand="SidebarUI.hide();"/>
     </box>
     <browser id="sidebar" flex="1" autoscroll="false" disablehistory="true" disablefullscreen="true"
               style="min-width: 14em; width: 18em; max-width: 36em;" tooltip="aHTMLTooltip"/>
   </vbox>
 
   <splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
   <vbox id="appcontent" flex="1">
     <!-- gNotificationBox will be added here lazily. -->
--- a/browser/base/content/browser-doctype.inc
+++ b/browser/base/content/browser-doctype.inc
@@ -1,8 +1,6 @@
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
 %brandDTD;
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
 %browserDTD;
-<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" >
-%textcontextDTD;
 <!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
 %placesDTD;
--- a/browser/base/content/browser-graphics-utils.js
+++ b/browser/base/content/browser-graphics-utils.js
@@ -9,17 +9,17 @@
  * Global browser interface with graphics utilities.
  */
 var gGfxUtils = {
   _isRecording: false,
   _isTransactionLogging: false,
   _isCapturingFrames: false,
 
   init() {
-    if (Services.prefs.getBoolPref("gfx.webrender.enable-capture")) {
+    if (Services.prefs.getBoolPref("gfx.webrender.debug.enable-capture")) {
       document.getElementById("wrCaptureCmd").removeAttribute("disabled");
       document
         .getElementById("wrToggleCaptureSequenceCmd")
         .removeAttribute("disabled");
     }
   },
 
   /**
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -234,24 +234,23 @@ this.SyncedTabsPanelList = class SyncedT
     if (!client.tabs.length) {
       let label = this._createNoSyncedTabsElement(
         "notabsforclientlabel",
         container
       );
       label.setAttribute("class", "PanelUI-remotetabs-notabsforclient-label");
     } else {
       // If this page will display all tabs, show no additional buttons.
-      // If the next page will display all the remaining tabs, show a "Show All" button
-      // Otherwise, show a "Shore More" button
+      // Otherwise, show a "Show More" button
       let hasNextPage = client.tabs.length > maxTabs;
       let nextPageIsLastPage =
         hasNextPage &&
         maxTabs + SyncedTabsPanelList.sRemoteTabsPerPage >= client.tabs.length;
       if (nextPageIsLastPage) {
-        // When the user clicks "Show All", try to have at least sRemoteTabsNextPageMinTabs more tabs
+        // When the user clicks "Show More", try to have at least sRemoteTabsNextPageMinTabs more tabs
         // to display in order to avoid user frustration
         maxTabs = Math.min(
           client.tabs.length - SyncedTabsPanelList.sRemoteTabsNextPageMinTabs,
           maxTabs
         );
       }
       if (hasNextPage) {
         client.tabs = client.tabs.slice(0, maxTabs);
@@ -853,17 +852,19 @@ var gSync = {
       "PanelUI-fxa-menu-syncnow-button"
     );
 
     const fxaMenuAccountButtonEl = PanelMultiView.getViewNode(
       document,
       "fxa-manage-account-button"
     );
 
-    let headerTitle = menuHeaderTitleEl.getAttribute("defaultLabel");
+    let headerTitle = this.fluentStrings.formatValueSync(
+      "appmenuitem-fxa-sign-in"
+    );
     let headerDescription = this.fluentStrings.formatValueSync(
       "fxa-menu-turn-on-sync-default"
     );
 
     cadButtonEl.setAttribute("disabled", true);
     syncNowButtonEl.hidden = true;
     fxaMenuAccountButtonEl.classList.remove("subviewbutton-nav");
     fxaMenuAccountButtonEl.removeAttribute("closemenu");
@@ -921,16 +922,17 @@ var gSync = {
     }
     mainWindowEl.setAttribute("fxastatus", stateValue);
 
     menuHeaderTitleEl.value = headerTitle;
     menuHeaderDescriptionEl.value = headerDescription;
     // We remove the data-l10n-id attribute here to prevent the node's value
     // attribute from being overwritten by Fluent when the panel is moved
     // around in the DOM.
+    menuHeaderTitleEl.removeAttribute("data-l10n-id");
     menuHeaderDescriptionEl.removeAttribute("data-l10n-id");
   },
 
   enableSendTabIfValidTab() {
     // All tabs selected must be sendable for the Send Tab button to be enabled
     // on the FxA menu.
     let canSendAllURIs = gBrowser.selectedTabs.every(t =>
       BrowserUtils.isShareableURL(t.linkedBrowser.currentURI)
@@ -1538,22 +1540,19 @@ var gSync = {
     sendTabsToDevice.disabled = !enabled;
 
     if (hideItems || !hasASendableURI) {
       sendTabsToDevice.hidden = true;
     } else {
       let tabCount = aTargetTab.multiselected
         ? gBrowser.multiSelectedTabsCount
         : 1;
-      sendTabsToDevice.label = PluralForm.get(
-        tabCount,
-        gNavigatorBundle.getString("sendTabsToDevice.label")
-      ).replace("#1", tabCount.toLocaleString());
-      sendTabsToDevice.accessKey = gNavigatorBundle.getString(
-        "sendTabsToDevice.accesskey"
+      sendTabsToDevice.setAttribute(
+        "data-l10n-args",
+        JSON.stringify({ tabCount })
       );
       sendTabsToDevice.hidden = false;
     }
   },
 
   // "Send Page to Device" and "Send Link to Device" menu items
   updateContentContextMenu(contextMenu) {
     if (!this.FXA_ENABLED) {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -8268,17 +8268,16 @@ function undoCloseTab(aIndex) {
     if (SessionStore.getClosedTabCount(window) > index) {
       tab = SessionStore.undoCloseTab(window, index);
 
       if (blankTabToRemove) {
         gBrowser.removeTab(blankTabToRemove);
       }
     }
   }
-  SessionStore.setLastClosedTabCount(window, 1);
 
   return tab;
 }
 
 /**
  * Re-open a closed window.
  * @param aIndex
  *        The index of the window (via SessionStore.getClosedWindowData)
--- a/browser/base/content/certerror/aboutNetError.js
+++ b/browser/base/content/certerror/aboutNetError.js
@@ -143,16 +143,32 @@ function showTls10Container() {
   const button = document.getElementById("enableTls10Button");
   button.addEventListener("click", function enableTls10(e) {
     RPMSetBoolPref("security.tls.version.enable-deprecated", true);
     retryThis(button);
   });
   setFocus("#enableTls10Button", "beforeend");
 }
 
+function toggleCertErrorDebugInfoVisibility(shouldShow) {
+  let debugInfo = document.getElementById("certificateErrorDebugInformation");
+  let copyButton = document.getElementById("copyToClipboardTop");
+
+  if (shouldShow === undefined) {
+    shouldShow = debugInfo.hidden;
+  }
+  if (shouldShow) {
+    debugInfo.hidden = false;
+    copyButton.scrollIntoView({ block: "start", behavior: "smooth" });
+    copyButton.focus();
+  } else {
+    debugInfo.hidden = true;
+  }
+}
+
 function setupAdvancedButton() {
   // Get the hostname and add it to the panel
   var panel = document.getElementById("badCertAdvancedPanel");
   for (var span of panel.querySelectorAll("span.hostname")) {
     span.textContent = HOST_NAME;
   }
 
   // Register click handler for the weakCryptoAdvancedPanel
@@ -161,18 +177,17 @@ function setupAdvancedButton() {
     .addEventListener("click", togglePanelVisibility);
 
   function togglePanelVisibility() {
     toggleDisplay(panel);
     if (gIsCertError) {
       // Toggling the advanced panel must ensure that the debugging
       // information panel is hidden as well, since it's opened by the
       // error code link in the advanced panel.
-      var div = document.getElementById("certificateErrorDebugInformation");
-      div.style.display = "none";
+      toggleCertErrorDebugInfoVisibility(false);
     }
 
     if (panel.style.display == "block") {
       // send event to trigger telemetry ping
       var event = new CustomEvent("AboutNetErrorUIExpanded", { bubbles: true });
       document.dispatchEvent(event);
     }
   }
@@ -181,18 +196,17 @@ function setupAdvancedButton() {
     return;
   }
 
   if (getCSSClass() == "expertBadCert") {
     toggleDisplay(document.getElementById("badCertAdvancedPanel"));
     // Toggling the advanced panel must ensure that the debugging
     // information panel is hidden as well, since it's opened by the
     // error code link in the advanced panel.
-    var div = document.getElementById("certificateErrorDebugInformation");
-    div.style.display = "none";
+    toggleCertErrorDebugInfoVisibility(false);
   }
 
   disallowCertOverridesIfNeeded();
 }
 
 function disallowCertOverridesIfNeeded() {
   var cssClass = getCSSClass();
   // Disallow overrides if this is a Strict-Transport-Security
@@ -1143,20 +1157,17 @@ async function setTechnicalDetailsOnCert
         // the user here, expand the section by default
         if (href && cssClass != "expertBadCert") {
           document.getElementById("badCertAdvancedPanel").style.display =
             "block";
           if (error == "nssBadCert") {
             // Toggling the advanced panel must ensure that the debugging
             // information panel is hidden as well, since it's opened by the
             // error code link in the advanced panel.
-            let div = document.getElementById(
-              "certificateErrorDebugInformation"
-            );
-            div.style.display = "none";
+            toggleCertErrorDebugInfoVisibility(false);
           }
         }
 
         // Set the link if we want it.
         if (href) {
           setL10NLabel("cert-error-domain-mismatch-single", args, {
             href,
             "data-l10n-name": "domain-mismatch-link",
@@ -1194,16 +1205,17 @@ async function setTechnicalDetailsOnCert
   setL10NLabel(
     "cert-error-code-prefix-link",
     { error: failedCertInfo.errorCodeString },
     {
       title: failedCertInfo.errorCodeString,
       id: "errorCode",
       "data-l10n-name": "error-code-link",
       "data-telemetry-id": "error_code_link",
+      href: "#certificateErrorDebugInformation",
     },
     false
   );
   let errorCodeLink = document.getElementById("errorCode");
   if (errorCodeLink) {
     // We're attaching the event listener to the parent element and not on
     // the errorCodeLink itself because event listeners cannot be attached
     // to fluent DOM overlays.
@@ -1213,20 +1225,18 @@ async function setTechnicalDetailsOnCert
   let div = document.getElementById("certificateErrorText");
   div.textContent = await getFailedCertificatesAsPEMString();
 }
 
 function handleErrorCodeClick(event) {
   if (event.target.id !== "errorCode") {
     return;
   }
-
-  let debugInfo = document.getElementById("certificateErrorDebugInformation");
-  debugInfo.style.display = "block";
-  debugInfo.scrollIntoView({ block: "start", behavior: "smooth" });
+  event.preventDefault();
+  toggleCertErrorDebugInfoVisibility();
   recordClickTelemetry(event);
 }
 
 /* Only focus if we're the toplevel frame; otherwise we
    don't want to call attention to ourselves!
 */
 function setFocus(selector, position = "afterbegin") {
   if (window.top == window) {
--- a/browser/base/content/certerror/aboutNetError.xhtml
+++ b/browser/base/content/certerror/aboutNetError.xhtml
@@ -193,17 +193,17 @@
 
         <div id="blockingErrorReporting">
             <p class="toggle-container-with-text">
                 <input type="checkbox" id="automaticallyReportBlockingInFuture" role="checkbox"/>
                 <label for="automaticallyReportBlockingInFuture" >&errorReporting.automatic2;</label>
             </p>
         </div>
 
-        <div id="certificateErrorDebugInformation">
+        <div id="certificateErrorDebugInformation" hidden="">
           <button id="copyToClipboardTop" data-telemetry-id="clipboard_button_top">&certerror.copyToClipboard.label;</button>
           <div id="certificateErrorText"/>
           <button id="copyToClipboardBottom" data-telemetry-id="clipboard_button_bot">&certerror.copyToClipboard.label;</button>
         </div>
       </div>
     </div>
   </body>
   <script src="chrome://browser/content/certerror/aboutNetErrorCodes.js"/>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/logos/vpn-promo-logo.svg
@@ -0,0 +1,4 @@
+<!-- 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="127" height="121" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M97.936 27.343h-45.38c-2.406 0-4.357 1.86-4.357 4.152v64.92c0 2.292 1.951 4.151 4.358 4.151h45.379c2.406 0 4.357-1.859 4.357-4.152V31.495c0-2.293-1.951-4.152-4.357-4.152z" fill="#C4C6FC"/><path d="M97.933 100.566v-.065H52.557a4.405 4.405 0 0 1-3.032-1.198 3.994 3.994 0 0 1-1.257-2.889V31.495a3.99 3.99 0 0 1 1.257-2.888 4.402 4.402 0 0 1 3.032-1.198h45.376a4.4 4.4 0 0 1 3.033 1.197 3.99 3.99 0 0 1 1.258 2.89v64.918a3.99 3.99 0 0 1-1.258 2.89 4.4 4.4 0 0 1-3.033 1.197v.131a4.54 4.54 0 0 0 3.13-1.236 4.119 4.119 0 0 0 1.296-2.982V31.495c0-.553-.114-1.102-.337-1.613a4.199 4.199 0 0 0-.959-1.368 4.46 4.46 0 0 0-1.437-.913 4.613 4.613 0 0 0-1.693-.32H52.557c-.582 0-1.157.108-1.694.32a4.443 4.443 0 0 0-1.436.913 4.208 4.208 0 0 0-.96 1.368 4.042 4.042 0 0 0-.337 1.613v64.92c0 1.118.467 2.19 1.297 2.981a4.542 4.542 0 0 0 3.13 1.236h45.376v-.066z" fill="#C4C7FC"/><path d="M91.93 90.498l6.283-.252a5.237 5.237 0 0 0 3.371-1.442 6.97 6.97 0 0 0 1.977-3.471 6.32 6.32 0 0 1 1.786-3.154 4.55 4.55 0 0 1 2.926-1.26l1.662-.06-.027-.649-1.664.063a5.243 5.243 0 0 0-3.371 1.442 6.967 6.967 0 0 0-1.974 3.471 6.32 6.32 0 0 1-1.788 3.152 4.538 4.538 0 0 1-2.91 1.26l-6.285.237.027.648-.013.015zm1.373-43.162c3.881-2.984 8.995-5.142 13.604-6.985a45.786 45.786 0 0 1 4.477-1.512c1.852-.517 3.871-.93 5.535-.93a6.667 6.667 0 0 1 1.733.198c.413.094.797.28 1.119.543a2.522 2.522 0 0 1 .934 1.979 4.683 4.683 0 0 1-.439 1.837c-.5 1.152-1.402 2.417-2.508 3.68a53.812 53.812 0 0 1-5.646 5.38 150.31 150.31 0 0 1-2.564 2.107 45.061 45.061 0 0 0-1.72 1.43c-2.942 2.639-5.868 5.394-8.416 8.366-2.548 2.972-4.715 6.161-6.139 9.673l.636.232c1.386-3.426 3.513-6.555 6.03-9.494 2.516-2.94 5.421-5.674 8.355-8.306.264-.252.682-.585 1.19-1.008 1.778-1.447 4.734-3.794 7.253-6.328a26.11 26.11 0 0 0 3.251-3.831c.403-.6.741-1.235 1.011-1.898.239-.58.368-1.196.381-1.818a3.098 3.098 0 0 0-.263-1.292 3.235 3.235 0 0 0-.779-1.085 3.526 3.526 0 0 0-1.5-.787 7.427 7.427 0 0 0-1.919-.221c-1.778 0-3.847.43-5.725.955a47.279 47.279 0 0 0-4.551 1.54c-4.622 1.848-9.79 4.018-13.758 7.079l.425.504-.007-.008z" fill="#A6A8E2"/><path d="M15.713 30.845c.421 2.1 1.059 4.129 2.366 5.735a7.799 7.799 0 0 0 2.497 2.017c1.164.574 2.432.933 3.736 1.056 1.119.115 2.244.168 3.369.159 1.587 0 3.196-.074 4.746-.074 1.908 0 3.741.109 5.419.575a9.297 9.297 0 0 1 4.519 2.723l.51-.426a10.016 10.016 0 0 0-4.839-2.92c-1.778-.491-3.675-.597-5.609-.6-1.571 0-3.175.074-4.746.074-1.1.008-2.199-.043-3.292-.154a9.972 9.972 0 0 1-3.487-.976 7.29 7.29 0 0 1-3.08-3.025 14.702 14.702 0 0 1-1.436-4.285l-.67.12h-.002z" fill="#C4C6FC"/><path d="M108.744 31.737h-.339a.511.511 0 0 1-.161.369.564.564 0 0 1-.772 0 .51.51 0 0 1-.16-.369.496.496 0 0 1 .334-.484.551.551 0 0 1 .211-.035c.145 0 .284.055.387.152.102.097.16.23.161.367h.68a1.13 1.13 0 0 0-.207-.65 1.213 1.213 0 0 0-.551-.43 1.283 1.283 0 0 0-.709-.067 1.25 1.25 0 0 0-.629.32 1.163 1.163 0 0 0-.336.6 1.12 1.12 0 0 0 .07.675c.093.214.251.396.452.525a1.273 1.273 0 0 0 1.551-.146c.23-.219.359-.517.359-.827h-.341zM30.279 4.013h-.339a.502.502 0 0 1-.16.37.55.55 0 0 1-.388.154.573.573 0 0 1-.411-.138.522.522 0 0 1-.174-.382.5.5 0 0 1 .174-.382.553.553 0 0 1 .411-.139c.145 0 .284.055.387.152a.51.51 0 0 1 .161.368h.68a1.13 1.13 0 0 0-.207-.65 1.217 1.217 0 0 0-.55-.431 1.284 1.284 0 0 0-.71-.067 1.248 1.248 0 0 0-.629.32 1.154 1.154 0 0 0-.336.6 1.12 1.12 0 0 0 .07.675c.093.214.25.397.452.525.202.129.44.197.682.197a1.27 1.27 0 0 0 .472-.086 1.174 1.174 0 0 0 .665-.634c.062-.143.093-.295.091-.45l-.341-.002zm-2.717 83.044h-.342a.51.51 0 0 1-.161.367.562.562 0 0 1-.386.152.574.574 0 0 1-.412-.139.521.521 0 0 1-.174-.381.5.5 0 0 1 .174-.382.553.553 0 0 1 .412-.139c.145 0 .284.055.387.153a.51.51 0 0 1 .16.369h.68a1.13 1.13 0 0 0-.205-.65 1.217 1.217 0 0 0-.55-.432 1.284 1.284 0 0 0-.71-.068 1.248 1.248 0 0 0-.63.32 1.15 1.15 0 0 0-.336.598c-.048.227-.024.463.07.676.092.214.25.397.451.526.202.129.44.197.683.197a1.26 1.26 0 0 0 .87-.339 1.16 1.16 0 0 0 .265-.38 1.1 1.1 0 0 0 .092-.448h-.338zM6.747 58.645v1.658a.32.32 0 0 0 .11.212.35.35 0 0 0 .46 0 .32.32 0 0 0 .11-.212v-1.658a.311.311 0 0 0-.088-.248.342.342 0 0 0-.252-.106.357.357 0 0 0-.252.106.322.322 0 0 0-.088.248z" fill="#000"/><path d="M6.215 59.797h1.741a.358.358 0 0 0 .26-.084.326.326 0 0 0 .111-.24.312.312 0 0 0-.11-.24.343.343 0 0 0-.26-.084H6.214a.358.358 0 0 0-.26.084.326.326 0 0 0-.111.24.312.312 0 0 0 .11.24.345.345 0 0 0 .261.084zm116.79 3.383v1.658a.317.317 0 0 0 .109.212.351.351 0 0 0 .461 0 .322.322 0 0 0 .11-.212V63.18a.316.316 0 0 0-.088-.249.353.353 0 0 0-.504 0 .319.319 0 0 0-.088.249z" fill="#000"/><path d="M122.473 64.332h1.741a.361.361 0 0 0 .261-.084.335.335 0 0 0 .081-.11.299.299 0 0 0 .029-.13.299.299 0 0 0-.029-.131.335.335 0 0 0-.342-.193h-1.741a.35.35 0 0 0-.222.104.32.32 0 0 0-.089.22c0 .081.032.16.089.22a.35.35 0 0 0 .222.104z" fill="#000"/><path d="M90.088 21.924H44.71c-2.407 0-4.358 1.858-4.358 4.152v64.918c0 2.293 1.95 4.152 4.358 4.152h45.378c2.407 0 4.358-1.859 4.358-4.151v-64.92c0-2.293-1.951-4.151-4.358-4.151z" fill="#fff"/><path d="M90.085 95.146v-.322h-45.37a4.123 4.123 0 0 1-2.843-1.117 3.74 3.74 0 0 1-1.182-2.705V26.078c0-.503.104-1.002.306-1.466.203-.465.5-.888.873-1.243a4.039 4.039 0 0 1 1.306-.83 4.193 4.193 0 0 1 1.54-.29H90.09a4.12 4.12 0 0 1 2.841 1.121 3.74 3.74 0 0 1 1.177 2.708v64.924a3.74 3.74 0 0 1-1.177 2.708 4.122 4.122 0 0 1-2.841 1.121v.648a4.82 4.82 0 0 0 3.319-1.313 4.372 4.372 0 0 0 1.374-3.164V26.078a4.372 4.372 0 0 0-1.376-3.166 4.82 4.82 0 0 0-3.323-1.311h-45.37a4.82 4.82 0 0 0-3.323 1.311 4.372 4.372 0 0 0-1.377 3.166v64.924a4.373 4.373 0 0 0 1.38 3.16 4.82 4.82 0 0 0 3.32 1.31H90.09l-.006-.326z" fill="#000"/><path d="M79.862 43.23a4.71 4.71 0 0 0-1.8-2.065 5.057 5.057 0 0 0-2.698-.774 5.034 5.034 0 0 0-2.293.538 4.769 4.769 0 0 0-1.753 1.507 4.4 4.4 0 0 0-.642 1.348h-6.853a3.305 3.305 0 0 0-.196-.554 2.927 2.927 0 0 0-.265-.504l2.45-2.335c.74.404 1.577.615 2.43.613a4.967 4.967 0 0 0 1.894-.368 4.794 4.794 0 0 0 2.419-2.11 4.45 4.45 0 0 0 .469-3.091 4.611 4.611 0 0 0-1.694-2.675 5.025 5.025 0 0 0-3.089-1.048 5.206 5.206 0 0 0-1.902.363 4.83 4.83 0 0 0-2.167 1.715 4.49 4.49 0 0 0-.81 2.57 4.4 4.4 0 0 0 .609 2.244l-2.482 2.367a5.071 5.071 0 0 0-2.357-.575 5.083 5.083 0 0 0-1.867.352 4.894 4.894 0 0 0-1.584 1.006 4.634 4.634 0 0 0-1.057 1.507 4.453 4.453 0 0 0 .015 3.591c.37.83.984 1.538 1.767 2.042a5 5 0 0 0 2.726.794c.797 0 1.583-.186 2.287-.543a4.805 4.805 0 0 0 1.753-1.501 4.18 4.18 0 0 0 .65-1.341h6.848a4.043 4.043 0 0 0 .429.988l-2.485 2.364a5.073 5.073 0 0 0-2.354-.572 5.02 5.02 0 0 0-2.294.538 4.77 4.77 0 0 0-1.755 1.507 4.52 4.52 0 0 0-.83 2.596 4.43 4.43 0 0 0 .383 1.813 4.775 4.775 0 0 0 1.768 2.044c.734.47 1.59.74 2.473.784a5.084 5.084 0 0 0 2.544-.54 4.793 4.793 0 0 0 1.89-1.709 4.487 4.487 0 0 0 .7-2.392 4.493 4.493 0 0 0-.651-2.31l2.444-2.326c.74.404 1.577.615 2.43.613a5.025 5.025 0 0 0 3.445-1.367 4.56 4.56 0 0 0 1.436-3.282 4.481 4.481 0 0 0-.381-1.822zM66.17 35.524c.17-.381.452-.708.812-.94.37-.243.81-.371 1.26-.369.3 0 .596.056.873.167.375.149.7.392.941.703a2.05 2.05 0 0 1 .22 2.188c-.174.35-.445.648-.783.862a2.28 2.28 0 0 1-1.252.368c-.369 0-.732-.087-1.057-.252a2.226 2.226 0 0 1-.81-.696 2.078 2.078 0 0 1-.204-2.032zm-4.967 10.355a2.163 2.163 0 0 1-.74.894 2.348 2.348 0 0 1-2.294.21 2.22 2.22 0 0 1-.905-.743 2.065 2.065 0 0 1-.201-2.03c.17-.382.452-.71.812-.942a2.34 2.34 0 0 1 2.117-.2c.407.164.755.439 1 .79.245.35.375.763.376 1.184a2 2 0 0 1-.165.837zm7.126 8.68a2.203 2.203 0 0 1-1.224 1.149 2.355 2.355 0 0 1-1.719-.008 2.285 2.285 0 0 1-.994-.781 2.037 2.037 0 0 1-.379-1.2c0-.281.058-.56.171-.819.113-.26.279-.496.487-.694.209-.2.456-.357.729-.464a2.342 2.342 0 0 1 1.733.006c.408.162.756.436 1 .787a2.056 2.056 0 0 1 .196 2.016v.007zm9.11-8.68a2.16 2.16 0 0 1-.74.894 2.344 2.344 0 0 1-2.293.21 2.22 2.22 0 0 1-.905-.743 2.056 2.056 0 0 1-.204-2.03c.17-.382.453-.71.813-.942a2.34 2.34 0 0 1 2.117-.2c.407.164.755.439 1 .79.245.35.375.763.375 1.184.007.287-.05.572-.164.837z" fill="#000"/><path d="M72.541 82.552l-12.655-.075a6.026 6.026 0 0 1-4.074-1.707 5.472 5.472 0 0 1-1.657-3.936 5.478 5.478 0 0 1 1.709-3.915 6.034 6.034 0 0 1 4.096-1.658l12.655.076c.783-.01 1.56.128 2.287.408a5.92 5.92 0 0 1 1.942 1.22 5.601 5.601 0 0 1 1.295 1.84 5.382 5.382 0 0 1-.029 4.345 5.609 5.609 0 0 1-1.319 1.826 5.928 5.928 0 0 1-1.958 1.196c-.73.27-1.51.4-2.292.38z" fill="#3BFFB7"/><path d="M72.54 82.552v-.322l-12.654-.079a5.696 5.696 0 0 1-3.9-1.565 5.17 5.17 0 0 1-1.611-3.728v-.033a5.174 5.174 0 0 1 1.643-3.716 5.7 5.7 0 0 1 3.913-1.535h.037l12.652.078a5.696 5.696 0 0 1 3.9 1.566 5.169 5.169 0 0 1 1.611 3.728v.035a5.173 5.173 0 0 1-1.643 3.716 5.7 5.7 0 0 1-3.913 1.535h-.034v.645h.04a6.39 6.39 0 0 0 4.388-1.725c1.166-1.105 1.827-2.604 1.837-4.17v-.039c0-1.566-.65-3.069-1.81-4.18a6.38 6.38 0 0 0-4.376-1.749l-12.655-.078h-.04a6.39 6.39 0 0 0-4.38 1.728c-1.164 1.104-1.824 2.601-1.835 4.166v.038c0 1.566.65 3.07 1.81 4.18a6.383 6.383 0 0 0 4.376 1.751l12.655.076-.01-.323z" fill="#000"/><path d="M80.756 76.926a5.422 5.422 0 0 1-1.012 3.108 5.838 5.838 0 0 1-2.654 2.05 6.158 6.158 0 0 1-3.402.298 5.976 5.976 0 0 1-3.003-1.552 5.526 5.526 0 0 1-1.591-2.88 5.362 5.362 0 0 1 .356-3.238 5.68 5.68 0 0 1 2.184-2.503 6.097 6.097 0 0 1 3.275-.925 6.134 6.134 0 0 1 2.249.44 5.905 5.905 0 0 1 1.901 1.227 5.591 5.591 0 0 1 1.264 1.827 5.39 5.39 0 0 1 .433 2.148z" fill="#fff"/><path d="M80.756 76.926h-.341a5.172 5.172 0 0 1-1.636 3.71 5.699 5.699 0 0 1-3.902 1.543h-.037a5.697 5.697 0 0 1-3.905-1.563 5.17 5.17 0 0 1-1.614-3.73v-.033a5.158 5.158 0 0 1 1.64-3.719 5.684 5.684 0 0 1 3.916-1.53h.037a5.695 5.695 0 0 1 3.888 1.565 5.17 5.17 0 0 1 1.613 3.716v.033h.68v-.033c0-1.566-.65-3.069-1.808-4.18a6.378 6.378 0 0 0-4.376-1.749h-.034c-1.643 0-3.22.62-4.386 1.724a5.794 5.794 0 0 0-1.837 4.168v.037c0 1.567.651 3.07 1.81 4.18a6.385 6.385 0 0 0 4.376 1.752h.037c1.644 0 3.22-.62 4.387-1.725 1.165-1.104 1.826-2.603 1.836-4.169l-.344.003z" fill="#000"/><path d="M32.962 53.674H18.674v13.612h14.288V53.674z" fill="#fff"/><path d="M32.962 67.289v-.326H19.013V54h13.61v13.29h.339v-.326.326h.341V53.35H18.336v14.26h14.967v-.322h-.341z" fill="#000"/><path d="M23.434 60.936l1.905 1.818a.353.353 0 0 0 .265.093.353.353 0 0 0 .248-.129l2.858-3.63a.313.313 0 0 0-.076-.445.354.354 0 0 0-.47.055l-2.621 3.332-1.627-1.55a.342.342 0 0 0-.24-.096.355.355 0 0 0-.24.095.323.323 0 0 0-.1.228.31.31 0 0 0 .098.23z" fill="#000"/><path d="M34.25 9.925H3.544c-.34 0-.614.261-.614.584v19.25c0 .322.275.584.614.584H34.25a.6.6 0 0 0 .613-.585V10.51c0-.323-.274-.584-.613-.584z" fill="#fff"/><path d="M34.253 30.34v-.322H3.543a.272.272 0 0 1-.187-.074.246.246 0 0 1-.078-.178v-19.26a.242.242 0 0 1 .08-.183.266.266 0 0 1 .193-.076h30.702a.278.278 0 0 1 .193.076.252.252 0 0 1 .08.184v19.251a.246.246 0 0 1-.078.179.271.271 0 0 1-.187.073v.648a.982.982 0 0 0 .672-.267.89.89 0 0 0 .28-.64V10.507a.891.891 0 0 0-.28-.64.982.982 0 0 0-.672-.268H3.543a.98.98 0 0 0-.675.268.899.899 0 0 0-.206.293.863.863 0 0 0-.072.347v19.251c0 .12.024.237.072.347a.91.91 0 0 0 .206.294.98.98 0 0 0 .675.267h30.71v-.325z" fill="#000"/><path d="M3.14 13.65h31.936v-.648H3.14m13.868 4.251h13.025a.348.348 0 0 0 .222-.105.317.317 0 0 0 .09-.22.317.317 0 0 0-.09-.219.348.348 0 0 0-.222-.104H17.007a.358.358 0 0 0-.26.084.324.324 0 0 0-.11.24.312.312 0 0 0 .11.24.345.345 0 0 0 .26.084zm0 3.604h13.025a.348.348 0 0 0 .222-.104.317.317 0 0 0 .09-.22.317.317 0 0 0-.09-.22.348.348 0 0 0-.222-.104H17.007a.358.358 0 0 0-.26.085.324.324 0 0 0-.11.24.312.312 0 0 0 .11.24.343.343 0 0 0 .26.083zm0 3.595h13.025a.348.348 0 0 0 .222-.104.317.317 0 0 0 .09-.22.317.317 0 0 0-.09-.22.348.348 0 0 0-.222-.104H17.007a.358.358 0 0 0-.26.084.324.324 0 0 0-.11.24.312.312 0 0 0 .11.24.343.343 0 0 0 .26.084z" fill="#000"/><path d="M13.853 20.734a3.482 3.482 0 0 1-.632 2.004 3.747 3.747 0 0 1-1.694 1.331 3.954 3.954 0 0 1-2.186.21 3.843 3.843 0 0 1-1.938-.984 3.553 3.553 0 0 1-1.038-1.845 3.444 3.444 0 0 1 .213-2.082 3.645 3.645 0 0 1 1.393-1.618 3.915 3.915 0 0 1 2.101-.608c1.001 0 1.962.378 2.67 1.051a3.519 3.519 0 0 1 1.111 2.54z" fill="#A67BFC"/><path d="M13.853 20.734h-.338c0 .758-.277 1.492-.782 2.078a3.474 3.474 0 0 1-1.988 1.135 3.59 3.59 0 0 1-2.292-.325 3.34 3.34 0 0 1-1.556-1.636 3.133 3.133 0 0 1-.113-2.205 3.295 3.295 0 0 1 1.38-1.773 3.572 3.572 0 0 1 2.248-.537 3.513 3.513 0 0 1 2.095.944c.32.304.573.666.746 1.064.173.398.262.824.262 1.255h.68c0-.777-.242-1.536-.695-2.182a4.085 4.085 0 0 0-1.85-1.447 4.311 4.311 0 0 0-2.382-.223 4.19 4.19 0 0 0-2.11 1.075 3.862 3.862 0 0 0-1.128 2.01 3.755 3.755 0 0 0 .234 2.27c.312.718.84 1.33 1.518 1.762a4.269 4.269 0 0 0 2.29.662c.542 0 1.078-.1 1.579-.298.5-.197.954-.486 1.337-.851s.687-.798.894-1.275c.207-.476.313-.987.313-1.503h-.342z" fill="#000"/><path d="M9.409 22.31h.34v-2.468l1.268.907-1.812 1.3.204.253.206.252 2.172-1.558a.327.327 0 0 0 .126-.252.313.313 0 0 0-.126-.252l-2.172-1.56a.35.35 0 0 0-.355-.026.333.333 0 0 0-.137.12.31.31 0 0 0-.05.17v3.113a.315.315 0 0 0 .19.287.353.353 0 0 0 .355-.035l-.21-.252zm11.895 89.507h32.039v-.648H21.304m-15.11 3.854h32.035v-.648H6.195" fill="#000"/><path d="M53.92 109.041H25.166a5.576 5.576 0 0 1 2.024-3.331 6.082 6.082 0 0 1 3.797-1.312h.228a8.338 8.338 0 0 1 3.535-3.241 8.881 8.881 0 0 1 4.825-.843 8.74 8.74 0 0 1 4.512 1.836 8.093 8.093 0 0 1 2.698 3.904 4.337 4.337 0 0 1 2.183-1.019 4.458 4.458 0 0 1 2.412.285 4.214 4.214 0 0 1 1.856 1.496 3.92 3.92 0 0 1 .693 2.22l-.01.005z" fill="#A77FFA"/><path d="M45.879 111.491v-.322H14.552v.322l.333.058a5.745 5.745 0 0 1 2.085-3.436 6.27 6.27 0 0 1 3.916-1.353h.233a.349.349 0 0 0 .18-.04.328.328 0 0 0 .13-.124 8.755 8.755 0 0 1 3.71-3.398 9.312 9.312 0 0 1 5.063-.884 9.164 9.164 0 0 1 4.734 1.925 8.49 8.49 0 0 1 2.833 4.094.34.34 0 0 0 .236.218.349.349 0 0 0 .32-.072 4.378 4.378 0 0 1 2.201-1.027 4.492 4.492 0 0 1 2.432.288 4.248 4.248 0 0 1 1.871 1.508c.457.666.7 1.443.7 2.238h.68c0-.92-.282-1.821-.81-2.592a4.92 4.92 0 0 0-2.167-1.747 5.205 5.205 0 0 0-2.818-.334 5.076 5.076 0 0 0-2.55 1.19l.23.237.326-.094a9.135 9.135 0 0 0-3.047-4.406 9.854 9.854 0 0 0-5.094-2.072 10.03 10.03 0 0 0-5.448.95 9.427 9.427 0 0 0-3.992 3.658l.296.159.032-.308h-.265c-1.59 0-3.131.534-4.35 1.507a6.4 6.4 0 0 0-2.32 3.817.311.311 0 0 0 .076.249.358.358 0 0 0 .244.117h31.327a.358.358 0 0 0 .24-.096.323.323 0 0 0 .101-.23h-.341z" fill="#000"/><path d="M124.211 85.345a1.358 1.358 0 0 0-.037-.209 1.745 1.745 0 0 0-.728-1.025 1.904 1.904 0 0 0-1.478-.27l-8.036 1.814a1.865 1.865 0 0 1-1.121-.097 1.775 1.775 0 0 1-.72-.545l-.344-.444 11.906-3.4a.325.325 0 0 0 .169-.112.303.303 0 0 0 .067-.186l.076-5.904a.291.291 0 0 0-.037-.156.302.302 0 0 0-.082-.094.33.33 0 0 0-.243-.065h-.04l-3.334.756-.092-.38a3.9 3.9 0 0 0-1.623-2.292 4.248 4.248 0 0 0-3.299-.61 4.117 4.117 0 0 0-2.397 1.55 3.776 3.776 0 0 0-.725 2.672c.021.16.051.32.09.477l.096.378-2.776.628-.881-2.458a.309.309 0 0 0-.138-.166.348.348 0 0 0-.214-.048h-.037l-1.463.33a.332.332 0 0 0-.192.125.298.298 0 0 0-.059.215.091.091 0 0 0 0 .04.298.298 0 0 0 .132.184.33.33 0 0 0 .225.056h.042l1.164-.252.119.338c.156.448 2.646 7.562 2.982 8.508.019.047.029.08.035.095a2.385 2.385 0 0 0 1.259 1.311l.368.162-.17.352c-.14.286-.196.602-.161.915.06.453.297.867.664 1.16.186.15.402.262.635.33a1.97 1.97 0 0 0 .953.024c.443-.1.832-.355 1.087-.714.208-.286.32-.623.324-.97a1.675 1.675 0 0 0-.303-.977l-.317-.46 5.069-1.148-.066.552a1.581 1.581 0 0 0 0 .401c.057.456.3.873.677 1.16a1.884 1.884 0 0 0 1.347.375c.074 0 .148-.02.225-.035.434-.102.814-.35 1.071-.699.256-.348.372-.773.326-1.197l.005.005zm-10.879-11.293a3.39 3.39 0 0 1 2.016-1.306c.134-.032.27-.056.407-.07a3.568 3.568 0 0 1 2.344.581 3.275 3.275 0 0 1 1.36 1.91c.033.128.057.258.072.39a3.144 3.144 0 0 1-.606 2.236 3.43 3.43 0 0 1-2.008 1.293 3.567 3.567 0 0 1-2.751-.514 3.277 3.277 0 0 1-1.435-2.3 3.132 3.132 0 0 1 .601-2.22zm-1.71 9.892l-1.931-5.51 2.847-.644.159.232a4.04 4.04 0 0 0 1.682 1.41 4.281 4.281 0 0 0 2.699.274 4.094 4.094 0 0 0 2.143-1.237c.548-.603.889-1.35.979-2.143l.032-.275 3.064-.678-.066 4.979v.287l-11.608 3.305zm3.686 4.048a1.172 1.172 0 0 1-.823.472c-.284.03-.57-.04-.803-.196a1.12 1.12 0 0 1-.491-.79c-.031-.27.042-.543.207-.765a1.176 1.176 0 0 1 .828-.465c.284-.03.569.04.802.196a1.112 1.112 0 0 1 .489.787c.032.27-.043.54-.209.761zm8.041-1.815a1.172 1.172 0 0 1-.826.472 1.234 1.234 0 0 1-.793-.195 1.121 1.121 0 0 1-.495-.791c-.03-.27.044-.542.208-.765.165-.222.409-.38.686-.445a.895.895 0 0 1 .14-.02c.285-.032.571.037.805.194a1.106 1.106 0 0 1 .489.789c.03.27-.046.541-.214.761z" fill="#C4C6FC"/><path d="M118.943 77.788a3.427 3.427 0 0 1-2.019 1.306 3.571 3.571 0 0 1-2.751-.513 3.276 3.276 0 0 1-1.434-2.3 3.16 3.16 0 0 1 .611-2.234 3.45 3.45 0 0 1 2.006-1.296 3.09 3.09 0 0 1 .407-.07 3.571 3.571 0 0 1 2.344.581 3.276 3.276 0 0 1 1.36 1.91c.033.128.057.258.072.39a3.164 3.164 0 0 1-.596 2.226zm4.406 8.389a1.172 1.172 0 0 1-.826.472 1.239 1.239 0 0 1-.794-.195 1.124 1.124 0 0 1-.494-.791c-.03-.27.044-.542.208-.765a1.19 1.19 0 0 1 .686-.445.894.894 0 0 1 .14-.02c.284-.032.571.037.805.193a1.106 1.106 0 0 1 .489.79c.03.27-.046.541-.214.761zm-8.041 1.815a1.172 1.172 0 0 1-.823.472c-.284.03-.57-.04-.804-.196a1.117 1.117 0 0 1-.49-.79c-.031-.27.042-.543.207-.765a1.173 1.173 0 0 1 .828-.465c.283-.03.569.04.802.196a1.112 1.112 0 0 1 .489.787c.032.27-.043.54-.209.761z" fill="#fff"/><path d="M123.296 75.373l-.066 4.979v.287l-11.608 3.305-1.931-5.51 2.847-.644.159.232a4.04 4.04 0 0 0 1.682 1.41 4.281 4.281 0 0 0 2.699.274 4.094 4.094 0 0 0 2.143-1.237c.547-.603.889-1.35.979-2.143l.032-.275 3.064-.678z" fill="#fff"/><path d="M117.388 74.94a.295.295 0 0 1-.048.201l-1.114 1.722a.322.322 0 0 1-.246.148.328.328 0 0 1-.209-.045l-.905-.532a.334.334 0 0 1-.106-.102.3.3 0 0 1-.002-.334.311.311 0 0 1 .087-.09.35.35 0 0 1 .247-.056.34.34 0 0 1 .121.043l.622.365.941-1.454a.343.343 0 0 1 .53-.045.31.31 0 0 1 .082.176v.002z" fill="#000"/></svg>
\ No newline at end of file
--- a/browser/base/content/main-popupset.inc.xhtml
+++ b/browser/base/content/main-popupset.inc.xhtml
@@ -49,17 +49,19 @@
                   tbattr="tabbrowser-multiple"
                   oncommand="gBrowser.moveTabsToEnd(TabContextMenu.contextTab);"/>
         <menuitem id="context_openTabInWindow" data-lazy-l10n-id="move-to-new-window"
                   tbattr="tabbrowser-multiple"
                   oncommand="gBrowser.replaceTabsWithWindow(TabContextMenu.contextTab);"/>
       </menupopup>
     </menu>
     <menu id="context_sendTabToDevice"
-          class="sync-ui-item">
+          class="sync-ui-item"
+          data-lazy-l10n-id="tab-context-send-tabs-to-device"
+          data-l10n-args='{"tabCount": 1}'>
       <menupopup id="context_sendTabToDevicePopupMenu"
                  onpopupshowing="gSync.populateSendTabToDevicesMenu(event.target, TabContextMenu.contextTab.linkedBrowser.currentURI.spec, TabContextMenu.contextTab.linkedBrowser.contentTitle, TabContextMenu.contextTab.multiselected);"/>
     </menu>
     <menu id="context_reopenInContainer"
           data-lazy-l10n-id="tab-context-open-in-new-container-tab"
           hidden="true">
       <menupopup oncommand="TabContextMenu.reopenInContainer(event);"
                  onpopupshowing="TabContextMenu.createReopenInContainerMenu(event);"/>
@@ -375,22 +377,17 @@
   </menupopup>
 
   <menupopup id="blockedPopupOptions"
              onpopupshowing="gPopupBlockerObserver.fillPopupList(event);"
              onpopuphiding="gPopupBlockerObserver.onPopupHiding(event);">
     <menuitem id="blockedPopupAllowSite"
               oncommand="gPopupBlockerObserver.toggleAllowPopupsForSite(event);"/>
     <menuitem
-#ifdef XP_WIN
-              label="&editPopupSettings.label;"
-#else
-              label="&editPopupSettingsUnix.label;"
-#endif
-              accesskey="&editPopupSettings.accesskey;"
+              data-l10n-id="edit-popup-settings"
               oncommand="gPopupBlockerObserver.editPopupSettings();"/>
     <menuitem id="blockedPopupDontShowMessage"
               data-l10n-id="popups-infobar-dont-show-message"
               type="checkbox"
               oncommand="gPopupBlockerObserver.dontShowMessage();"/>
     <menuseparator id="blockedPopupsSeparator"/>
   </menupopup>
 
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -558,27 +558,27 @@
                    print-button-title="&printButton.tooltip;"
 #endif
                    keepbroadcastattributeswhencustomizing="true"
                    tooltip="dynamic-shortcut-tooltip"
                    label="&printButton.label;"/>
 
 
     <toolbarbutton id="new-window-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
-                   label="&newNavigatorCmd.label;"
+                   data-l10n-id="appmenuitem-new-window"
                    command="cmd_newNavigator"
                    tooltip="dynamic-shortcut-tooltip"
                    ondrop="newWindowButtonObserver.onDrop(event)"
                    ondragover="newWindowButtonObserver.onDragOver(event)"
                    ondragenter="newWindowButtonObserver.onDragOver(event)"/>
 
     <toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                    observes="View:FullScreen"
                    type="checkbox"
-                   label="&fullScreenCmd.label;"
+                   data-l10n-id="appmenuitem-fullscreen"
                    tooltip="dynamic-shortcut-tooltip"/>
 
     <toolbarbutton id="bookmarks-menu-button"
                    class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
                    type="menu"
                    data-l10n-id="bookmarks-menu-button"
                    tooltip="dynamic-shortcut-tooltip"
                    ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -280,17 +280,17 @@ class nsContextMenu {
     if (context.shouldInitInlineSpellCheckerUINoChildren) {
       InlineSpellCheckerUI.initFromRemote(
         this.contentData.spellInfo,
         this.actor.manager
       );
     }
 
     if (this.contentData.spellInfo) {
-      this.spellSuggesions = this.contentData.spellInfo.spellSuggestions;
+      this.spellSuggestions = this.contentData.spellInfo.spellSuggestions;
     }
 
     if (context.shouldInitInlineSpellCheckerUIWithChildren) {
       InlineSpellCheckerUI.initFromRemote(
         this.contentData.spellInfo,
         this.actor.manager
       );
       let canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
@@ -770,17 +770,17 @@ class nsContextMenu {
     // suggestion list
     if (onMisspelling) {
       var suggestionsSeparator = document.getElementById(
         "spell-add-to-dictionary"
       );
       var numsug = InlineSpellCheckerUI.addSuggestionsToMenu(
         suggestionsSeparator.parentNode,
         suggestionsSeparator,
-        this.spellSuggesions
+        this.spellSuggestions
       );
       this.showItem("spell-no-suggestions", numsug == 0);
     } else {
       this.showItem("spell-no-suggestions", false);
     }
 
     // dictionary list
     this.showItem("spell-dictionaries", showDictionaries);
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -33,17 +33,19 @@
         </menulist>
         <description id="webRTC-all-windows-shared" hidden="true" data-l10n-id="popup-all-windows-shared"></description>
       </popupnotificationcontent>
 
       <popupnotificationcontent id="webRTC-preview" hidden="true">
         <html:video id="webRTC-previewVideo" tabindex="-1"/>
         <vbox id="webRTC-previewWarningBox">
           <description id="webRTC-previewWarning"/>
-          <label id="webRTC-previewWarning-learnMore" is="text-link" class="popup-notification-learnmore-link"/>
+          <hbox>
+            <label id="webRTC-previewWarning-learnMore" is="text-link" class="popup-notification-learnmore-link"/>
+          </hbox>
         </vbox>
       </popupnotificationcontent>
 
       <popupnotificationcontent id="webRTC-selectMicrophone" orient="vertical">
         <label id="webRTC-selectMicrophone-label"
                data-l10n-id="popup-select-microphone-device"
                control="webRTC-selectMicrophone-menulist"/>
         <html:div class="webRTC-selectDevice-selector-container">
new file mode 100644
--- /dev/null
+++ b/browser/base/content/spotlight.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+
+<!-- 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/. -->
+
+<html>
+<head>
+  <meta http-equiv="Content-Security-Policy" content="default-src chrome:; object-src 'none'">
+  <meta name="referrer" content="no-referrer">
+  <link rel="stylesheet" type="text/css" href="chrome://global/skin/in-content/common.css">
+  <link rel="stylesheet" type="text/css" href="chrome://browser/skin/spotlight.css">
+  <link rel="localization" href="branding/brand.ftl">
+  <link rel="localization" href="browser/branding/brandings.ftl">
+  <link rel="localization" href="browser/newtab/asrouter.ftl">
+</head>
+<body role="dialog" aria-labelledby="title" aria-describedby="content">
+  <template id="logo-and-content">
+    <div id="dialog-content">
+      <img class="logo" alt="" src=""/>
+      <h1 id="title" class="title"></h1>
+      <p id="content" class="text"></p>
+      <button id="primary" class="primary"></button>
+      <button id="secondary" class="secondary text-link"></button>
+    </div>
+  </template>
+  <script src="chrome://browser/content/spotlight.js"></script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/base/content/spotlight.js
@@ -0,0 +1,54 @@
+/* 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/. */
+
+const { document: gDoc } = window.docShell.chromeEventHandler.ownerGlobal;
+
+function renderSpotlight() {
+  const [content, params] = window.arguments[0];
+  const template = document.querySelector(`#${content?.template}`);
+  const clone = template.content.cloneNode(true);
+
+  document.body.classList.add(content.template);
+
+  let imageEl = clone.querySelector(".logo");
+  imageEl.src = content.logoImageURL;
+
+  for (let textProp in content.body) {
+    let el = clone.querySelector(`.${textProp}`);
+    if (!content.body[textProp]?.label) {
+      el.remove();
+      continue;
+    }
+    if (content.body[textProp].label.string_id) {
+      document.l10n.setAttributes(el, content.body[textProp].label.string_id);
+    } else {
+      el.textContent = content.body[textProp].label;
+    }
+  }
+
+  document.body.appendChild(clone);
+
+  let primaryBtn = document.getElementById("primary");
+  let secondaryBtn = document.getElementById("secondary");
+  if (primaryBtn) {
+    primaryBtn.addEventListener("click", () => {
+      params.primaryBtn = true;
+      window.close();
+    });
+
+    // If we just call focus() at some random time, it'll cause a flush,
+    // which slows things down unnecessarily, so instead we use rAF...
+    requestAnimationFrame(() => {
+      primaryBtn.focus({ preventFocusRing: true });
+    });
+  }
+  if (secondaryBtn) {
+    secondaryBtn.addEventListener("click", () => {
+      params.secondaryBtn = true;
+      window.close();
+    });
+  }
+}
+
+renderSpotlight();
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -2578,17 +2578,16 @@
         UserInteraction.start("browser.tabs.opening", "initting", window);
       }
 
       // Don't use document.l10n.setAttributes because the FTL file is loaded
       // lazily and we won't be able to resolve the string.
       document
         .getElementById("History:UndoCloseTab")
         .setAttribute("data-l10n-args", JSON.stringify({ tabCount: 1 }));
-      SessionStore.setLastClosedTabCount(window, 1);
 
       // if we're adding tabs, we're past interrupt mode, ditch the owner
       if (this.selectedTab.owner) {
         this.selectedTab.owner = null;
       }
 
       // Find the tab that opened this one, if any. This is used for
       // determining positioning, and inherited attributes such as the
@@ -3127,34 +3126,34 @@
       var warnOnClose = { value: true };
 
       // focus the window before prompting.
       // this will raise any minimized window, which will
       // make it obvious which window the prompt is for and will
       // solve the problem of windows "obscuring" the prompt.
       // see bug #350299 for more details
       window.focus();
-      let warningMessage = gTabBrowserBundle.GetStringFromName(
-        "tabs.closeWarningMultipleTabs"
+      let warningTitle = gTabBrowserBundle.GetStringFromName(
+        "tabs.closeTabsTitle"
       );
-      warningMessage = PluralForm.get(tabsToClose, warningMessage).replace(
+      warningTitle = PluralForm.get(tabsToClose, warningTitle).replace(
         "#1",
         tabsToClose
       );
       let flags =
         ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
         ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1;
       let checkboxLabel =
         aCloseTabs == this.closingTabsEnum.ALL
-          ? gTabBrowserBundle.GetStringFromName("tabs.closeWarningPrompt")
+          ? gTabBrowserBundle.GetStringFromName("tabs.closeTabsConfirmCheckbox")
           : null;
       var buttonPressed = ps.confirmEx(
         window,
-        gTabBrowserBundle.GetStringFromName("tabs.closeTitleTabs"),
-        warningMessage,
+        warningTitle,
+        null,
         flags,
         gTabBrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
         null,
         null,
         checkboxLabel,
         warnOnClose
       );
 
@@ -3420,28 +3419,29 @@
         window.closeWindow(
           true,
           suppressWarnAboutClosingWindow ? null : window.warnAboutClosingWindow,
           "close-last-tab"
         );
         return;
       }
 
-      let initialTabCount = tabs.length;
+      SessionStore.resetLastClosedTabCount(window);
       this._clearMultiSelectionLocked = true;
 
       // Guarantee that _clearMultiSelectionLocked lock gets released.
       try {
         let tabsWithBeforeUnloadPrompt = [];
         let tabsWithoutBeforeUnload = [];
         let beforeUnloadPromises = [];
         let lastToClose;
         let aParams = { animate, prewarmed: true };
 
         for (let tab of tabs) {
+          tab._closedInGroup = true;
           if (tab.selected) {
             lastToClose = tab;
             let toBlurTo = this._findTabToBlurTo(lastToClose, tabs);
             if (toBlurTo) {
               this._getSwitcher().warmupTab(toBlurTo);
             }
           } else if (this._hasBeforeUnload(tab)) {
             TelemetryStopwatch.start("FX_TAB_CLOSE_PERMIT_UNLOAD_TIME_MS", tab);
@@ -3511,40 +3511,41 @@
         );
         if (!done) {
           return;
         }
 
         // Now run again sequentially the beforeunload listeners that will result in a prompt.
         for (let tab of tabsWithBeforeUnloadPrompt) {
           this.removeTab(tab, aParams);
+          if (!tab.closing) {
+            // If we abort the closing of the tab.
+            tab._closedInGroup = false;
+          }
         }
 
         // Avoid changing the selected browser several times by removing it,
         // if appropriate, lastly.
         if (lastToClose) {
           this.removeTab(lastToClose, aParams);
         }
       } catch (e) {
         Cu.reportError(e);
       }
 
       this._clearMultiSelectionLocked = false;
       this.avoidSingleSelectedTab();
-      let closedTabsCount =
-        initialTabCount - tabs.filter(t => t.isConnected && !t.closing).length;
       // Don't use document.l10n.setAttributes because the FTL file is loaded
       // lazily and we won't be able to resolve the string.
-      document
-        .getElementById("History:UndoCloseTab")
-        .setAttribute(
-          "data-l10n-args",
-          JSON.stringify({ tabCount: closedTabsCount })
-        );
-      SessionStore.setLastClosedTabCount(window, closedTabsCount);
+      document.getElementById("History:UndoCloseTab").setAttribute(
+        "data-l10n-args",
+        JSON.stringify({
+          tabCount: SessionStore.getLastClosedTabCount(window),
+        })
+      );
     },
 
     removeCurrentTab(aParams) {
       this.removeTab(this.selectedTab, aParams);
     },
 
     removeTab(
       aTab,
--- a/browser/base/content/test/about/browser_aboutCertError.js
+++ b/browser/base/content/test/about/browser_aboutCertError.js
@@ -163,16 +163,20 @@ add_task(async function checkAdvancedDet
 
     let message = await SpecialPowers.spawn(bc, [], async function() {
       let doc = content.document;
       let shortDescText = doc.getElementById("errorShortDescText");
       Assert.ok(
         shortDescText.textContent.includes("expired.example.com"),
         "Should list hostname in error message."
       );
+      Assert.ok(
+        doc.getElementById("certificateErrorDebugInformation").hidden,
+        "Debug info is initially hidden"
+      );
 
       let exceptionButton = doc.getElementById("exceptionDialogButton");
       Assert.ok(
         !exceptionButton.disabled,
         "Exception button is not disabled by default."
       );
 
       let advancedButton = doc.getElementById("advancedButton");
@@ -196,16 +200,21 @@ add_task(async function checkAdvancedDet
 
     message = await SpecialPowers.spawn(bc, [], async function() {
       let doc = content.document;
       let errorCode = doc.getElementById("errorCode");
       errorCode.click();
       let div = doc.getElementById("certificateErrorDebugInformation");
       let text = doc.getElementById("certificateErrorText");
 
+      Assert.ok(
+        content.getComputedStyle(div).display !== "none",
+        "Debug information is visible"
+      );
+
       let serhelper = Cc[
         "@mozilla.org/network/serialization-helper;1"
       ].getService(Ci.nsISerializationHelper);
       let serializable = content.docShell.failedChannel.securityInfo
         .QueryInterface(Ci.nsITransportSecurityInfo)
         .QueryInterface(Ci.nsISerializable);
       let serializedSecurityInfo = serhelper.serializeToString(serializable);
       return {
--- a/browser/base/content/test/contextMenu/browser_contextmenu.js
+++ b/browser/base/content/test/contextMenu/browser_contextmenu.js
@@ -169,81 +169,60 @@ add_task(async function test_plaintext()
     true,
     "---",
     null,
     "context-viewsource",
     true,
   ]);
 });
 
+const kLinkItems = [
+  "context-openlinkintab",
+  true,
+  ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
+  // We need a blank entry here because the containers submenu is
+  // dynamically generated with no ids.
+  ...(hasContainers ? ["", null] : []),
+  "context-openlink",
+  true,
+  "context-openlinkprivate",
+  true,
+  "---",
+  null,
+  "context-bookmarklink",
+  true,
+  "context-savelink",
+  true,
+  ...(hasPocket ? ["context-savelinktopocket", true] : []),
+  "context-copylink",
+  true,
+  "---",
+  null,
+  "context-searchselect",
+  true,
+  "context-searchselect-private",
+  true,
+];
+
 add_task(async function test_link() {
-  await test_contextmenu("#test-link", [
-    "context-openlinkintab",
-    true,
-    ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
-    // We need a blank entry here because the containers submenu is
-    // dynamically generated with no ids.
-    ...(hasContainers ? ["", null] : []),
-    "context-openlink",
-    true,
-    "context-openlinkprivate",
-    true,
-    "---",
-    null,
-    "context-bookmarklink",
-    true,
-    "context-savelink",
-    true,
-    ...(hasPocket ? ["context-savelinktopocket", true] : []),
-    "context-copylink",
-    true,
-    "---",
-    null,
-    "context-searchselect",
-    true,
-    "context-searchselect-private",
-    true,
-  ]);
+  await test_contextmenu("#test-link", kLinkItems);
 });
 
 add_task(async function test_link_in_shadow_dom() {
-  await test_contextmenu(
-    "#shadow-host",
-    [
-      "context-openlinkintab",
-      true,
-      ...(hasContainers ? ["context-openlinkinusercontext-menu", true] : []),
-      // We need a blank entry here because the containers submenu is
-      // dynamically generated with no ids.
-      ...(hasContainers ? ["", null] : []),
-      "context-openlink",
-      true,
-      "context-openlinkprivate",
-      true,
-      "---",
-      null,
-      "context-bookmarklink",
-      true,
-      "context-savelink",
-      true,
-      ...(hasPocket ? ["context-savelinktopocket", true] : []),
-      "context-copylink",
-      true,
-      "---",
-      null,
-      "context-searchselect",
-      true,
-      "context-searchselect-private",
-      true,
-    ],
-    {
-      offsetX: 6,
-      offsetY: 6,
-    }
-  );
+  await test_contextmenu("#shadow-host", kLinkItems, {
+    offsetX: 6,
+    offsetY: 6,
+  });
+});
+
+add_task(async function test_link_over_shadow_dom() {
+  await test_contextmenu("#shadow-host-in-link", kLinkItems, {
+    offsetX: 6,
+    offsetY: 6,
+  });
 });
 
 add_task(async function test_mailto() {
   await test_contextmenu("#test-mailto", [
     "context-copyemail",
     true,
     "---",
     null,
--- a/browser/base/content/test/contextMenu/subtst_contextmenu.html
+++ b/browser/base/content/test/contextMenu/subtst_contextmenu.html
@@ -4,22 +4,24 @@
   <title>Subtest for browser context menu</title>
 </head>
 <body>
 Browser context menu subtest.
 
 <div id="test-text">Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</div>
 <a id="test-link" href="http://mozilla.com">Click the monkey!</a>
 <div id="shadow-host"></div>
+<a href="http://mozilla.com" style="display: block">
+  <span id="shadow-host-in-link"></span>
+</a>
 <script>
-// Create the shadow DOM in case shadow DOM is enabled.
-if ("ShadowRoot" in this) {
-  var sr = document.getElementById("shadow-host").attachShadow({mode: "closed"});
-  sr.innerHTML = "<a href='http://mozilla.com'>Click the monkey!</a>";
-}
+document.getElementById("shadow-host").attachShadow({ mode: "closed" }).innerHTML =
+  "<a href='http://mozilla.com'>Click the monkey!</a>";
+document.getElementById("shadow-host-in-link").attachShadow({ mode: "closed" }).innerHTML =
+  "<span>Click the monkey!</span>";
 </script>
 <a id="test-mailto" href="mailto:codemonkey@mozilla.com">Mail the monkey!</a><br>
 <input id="test-input"><br>
 <img id="test-image" src="ctxmenu-image.png">
 <svg>
   <image id="test-svg-image" href="ctxmenu-image.png"/>
 </svg>
 <canvas id="test-canvas" width="100" height="100" style="background-color: blue"></canvas>
--- a/browser/base/content/test/general/browser_storagePressure_notification.js
+++ b/browser/base/content/test/general/browser_storagePressure_notification.js
@@ -23,17 +23,17 @@ function openAboutPrefPromise(win) {
       "about:preferences#privacy"
     ),
     TestUtils.topicObserved("privacy-pane-loaded", () => true),
     TestUtils.topicObserved("sync-pane-loaded", () => true),
   ];
   return Promise.all(promises);
 }
 add_task(async function setup() {
-  let win = await BrowserTestUtils.openNewWindowWithFlushedXULCacheForMozSupports();
+  let win = await BrowserTestUtils.openNewBrowserWindow();
   // Open a new tab to keep the window open.
   await BrowserTestUtils.openNewForegroundTab(
     win.gBrowser,
     "https://example.com"
   );
 });
 
 // Test only displaying notification once within the given interval
--- a/browser/base/content/test/performance/browser_startup_mainthreadio.js
+++ b/browser/base/content/test/performance/browser_startup_mainthreadio.js
@@ -209,23 +209,16 @@ const startupPhases = {
     },
     {
       // bug 1543752
       path: "PrefD:user.js",
       stat: 1,
       read: 1,
       close: 1,
     },
-    {
-      // bug 1546838
-      path: "ProfD:xulstore/data.mdb",
-      condition: WIN,
-      read: 1,
-      write: 1,
-    },
   ],
 
   "before opening first browser window": [
     {
       // bug 1541226
       path: "ProfD:",
       condition: WIN,
       stat: 1,
@@ -345,22 +338,16 @@ const startupPhases = {
       stat: 1,
     },
     {
       // Not in packaged builds; useful for artifact builds.
       path: "GreD:EventArtifactDefinitions.json",
       condition: WIN && !AppConstants.MOZILLA_OFFICIAL,
       stat: 1,
     },
-    {
-      // bug 1546838
-      path: "ProfD:xulstore/data.mdb",
-      condition: MAC,
-      write: 1,
-    },
   ],
 
   // We are at this phase once we are ready to handle user events.
   // Any IO at this phase or before gets in the way of the user
   // interacting with the first browser window.
   "before handling user events": [
     {
       path: "GreD:update.test",
@@ -481,34 +468,34 @@ const startupPhases = {
       write: 2,
     },
     {
       // bug 1391590
       path: "ProfD:places.sqlite-wal",
       ignoreIfUnused: true,
       stat: 4,
       fsync: 3,
-      read: 44,
-      write: 164,
+      read: 47,
+      write: 170,
     },
     {
       // bug 1391590
       path: "ProfD:places.sqlite-shm",
       condition: WIN,
       ignoreIfUnused: true,
       stat: 1,
     },
     {
       // bug 1391590
       path: "ProfD:places.sqlite",
       ignoreIfUnused: true,
       fsync: 2,
       read: 4,
       stat: 3,
-      write: 1317,
+      write: 1320,
     },
     {
       // bug 1391590
       path: "ProfD:favicons.sqlite-journal",
       ignoreIfUnused: true,
       fsync: 2,
       stat: 7,
       read: 2,
--- a/browser/base/content/test/performance/io/browser.ini
+++ b/browser/base/content/test/performance/io/browser.ini
@@ -18,12 +18,12 @@ prefs =
 environment =
   GNOME_ACCESSIBILITY=0
   MOZ_PROFILER_STARTUP=1
   MOZ_PROFILER_STARTUP_PERFORMANCE_TEST=1
   MOZ_PROFILER_STARTUP_FEATURES=js,mainthreadio
   MOZ_PROFILER_STARTUP_ENTRIES=10000000
 [../browser_startup_mainthreadio.js]
 skip-if =
-  apple_silicon  # failures
+  apple_silicon  # bug 1707724
   os == "win" && bits == 32
 [../browser_startup_content_mainthreadio.js]
 [../browser_startup_syncIPC.js]
--- a/browser/base/content/test/plugins/BlocklistTestProxy.jsm
+++ b/browser/base/content/test/plugins/BlocklistTestProxy.jsm
@@ -24,19 +24,17 @@ var BlocklistProxy = {
   QueryInterface: ChromeUtils.generateQI([
     "nsIObserver",
     "nsIBlocklistService",
     "nsITimerCallback",
   ]),
 
   init() {
     if (!this._uuid) {
-      this._uuid = Cc["@mozilla.org/uuid-generator;1"]
-        .getService(Ci.nsIUUIDGenerator)
-        .generateUUID();
+      this._uuid = Services.uuid.generateUUID();
       Cm.nsIComponentRegistrar.registerFactory(
         this._uuid,
         "",
         "@mozilla.org/extensions/blocklist;1",
         this
       );
     }
   },
--- a/browser/base/content/test/plugins/head.js
+++ b/browser/base/content/test/plugins/head.js
@@ -8,20 +8,16 @@ ChromeUtils.defineModuleGetter(
   "resource://gre/modules/PlacesUtils.jsm"
 );
 ChromeUtils.defineModuleGetter(
   this,
   "PromiseUtils",
   "resource://gre/modules/PromiseUtils.jsm"
 );
 
-XPCOMUtils.defineLazyServiceGetters(this, {
-  uuidGen: ["@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"],
-});
-
 // Various tests in this directory may define gTestBrowser, to use as the
 // default browser under test in some of the functions below.
 /* global gTestBrowser:true */
 
 /**
  * Waits a specified number of miliseconds.
  *
  * Usage:
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -29,16 +29,18 @@ var gExceptionPaths = [
   // These chrome resources are referenced using relative paths from JS files.
   "chrome://global/content/certviewer/components/",
 
   // https://github.com/mozilla/activity-stream/issues/3053
   "chrome://activity-stream/content/data/content/tippytop/images/",
   "chrome://activity-stream/content/data/content/tippytop/favicons/",
   // These resources are referenced by messages delivered through Remote Settings
   "chrome://activity-stream/content/data/content/assets/remote/",
+  "chrome://browser/content/assets/moz-vpn.svg",
+  "chrome://browser/content/assets/vpn-logo.svg",
 
   // toolkit/components/pdfjs/content/build/pdf.js
   "resource://pdf.js/web/images/",
 
   // Exclude the form autofill path that has been moved out of the extensions to
   // toolkit, see bug 1691821.
   "resource://gre-resources/autofill/",
 
--- a/browser/base/content/test/tabs/browser_reload_deleted_file.js
+++ b/browser/base/content/test/tabs/browser_reload_deleted_file.js
@@ -1,14 +1,12 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 
-const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(
-  Ci.nsIUUIDGenerator
-);
+const uuidGenerator = Services.uuid;
 
 const DUMMY_FILE = "dummy_page.html";
 
 // Test for bug 1327942.
 add_task(async function() {
   // Copy dummy page to unique file in TmpD, so that we can safely delete it.
   let dummyPage = getChromeDir(getResolvedURI(gTestPath));
   dummyPage.append(DUMMY_FILE);
--- a/browser/base/content/test/tabs/browser_tab_label_picture_in_picture.js
+++ b/browser/base/content/test/tabs/browser_tab_label_picture_in_picture.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 add_task(async function test_pip_label_changes_tab() {
-  let newWin = await BrowserTestUtils.openNewWindowWithFlushedXULCacheForMozSupports();
+  let newWin = await BrowserTestUtils.openNewBrowserWindow();
 
   let pipTab = newWin.document.querySelector(".tabbrowser-tab[selected]");
   pipTab.setAttribute("pictureinpicture", true);
 
   let pipLabel = pipTab.querySelector(".tab-icon-sound-pip-label");
 
   await BrowserTestUtils.withNewTab("about:blank", async browser => {
     let selectedTab = newWin.document.querySelector(
--- a/browser/base/content/test/tabs/browser_tab_manager_visibility.js
+++ b/browser/base/content/test/tabs/browser_tab_manager_visibility.js
@@ -6,17 +6,17 @@
 
 // The hostname for the test URIs.
 const TEST_HOSTNAME = "https://example.com";
 const DUMMY_PAGE_PATH = "/browser/base/content/test/tabs/dummy_page.html";
 
 add_task(async function tab_manager_visibility_preference_on() {
   Services.prefs.setBoolPref("browser.tabs.tabmanager.enabled", true);
 
-  let newWindow = await BrowserTestUtils.openNewWindowWithFlushedXULCacheForMozSupports();
+  let newWindow = await BrowserTestUtils.openNewWindowWithFlushedCacheForMozSupports();
   await BrowserTestUtils.withNewTab(
     {
       gBrowser: newWindow.gBrowser,
       url: TEST_HOSTNAME + DUMMY_PAGE_PATH,
     },
     async function(browser) {
       await Assert.ok(
         BrowserTestUtils.is_visible(
@@ -28,17 +28,17 @@ add_task(async function tab_manager_visi
   );
   Services.prefs.clearUserPref("browser.tabs.tabmanager.enabled");
   BrowserTestUtils.closeWindow(newWindow);
 });
 
 add_task(async function tab_manager_visibility_preference_off() {
   Services.prefs.setBoolPref("browser.tabs.tabmanager.enabled", false);
 
-  let newWindow = await BrowserTestUtils.openNewWindowWithFlushedXULCacheForMozSupports();
+  let newWindow = await BrowserTestUtils.openNewWindowWithFlushedCacheForMozSupports();
   await BrowserTestUtils.withNewTab(
     {
       gBrowser: newWindow.gBrowser,
       url: TEST_HOSTNAME + DUMMY_PAGE_PATH,
     },
     async function(browser) {
       await Assert.ok(
         BrowserTestUtils.is_hidden(
--- a/browser/base/content/test/tabs/browser_undo_close_tabs.js
+++ b/browser/base/content/test/tabs/browser_undo_close_tabs.js
@@ -21,34 +21,41 @@ add_task(async function withMultiSelecte
   gBrowser.selectedTab = tab2;
   await triggerClickOn(tab4, { shiftKey: true });
 
   ok(!initialTab.multiselected, "InitialTab is not multiselected");
   ok(!tab1.multiselected, "Tab1 is not multiselected");
   ok(tab2.multiselected, "Tab2 is multiselected");
   ok(tab3.multiselected, "Tab3 is multiselected");
   ok(tab4.multiselected, "Tab4 is multiselected");
-  is(gBrowser.multiSelectedTabsCount, 3, "Two multiselected tabs");
+  is(gBrowser.multiSelectedTabsCount, 3, "Three multiselected tabs");
 
   gBrowser.removeMultiSelectedTabs();
   await TestUtils.waitForCondition(
-    () => gBrowser.tabs.length == 2,
-    "wait for the multiselected tabs to close"
+    () => SessionStore.getLastClosedTabCount(window) == 3,
+    "wait for the multi selected tabs to close in SessionStore"
   );
   is(
     SessionStore.getLastClosedTabCount(window),
     3,
     "SessionStore should know how many tabs were just closed"
   );
 
   undoCloseTab();
   await TestUtils.waitForCondition(
     () => gBrowser.tabs.length == 5,
     "wait for the tabs to reopen"
   );
+
+  is(
+    SessionStore.getLastClosedTabCount(window),
+    SessionStore.getClosedTabCount(window) ? 1 : 0,
+    "LastClosedTabCount should be reset"
+  );
+
   info("waiting for the browsers to finish loading");
   // Check that the tabs are restored in the correct order
   for (let tabId of [2, 3, 4]) {
     let browser = gBrowser.tabs[tabId].linkedBrowser;
     await ContentTask.spawn(browser, tabId, async aTabId => {
       await ContentTaskUtils.waitForCondition(() => {
         return (
           content?.document?.readyState == "complete" &&
@@ -56,16 +63,77 @@ add_task(async function withMultiSelecte
         );
       }, "waiting for tab " + aTabId + " to load");
     });
   }
 
   gBrowser.removeAllTabsBut(initialTab);
 });
 
+add_task(async function withBothGroupsAndTab() {
+  let initialTab = gBrowser.selectedTab;
+  let tab1 = await addTab("https://example.com/1");
+  let tab2 = await addTab("https://example.com/2");
+  let tab3 = await addTab("https://example.com/3");
+
+  gBrowser.selectedTab = tab2;
+  await triggerClickOn(tab3, { shiftKey: true });
+
+  ok(!initialTab.multiselected, "InitialTab is not multiselected");
+  ok(!tab1.multiselected, "Tab1 is not multiselected");
+  ok(tab2.multiselected, "Tab2 is multiselected");
+  ok(tab3.multiselected, "Tab3 is multiselected");
+  is(gBrowser.multiSelectedTabsCount, 2, "Two multiselected tabs");
+
+  gBrowser.removeMultiSelectedTabs();
+  await TestUtils.waitForCondition(
+    () => gBrowser.tabs.length == 2,
+    "wait for the multiselected tabs to close"
+  );
+
+  is(
+    SessionStore.getLastClosedTabCount(window),
+    2,
+    "SessionStore should know how many tabs were just closed"
+  );
+
+  let tab4 = await addTab("http://example.com/4");
+
+  is(
+    SessionStore.getLastClosedTabCount(window),
+    2,
+    "LastClosedTabCount should be the same"
+  );
+
+  gBrowser.removeTab(tab4);
+
+  await TestUtils.waitForCondition(
+    () => SessionStore.getLastClosedTabCount(window) == 1,
+    "wait for the tab to close in SessionStore"
+  );
+
+  let count = 3;
+  for (let i = 0; i < 3; i++) {
+    is(
+      SessionStore.getLastClosedTabCount(window),
+      1,
+      "LastClosedTabCount should be one"
+    );
+    undoCloseTab();
+
+    await TestUtils.waitForCondition(
+      () => gBrowser.tabs.length == count,
+      "wait for the tabs to reopen"
+    );
+    count++;
+  }
+
+  gBrowser.removeAllTabsBut(initialTab);
+});
+
 add_task(async function withCloseTabsToTheRight() {
   let initialTab = gBrowser.selectedTab;
   let tab1 = await addTab("https://example.com/1");
   await addTab("https://example.com/2");
   await addTab("https://example.com/3");
   await addTab("https://example.com/4");
 
   gBrowser.removeTabsToTheEndFrom(tab1);
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -12,17 +12,17 @@ prefs =
   privacy.webrtc.allowSilencingNotifications=true
   privacy.webrtc.legacyGlobalIndicator=false
   privacy.webrtc.sharedTabWarning=false
   privacy.webrtc.deviceGracePeriodTimeoutMs=0
 
 [browser_device_controls_menus.js]
 skip-if =
   debug # bug 1369731
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [browser_devices_get_user_media.js]
 https_first_disabled = true
 skip-if = (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_anim.js]
 https_first_disabled = true
 [browser_devices_get_user_media_by_device_id.js]
 https_first_disabled = true
@@ -30,74 +30,74 @@ https_first_disabled = true
 https_first_disabled = true
 [browser_devices_get_user_media_in_frame.js]
 https_first_disabled = true
 skip-if = debug # bug 1369731
 [browser_devices_get_user_media_in_xorigin_frame.js]
 https_first_disabled = true
 skip-if =
   debug # bug 1369731
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [browser_devices_get_user_media_in_xorigin_frame_chain.js]
 https_first_disabled = true
 [browser_devices_get_user_media_multi_process.js]
 https_first_disabled = true
 skip-if =
   (debug && os == "win") # bug 1393761
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [browser_devices_get_user_media_paused.js]
 https_first_disabled = true
 skip-if =
   (os == "win" && !debug) # Bug 1440900
   (os =="linux" && !debug && bits == 64) # Bug 1440900
-  apple_silicon  # timed out
+  apple_silicon  # bug 1707735
   apple_catalina # platform migration
 [browser_devices_get_user_media_screen.js]
 https_first_disabled = true
 skip-if =
   (os == 'linux') # Bug 1503991
-  apple_silicon  # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [browser_devices_get_user_media_screen_tab_close.js]
 skip-if =
   apple_catalina  # platform migration
-  apple_silicon
+  apple_silicon   # bug 1707735
 [browser_devices_get_user_media_tear_off_tab.js]
 https_first_disabled = true
 skip-if =
   apple_catalina  # platform migration
-  apple_silicon
+  apple_silicon   # bug 1707735
 [browser_devices_get_user_media_unprompted_access.js]
 https_first_disabled = true
 [browser_devices_get_user_media_unprompted_access_in_frame.js]
 https_first_disabled = true
 [browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
 https_first_disabled = true
 skip-if = (os == "win" && bits == 64) # win8: bug 1334752
 [browser_devices_get_user_media_unprompted_access_queue_request.js]
 https_first_disabled = true
 [browser_devices_select_audio_output.js]
 [browser_global_mute_toggles.js]
 [browser_indicator_popuphiding.js]
 skip-if =
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [browser_notification_silencing.js]
 skip-if =
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [browser_stop_sharing_button.js]
 skip-if =
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [browser_stop_streams_on_indicator_close.js]
 skip-if =
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [browser_tab_switch_warning.js]
 skip-if =
   apple_catalina  # platform migration
 [browser_webrtc_hooks.js]
 [browser_devices_get_user_media_queue_request.js]
 https_first_disabled = true
 [browser_WebrtcGlobalInformation.js]
--- a/browser/base/content/test/webrtc/legacyIndicator/browser.ini
+++ b/browser/base/content/test/webrtc/legacyIndicator/browser.ini
@@ -15,34 +15,34 @@ prefs =
 skip-if = (os == "linux" && debug) # linux: bug 976544
 [../browser_devices_get_user_media_anim.js]
 [../browser_devices_get_user_media_default_permissions.js]
 [../browser_devices_get_user_media_in_frame.js]
 skip-if = debug # bug 1369731
 [../browser_devices_get_user_media_in_xorigin_frame.js]
 skip-if =
   debug # bug 1369731
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [../browser_devices_get_user_media_in_xorigin_frame_chain.js]
 [../browser_devices_get_user_media_multi_process.js]
 skip-if =
   (debug && os == "win") # bug 1393761
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [../browser_devices_get_user_media_paused.js]
 skip-if =
   (os == "win" && !debug) # Bug 1440900
   (os =="linux" && !debug && bits == 64) # Bug 1440900
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [../browser_devices_get_user_media_screen.js]
 skip-if =
   (os == 'linux') # Bug 1503991
-  apple_silicon   # timed out
+  apple_silicon   # bug 1707735
   apple_catalina  # platform migration
 [../browser_devices_get_user_media_tear_off_tab.js]
 [../browser_devices_get_user_media_unprompted_access.js]
 [../browser_devices_get_user_media_unprompted_access_in_frame.js]
 [../browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
 skip-if = (os == "win" && bits == 64) # win8: bug 1334752
 [../browser_devices_get_user_media_unprompted_access_queue_request.js]
 [../browser_webrtc_hooks.js]
--- a/browser/base/content/titlebar-items.inc.xhtml
+++ b/browser/base/content/titlebar-items.inc.xhtml
@@ -3,21 +3,25 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 <hbox class="titlebar-buttonbox-container" skipintoolbarset="true">
   <hbox class="titlebar-buttonbox titlebar-color">
     <toolbarbutton class="titlebar-button titlebar-min"
                    oncommand="window.minimize();"
                    data-l10n-id="browser-window-minimize-button"
                    />
+    <!-- titlebar-max-btn is a special attribute used to identify the maximize
+         button elements programmatically -->
     <toolbarbutton class="titlebar-button titlebar-max"
+                   titlebar-max-btn=""
                    oncommand="window.maximize();"
                    data-l10n-id="browser-window-maximize-button"
                    />
     <toolbarbutton class="titlebar-button titlebar-restore"
+                   titlebar-max-btn=""
                    oncommand="window.restore();"
                    data-l10n-id="browser-window-restore-down-button"
                    />
     <toolbarbutton class="titlebar-button titlebar-close"
                    command="cmd_closeWindow"
                    data-l10n-id="browser-window-close-button"
                    />
   </hbox>
--- a/browser/base/content/webext-panels.xhtml
+++ b/browser/base/content/webext-panels.xhtml
@@ -7,18 +7,16 @@
 
 <?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/usercontext/usercontext.css" type="text/css"?>
 
 <!DOCTYPE window [
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
-<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd">
-%textcontextDTD;
 ]>
 
 <window id="webextpanels-window"
         xmlns:html="http://www.w3.org/1999/xhtml"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script src="chrome://global/content/contentAreaUtils.js"/>
   <script src="chrome://browser/content/browser.js"/>
   <script src="chrome://browser/content/browser-places.js"/>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -11,16 +11,17 @@ browser.jar:
         content/browser/aboutRestartRequired.js       (content/aboutRestartRequired.js)
         content/browser/aboutRestartRequired.xhtml    (content/aboutRestartRequired.xhtml)
         content/browser/aboutRobots.xhtml             (content/aboutRobots.xhtml)
         content/browser/aboutRobots.js                (content/aboutRobots.js)
         content/browser/aboutRobots.css               (content/aboutRobots.css)
         content/browser/logos/etp-mobile.svg          (content/logos/etp-mobile.svg)
         content/browser/logos/lockwise.svg            (content/logos/lockwise.svg)
         content/browser/logos/monitor.svg             (content/logos/monitor.svg)
+        content/browser/logos/vpn-promo-logo.svg      (content/logos/vpn-promo-logo.svg)
         content/browser/logos/proxy-light.svg         (content/logos/proxy-light.svg)
         content/browser/logos/proxy-dark.svg          (content/logos/proxy-dark.svg)
         content/browser/logos/send.svg                (content/logos/send.svg)
         content/browser/logos/tracking-protection.svg (content/logos/tracking-protection.svg)
         content/browser/logos/tracking-protection-dark-theme.svg (content/logos/tracking-protection-dark-theme.svg)
         content/browser/logos/vpn-dark.svg            (content/logos/vpn-dark.svg)
         content/browser/logos/vpn-light.svg           (content/logos/vpn-light.svg)
         content/browser/certerror/aboutNetErrorCodes.js (content/certerror/aboutNetErrorCodes.js)
@@ -99,15 +100,17 @@ browser.jar:
         content/browser/webrtcIndicator.js            (content/webrtcIndicator.js)
 # the following files are browser-specific overrides
 *       content/browser/license.html                  (/toolkit/content/license.html)
 % override chrome://global/content/license.html chrome://browser/content/license.html
         content/browser/blockedSite.xhtml               (content/blockedSite.xhtml)
         content/browser/blockedSite.js                  (content/blockedSite.js)
         content/browser/upgradeDialog.html            (content/upgradeDialog.html)
         content/browser/upgradeDialog.js              (content/upgradeDialog.js)
+        content/browser/spotlight.html                (content/spotlight.html)
+        content/browser/spotlight.js                  (content/spotlight.js)
 
 % override chrome://global/content/netError.xhtml chrome://browser/content/certerror/aboutNetError.xhtml
 
 # L10n resources and overrides.
 % override chrome://global/locale/appstrings.properties chrome://browser/locale/appstrings.properties
 % override chrome://global/locale/netError.dtd chrome://browser/locale/netError.dtd
 % override chrome://global/locale/app-extension-fields.properties chrome://browser/locale/app-extension-fields.properties
--- a/browser/components/BrowserContentHandler.jsm
+++ b/browser/components/BrowserContentHandler.jsm
@@ -819,17 +819,17 @@ nsBrowserContentHandler.prototype = {
 
   handleContent: function bch_handleContent(contentType, context, request) {
     const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001;
 
     try {
       var webNavInfo = Cc["@mozilla.org/webnavigation-info;1"].getService(
         Ci.nsIWebNavigationInfo
       );
-      if (!webNavInfo.isTypeSupported(contentType, null)) {
+      if (!webNavInfo.isTypeSupported(contentType)) {
         throw NS_ERROR_WONT_HANDLE_CONTENT;
       }
     } catch (e) {
       throw NS_ERROR_WONT_HANDLE_CONTENT;
     }
 
     request.QueryInterface(Ci.nsIChannel);
     handURIToExistingBrowser(
@@ -921,23 +921,22 @@ nsDefaultCommandLineHandler.prototype = 
 
   _haveProfile: false,
 
   /* nsICommandLineHandler */
   handle: function dch_handle(cmdLine) {
     var urilist = [];
 
     if (
-      AppConstants.platform == "macosx" &&
       cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
-      Services.startup.wasSilentlyRestarted
+      Services.startup.wasSilentlyStarted
     ) {
-      // If we are starting up after a silent restart, don't open a window.
-      // We also need to make sure that the application doesn't immediately
-      // exit, so stay in a LastWindowClosingSurvivalArea until a window opens.
+      // If we are starting up in silent mode, don't open a window. We also need
+      // to make sure that the application doesn't immediately exit, so stay in
+      // a LastWindowClosingSurvivalArea until a window opens.
       Services.startup.enterLastWindowClosingSurvivalArea();
       Services.obs.addObserver(function windowOpenObserver() {
         Services.startup.exitLastWindowClosingSurvivalArea();
         Services.obs.removeObserver(windowOpenObserver, "domwindowopened");
       }, "domwindowopened");
       return;
     }
 
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -77,16 +77,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   RFPHelper: "resource://gre/modules/RFPHelper.jsm",
   SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
   Sanitizer: "resource:///modules/Sanitizer.jsm",
   SaveToPocket: "chrome://pocket/content/SaveToPocket.jsm",
   ScreenshotsUtils: "resource:///modules/ScreenshotsUtils.jsm",
   SearchSERPTelemetry: "resource:///modules/SearchSERPTelemetry.jsm",
   SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
   SessionStore: "resource:///modules/sessionstore/SessionStore.jsm",
+  ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
   TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
   TabUnloader: "resource:///modules/TabUnloader.jsm",
   TelemetryUtils: "resource://gre/modules/TelemetryUtils.jsm",
   TRRRacer: "resource:///modules/TRRPerformance.jsm",
   UIState: "resource://services-sync/UIState.jsm",
   UrlbarQuickSuggest: "resource:///modules/UrlbarQuickSuggest.jsm",
   UrlbarPrefs: "resource:///modules/UrlbarPrefs.jsm",
   WebChannel: "resource://gre/modules/WebChannel.jsm",
@@ -302,17 +303,19 @@ let JSWINDOWACTORS = {
     parent: {
       moduleURI: "resource:///actors/AboutReaderParent.jsm",
     },
     child: {
       moduleURI: "resource:///actors/AboutReaderChild.jsm",
       events: {
         DOMContentLoaded: {},
         pageshow: { mozSystemGroup: true },
-        pagehide: { mozSystemGroup: true },
+        // Don't try to create the actor if only the pagehide event fires.
+        // This can happen with the initial about:blank documents.
+        pagehide: { mozSystemGroup: true, createActor: false },
       },
     },
     messageManagerGroups: ["browsers"],
   },
 
   AboutTabCrashed: {
     parent: {
       moduleURI: "resource:///actors/AboutTabCrashedParent.jsm",
@@ -748,16 +751,17 @@ let JSWINDOWACTORS = {
     allFrames: true,
   },
 };
 
 (function earlyBlankFirstPaint() {
   let startTime = Cu.now();
   if (
     AppConstants.platform == "macosx" ||
+    Services.startup.wasSilentlyStarted ||
     !Services.prefs.getBoolPref("browser.startup.blankWindow", false)
   ) {
     return;
   }
 
   // Until bug 1450626 and bug 1488384 are fixed, skip the blank window when
   // using a non-default theme.
   if (
@@ -1309,16 +1313,20 @@ BrowserGlue.prototype = {
       "network.cookie.cookieBehavior",
       this._matchCBCategory
     );
     Services.prefs.removeObserver(
       "network.cookie.cookieBehavior.pbmode",
       this._matchCBCategory
     );
     Services.prefs.removeObserver(
+      "network.http.referer.disallowCrossSiteRelaxingDefault",
+      this._matchCBCategory
+    );
+    Services.prefs.removeObserver(
       ContentBlockingCategoriesPrefs.PREF_CB_CATEGORY,
       this._updateCBCategory
     );
     Services.prefs.removeObserver(
       "privacy.trackingprotection",
       this._setPrefExpectations
     );
     Services.prefs.removeObserver(
@@ -1633,16 +1641,18 @@ BrowserGlue.prototype = {
   // the first browser window has finished initializing
   _onFirstWindowLoaded: function BG__onFirstWindowLoaded(aWindow) {
     AboutNewTab.init();
 
     TabCrashHandler.init();
 
     ProcessHangMonitor.init();
 
+    UrlbarPrefs.maybeEnableOfflineQuickSuggest();
+
     // A channel for "remote troubleshooting" code...
     let channel = new WebChannel(
       "remote-troubleshooting",
       "remote-troubleshooting"
     );
     channel.listen((id, data, target) => {
       if (data.command == "request") {
         let { Troubleshoot } = ChromeUtils.import(
@@ -1748,16 +1758,20 @@ BrowserGlue.prototype = {
       "network.cookie.cookieBehavior",
       this._matchCBCategory
     );
     Services.prefs.addObserver(
       "network.cookie.cookieBehavior.pbmode",
       this._matchCBCategory
     );
     Services.prefs.addObserver(
+      "network.http.referer.disallowCrossSiteRelaxingDefault",
+      this._matchCBCategory
+    );
+    Services.prefs.addObserver(
       ContentBlockingCategoriesPrefs.PREF_CB_CATEGORY,
       this._updateCBCategory
     );
     Services.prefs.addObserver(
       "media.autoplay.default",
       this._updateAutoplayPref
     );
     Services.prefs.addObserver(
@@ -1929,54 +1943,67 @@ BrowserGlue.prototype = {
       exceptions
     );
   },
 
   /**
    * Application shutdown handler.
    */
   _onQuitApplicationGranted() {
-    // This pref must be set here because SessionStore will use its value
-    // on quit-application.
-    this._setPrefToSaveSession();
-
-    // Call trackStartupCrashEnd here in case the delayed call on startup hasn't
-    // yet occurred (see trackStartupCrashEnd caller in browser.js).
-    try {
-      Services.startup.trackStartupCrashEnd();
-    } catch (e) {
-      Cu.reportError(
-        "Could not end startup crash tracking in quit-application-granted: " + e
-      );
-    }
-
-    if (this._bookmarksBackupIdleTime) {
-      this._userIdleService.removeIdleObserver(
-        this,
-        this._bookmarksBackupIdleTime
-      );
-      delete this._bookmarksBackupIdleTime;
-    }
-
-    for (let mod of Object.values(initializedModules)) {
-      if (mod.uninit) {
-        mod.uninit();
+    let tasks = [
+      // This pref must be set here because SessionStore will use its value
+      // on quit-application.
+      () => this._setPrefToSaveSession(),
+
+      // Call trackStartupCrashEnd here in case the delayed call on startup hasn't
+      // yet occurred (see trackStartupCrashEnd caller in browser.js).
+      () => Services.startup.trackStartupCrashEnd(),
+
+      () => {
+        if (this._bookmarksBackupIdleTime) {
+          this._userIdleService.removeIdleObserver(
+            this,
+            this._bookmarksBackupIdleTime
+          );
+          delete this._bookmarksBackupIdleTime;
+        }
+      },
+
+      () => BrowserUsageTelemetry.uninit(),
+      () => SearchSERPTelemetry.uninit(),
+      () => Interactions.uninit(),
+      () => PageDataService.uninit(),
+      () => PageThumbs.uninit(),
+      () => NewTabUtils.uninit(),
+      () => Normandy.uninit(),
+      () => RFPHelper.uninit(),
+      () => ASRouterNewTabHook.destroy(),
+    ];
+
+    tasks.push(
+      ...Object.values(initializedModules)
+        .filter(m => m.uninit)
+        .map(m => () => m.uninit())
+    );
+
+    for (let task of tasks) {
+      try {
+        task();
+      } catch (ex) {
+        console.error(`Error during quit-application-granted: ${ex}`);
+        if (Cu.isInAutomation) {
+          // This usually happens after the test harness is done collecting
+          // test errors, thus we can't easily add a failure to it. The only
+          // noticeable solution we have is crashing.
+          Cc["@mozilla.org/xpcom/debug;1"]
+            .getService(Ci.nsIDebug2)
+            .abort(ex.filename, ex.lineNumber);
+        }
       }
     }
-
-    BrowserUsageTelemetry.uninit();
-    SearchSERPTelemetry.uninit();
-    Interactions.uninit();
-    PageDataService.uninit();
-    PageThumbs.uninit();
-    NewTabUtils.uninit();
-
-    Normandy.uninit();
-    RFPHelper.uninit();
-    ASRouterNewTabHook.destroy();
   },
 
   // Set up a listener to enable/disable the screenshots extension
   // based on its preference.
   _monitorScreenshotsPref() {
     const PREF = "extensions.screenshots.disabled";
     const ID = "screenshots@mozilla.org";
     const _checkScreenshotsPref = async () => {
@@ -2797,16 +2824,21 @@ BrowserGlue.prototype = {
     // aQuitType == "lastwindow" is overloaded. "lastwindow" is used to indicate
     // "the last window is closing but we're not quitting (a non-browser window is open)"
     // and also "we're quitting by closing the last window".
 
     if (aQuitType == "restart" || aQuitType == "os-restart") {
       return;
     }
 
+    // browser.warnOnQuit is a hidden global boolean to override all quit prompts.
+    if (!Services.prefs.getBoolPref("browser.warnOnQuit")) {
+      return;
+    }
+
     var windowcount = 0;
     var pagecount = 0;
     let pinnedcount = 0;
     for (let win of BrowserWindowTracker.orderedWindows) {
       if (win.closed) {
         continue;
       }
       windowcount++;
@@ -2815,133 +2847,145 @@ BrowserGlue.prototype = {
         pinnedcount += tabbrowser._numPinnedTabs;
         pagecount +=
           tabbrowser.browsers.length -
           tabbrowser._numPinnedTabs -
           tabbrowser._removingTabs.length;
       }
     }
 
-    if (pagecount < 2) {
+    // No windows open so no need for a warning.
+    if (!windowcount) {
+      return;
+    }
+
+    // browser.warnOnQuitShortcut is checked when quitting using the shortcut key.
+    // The warning will appear even when only one window/tab is open. For other
+    // methods of quitting, the warning only appears when there is more than one
+    // window or tab open.
+    if (this._quitSource == "shortcut") {
+      if (!Services.prefs.getBoolPref("browser.warnOnQuitShortcut")) {
+        return;
+      }
+    } else if (
+      pagecount < 2 ||
+      !Services.prefs.getBoolPref("browser.tabs.warnOnClose")
+    ) {
       return;
     }
 
     if (!aQuitType) {
       aQuitType = "quit";
     }
 
-    // browser.warnOnQuit is a hidden global boolean to override all quit prompts
-    if (!Services.prefs.getBoolPref("browser.warnOnQuit")) {
-      return;
-    }
-
-    // If we're going to automatically restore the session, only warn if the user asked for that.
-    let sessionWillBeRestored =
-      Services.prefs.getIntPref("browser.startup.page") == 3 ||
-      Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
-    // In the sessionWillBeRestored case, we only check the sessionstore-specific pref:
-    if (sessionWillBeRestored) {
-      if (
-        !Services.prefs.getBoolPref("browser.sessionstore.warnOnQuit", false)
-      ) {
-        return;
-      }
-      // Otherwise, we check browser.tabs.warnOnClose
-    } else if (!Services.prefs.getBoolPref("browser.tabs.warnOnClose")) {
-      return;
-    }
-
     let win = BrowserWindowTracker.getTopWindow();
 
     // Our prompt for quitting is most important, so replace others.
     win.gDialogBox.replaceDialogIfOpen();
 
-    let warningMessage;
+    let title, buttonLabel;
     // More than 1 window. Compose our own message.
     if (windowcount > 1) {
-      let tabSubstring = gTabbrowserBundle.GetStringFromName(
-        "tabs.closeWarningMultipleWindowsTabSnippet"
-      );
-      tabSubstring = PluralForm.get(pagecount, tabSubstring).replace(
-        /#1/,
-        pagecount
+      title = gTabbrowserBundle.GetStringFromName("tabs.closeWindowsTitle");
+      title = PluralForm.get(windowcount, title).replace(/#1/, windowcount);
+
+      buttonLabel =
+        AppConstants.platform == "win"
+          ? "tabs.closeWindowsButtonWin"
+          : "tabs.closeWindowsButton";
+      buttonLabel = gTabbrowserBundle.GetStringFromName(buttonLabel);
+    } else {
+      title = gTabbrowserBundle.GetStringFromName("tabs.closeTabsTitle");
+      title = PluralForm.get(pagecount, title).replace("#1", pagecount);
+
+      if (this._quitSource == "shortcut") {
+        let productName = gBrandBundle.GetStringFromName("brandShorterName");
+        title = gTabbrowserBundle.formatStringFromName(
+          "tabs.closeTabsWithKeyTitle",
+          [productName]
+        );
+        buttonLabel = gTabbrowserBundle.formatStringFromName(
+          "tabs.closeTabsWithKeyButton",
+          [productName]
+        );
+      } else {
+        title = gTabbrowserBundle.GetStringFromName("tabs.closeTabsTitle");
+        title = PluralForm.get(pagecount, title).replace("#1", pagecount);
+        buttonLabel = gTabbrowserBundle.GetStringFromName(
+          "tabs.closeButtonMultiple"
+        );
+      }
+    }
+
+    // The checkbox label is different depending on whether the shortcut
+    // was used to quit or not.
+    let checkboxLabel;
+    if (this._quitSource == "shortcut") {
+      let quitKeyElement = win.document.getElementById("key_quitApplication");
+      let quitKey = ShortcutUtils.prettifyShortcut(quitKeyElement);
+
+      checkboxLabel = gTabbrowserBundle.formatStringFromName(
+        "tabs.closeTabsWithKeyConfirmCheckbox",
+        [quitKey]
       );
-
-      let stringID = sessionWillBeRestored
-        ? "tabs.closeWarningMultipleWindowsSessionRestore3"
-        : "tabs.closeWarningMultipleWindows2";
-      let windowString = gTabbrowserBundle.GetStringFromName(stringID);
-      windowString = PluralForm.get(windowcount, windowString).replace(
-        /#1/,
-        windowcount
-      );
-      warningMessage = windowString.replace(/%(?:1\$)?S/i, tabSubstring);
     } else {
-      let stringID = sessionWillBeRestored
-        ? "tabs.closeWarningMultipleTabsSessionRestore"
-        : "tabs.closeWarningMultipleTabs";
-      warningMessage = gTabbrowserBundle.GetStringFromName(stringID);
-      warningMessage = PluralForm.get(pagecount, warningMessage).replace(
-        "#1",
-        pagecount
+      checkboxLabel = gTabbrowserBundle.GetStringFromName(
+        "tabs.closeTabsConfirmCheckbox"
       );
     }
 
     let warnOnClose = { value: true };
-    let titleId =
-      AppConstants.platform == "win"
-        ? "tabs.closeTabsAndQuitTitleWin"
-        : "tabs.closeTabsAndQuitTitle";
     let flags =
       Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
       Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1;
-    // Only display the checkbox in the non-sessionrestore case.
-    let checkboxLabel = !sessionWillBeRestored
-      ? gTabbrowserBundle.GetStringFromName("tabs.closeWarningPrompt")
-      : null;
-
     // buttonPressed will be 0 for closing, 1 for cancel (don't close/quit)
     let buttonPressed = Services.prompt.confirmEx(
       win,
-      gTabbrowserBundle.GetStringFromName(titleId),
-      warningMessage,
+      title,
+      null,
       flags,
-      gTabbrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
+      buttonLabel,
       null,
       null,
       checkboxLabel,
       warnOnClose
     );
     Services.telemetry.setEventRecordingEnabled("close_tab_warning", true);
     let warnCheckbox = warnOnClose.value ? "checked" : "unchecked";
-    if (!checkboxLabel) {
-      warnCheckbox = "not-present";
-    }
+
+    let sessionWillBeRestored =
+      Services.prefs.getIntPref("browser.startup.page") == 3 ||
+      Services.prefs.getBoolPref("browser.sessionstore.resume_session_once");
     Services.telemetry.recordEvent(
       "close_tab_warning",
       "shown",
       "application",
       null,
       {
         source: this._quitSource,
         button: buttonPressed == 0 ? "close" : "cancel",
         warn_checkbox: warnCheckbox,
         closing_wins: "" + windowcount,
         closing_tabs: "" + (pagecount + pinnedcount),
         will_restore: sessionWillBeRestored ? "yes" : "no",
       }
     );
 
-    this._quitSource = "unknown";
-
     // If the user has unticked the box, and has confirmed closing, stop showing
     // the warning.
-    if (!sessionWillBeRestored && buttonPressed == 0 && !warnOnClose.value) {
-      Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
-    }
+    if (buttonPressed == 0 && !warnOnClose.value) {
+      if (this._quitSource == "shortcut") {
+        Services.prefs.setBoolPref("browser.warnOnQuitShortcut", false);
+      } else {
+        Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
+      }
+    }
+
+    this._quitSource = "unknown";
+
     aCancelQuit.data = buttonPressed != 0;
   },
 
   /**
    * Initialize Places
    * - imports the bookmarks html file if bookmarks database is empty, try to
    *   restore bookmarks from a JSON backup if the backend indicates that the
    *   database was corrupt.
@@ -4320,26 +4364,28 @@ var ContentBlockingCategoriesPrefs = {
         "network.cookie.cookieBehavior": null,
         "network.cookie.cookieBehavior.pbmode": null,
         "privacy.trackingprotection.pbmode.enabled": null,
         "privacy.trackingprotection.enabled": null,
         "privacy.trackingprotection.socialtracking.enabled": null,
         "privacy.trackingprotection.fingerprinting.enabled": null,
         "privacy.trackingprotection.cryptomining.enabled": null,
         "privacy.annotate_channels.strict_list.enabled": null,
+        "network.http.referer.disallowCrossSiteRelaxingDefault": null,
       },
       standard: {
         "network.cookie.cookieBehavior": null,
         "network.cookie.cookieBehavior.pbmode": null,
         "privacy.trackingprotection.pbmode.enabled": null,
         "privacy.trackingprotection.enabled": null,
         "privacy.trackingprotection.socialtracking.enabled": null,
         "privacy.trackingprotection.fingerprinting.enabled": null,
         "privacy.trackingprotection.cryptomining.enabled": null,
         "privacy.annotate_channels.strict_list.enabled": null,
+        "network.http.referer.disallowCrossSiteRelaxingDefault": null,
       },
     };
     let type = "strict";
     let rulesArray = Services.prefs
       .getStringPref(this.PREF_STRICT_DEF)
       .split(",");
     for (let item of rulesArray) {
       switch (item) {
@@ -4398,16 +4444,26 @@ var ContentBlockingCategoriesPrefs = {
             "privacy.annotate_channels.strict_list.enabled"
           ] = true;
           break;
         case "-lvl2":
           this.CATEGORY_PREFS[type][
             "privacy.annotate_channels.strict_list.enabled"
           ] = false;
           break;
+        case "rp":
+          this.CATEGORY_PREFS[type][
+            "network.http.referer.disallowCrossSiteRelaxingDefault"
+          ] = true;
+          break;
+        case "-rp":
+          this.CATEGORY_PREFS[type][
+            "network.http.referer.disallowCrossSiteRelaxingDefault"
+          ] = false;
+          break;
         case "cookieBehavior0":
           this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
             Ci.nsICookieService.BEHAVIOR_ACCEPT;
           break;
         case "cookieBehavior1":
           this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] =
             Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
           break;
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -51,16 +51,18 @@ static const RedirEntry kRedirMap[] = {
     {"blocked", "chrome://browser/content/blockedSite.xhtml",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
          nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     {"certerror", "chrome://browser/content/certerror/aboutNetError.xhtml",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
          nsIAboutModule::HIDE_FROM_ABOUTABOUT},
+    {"unloads", "chrome://browser/content/tabunloader/aboutUnloads.html",
+     nsIAboutModule::ALLOW_SCRIPT},
     {"framecrashed", "chrome://browser/content/aboutFrameCrashed.html",
      nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
          nsIAboutModule::HIDE_FROM_ABOUTABOUT},
     {"logins", "chrome://browser/content/aboutlogins/aboutLogins.html",
      nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
          nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS |
          nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT},
     {"loginsimportreport",
--- a/browser/components/about/components.conf
+++ b/browser/components/about/components.conf
@@ -23,16 +23,17 @@ pages = [
     'protections',
     'profiling',
     'reader',
     'restartrequired',
     'rights',
     'robots',
     'sessionrestore',
     'tabcrashed',
+    'unloads',
     'welcome',
     'welcomeback',
 ]
 
 Classes = [
     {
         'cid': '{7e4bb6ad-2fc4-4dc6-89ef-23e8e5ccf980}',
         'contract_ids': ['@mozilla.org/network/protocol/about;1?what=%s' % page
--- a/browser/components/about/test/unit/xpcshell.ini
+++ b/browser/components/about/test/unit/xpcshell.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
+skip-if = toolkit == 'android' # bug 1730213
 head =
 # make the firefox services (eg newtab-service) available to xpcshell
 firefox-appdir = browser
 
 [test_getURIFlags.js]
--- a/browser/components/aboutlogins/content/components/generic-dialog.css
+++ b/browser/components/aboutlogins/content/components/generic-dialog.css
@@ -43,17 +43,17 @@
   font-weight: 300;
   user-select: none;
   margin: 0;
 }
 
 ::slotted([slot="content"]) {
   grid-column-start: 2;
   align-self: baseline;
-  padding-top: 10px;
+  margin-top: 16px;
   line-height: 1.8em;
 }
 
 ::slotted([slot="buttons"]) {
   grid-column: 1 / 4;
   grid-row-start: 3;
   display: flex;
   justify-content: flex-end;
--- a/browser/components/aboutlogins/tests/browser/browser.ini
+++ b/browser/components/aboutlogins/tests/browser/browser.ini
@@ -34,16 +34,18 @@ skip-if =
 [browser_openExport.js]
 [browser_openFiltered.js]
 [browser_openImport.js]
 skip-if = (os != "win" && os != "mac") # import is only available on Windows and macOS
 [browser_openImportCSV.js]
 [browser_openPreferences.js]
 [browser_openPreferencesExternal.js]
 [browser_openSite.js]
+skip-if =
+  os == "linux" && bits == 64 # Bug 1581889
 [browser_osAuthDialog.js]
 skip-if = (os == 'linux') # bug 1527745
 [browser_removeAllDialog.js]
 [browser_sessionRestore.js]
 skip-if = tsan || debug # Bug 1576876
 [browser_tabKeyNav.js]
 [browser_updateLogin.js]
 [browser_vulnerableLoginAddedInSecondaryWindow.js]
--- a/browser/components/aboutlogins/tests/unit/xpcshell.ini
+++ b/browser/components/aboutlogins/tests/unit/xpcshell.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
+skip-if = toolkit == 'android' # bug 1730213
 head = head.js
 firefox-appdir = browser
 
 [test_getPotentialBreachesByLoginGUID.js]
 tags = remote-settings
--- a/browser/components/attribution/test/browser/browser_AttributionCode_Mac_telemetry.js
+++ b/browser/components/attribution/test/browser/browser_AttributionCode_Mac_telemetry.js
@@ -193,18 +193,17 @@ add_task(async function test_broken_refe
     "https://example.com?content=content",
     true
   );
 
   // This uses macOS internals to change the GUID so that it will look like the
   // application has quarantine data but nothing will be pressent in the
   // quarantine database.  This shouldn't happen in the wild.
   function generateQuarantineGUID() {
-    let str = Cc["@mozilla.org/uuid-generator;1"]
-      .getService(Ci.nsIUUIDGenerator)
+    let str = Services.uuid
       .generateUUID()
       .toString()
       .toUpperCase();
     // Strip {}.
     return str.substring(1, str.length - 1);
   }
 
   // These magic constants are macOS GateKeeper flags.
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -36,17 +36,17 @@ tags = openwindow
 [browser_windowOpen.js]
 tags = openwindow
 [browser_serviceworkers.js]
 [browser_broadcastchannel.js]
 [browser_blobUrl.js]
 [browser_middleClick.js]
 skip-if =
     (verify && debug && (os == 'linux'))
-    apple_silicon
+    apple_silicon # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
 [browser_imageCache.js]
 skip-if = (verify && debug && (os == 'win'))
 [browser_count_and_remove.js]
 [browser_relatedTab.js]
 [browser_saveLink.js]
 skip-if = (verify && !debug && (os == 'win'))
 support-files =
   saveLink.sjs
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -26,23 +26,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
 });
 
 XPCOMUtils.defineLazyGetter(this, "gWidgetsBundle", function() {
   const kUrl =
     "chrome://browser/locale/customizableui/customizableWidgets.properties";
   return Services.strings.createBundle(kUrl);
 });
 
-XPCOMUtils.defineLazyServiceGetter(
-  this,
-  "gELS",
-  "@mozilla.org/eventlistenerservice;1",
-  "nsIEventListenerService"
-);
-
 XPCOMUtils.defineLazyPreferenceGetter(
   this,
   "gBookmarksToolbar2h2020",
   "browser.toolbars.bookmarks.2h2020",
   false
 );
 
 const kDefaultThemeID = "default-theme@mozilla.org";
@@ -1183,28 +1176,28 @@ var CustomizableUIInternal = {
         this.notifyListeners("onAreaReset", aArea, container);
       }
     } finally {
       this.endBatchUpdate();
     }
   },
 
   addPanelCloseListeners(aPanel) {
-    gELS.addSystemEventListener(aPanel, "click", this, false);
-    gELS.addSystemEventListener(aPanel, "keypress", this, false);
+    Services.els.addSystemEventListener(aPanel, "click", this, false);
+    Services.els.addSystemEventListener(aPanel, "keypress", this, false);
     let win = aPanel.ownerGlobal;
     if (!gPanelsForWindow.has(win)) {
       gPanelsForWindow.set(win, new Set());
     }
     gPanelsForWindow.get(win).add(this._getPanelForNode(aPanel));
   },
 
   removePanelCloseListeners(aPanel) {
-    gELS.removeSystemEventListener(aPanel, "click", this, false);
-    gELS.removeSystemEventListener(aPanel, "keypress", this, false);
+    Services.els.removeSystemEventListener(aPanel, "click", this, false);
+    Services.els.removeSystemEventListener(aPanel, "keypress", this, false);
     let win = aPanel.ownerGlobal;
     let panels = gPanelsForWindow.get(win);
     if (panels) {
       panels.delete(this._getPanelForNode(aPanel));
     }
   },
 
   ensureButtonContextMenu(aNode, aAreaNode, forcePanel) {
@@ -5085,17 +5078,17 @@ OverflowableToolbar.prototype = {
     }
     return new Promise(resolve => {
       let doc = this._panel.ownerDocument;
       this._panel.hidden = false;
       let multiview = this._panel.querySelector("panelmultiview");
       let mainViewId = multiview.getAttribute("mainViewId");
       let mainView = doc.getElementById(mainViewId);
       let contextMenu = doc.getElementById(mainView.getAttribute("context"));
-      gELS.addSystemEventListener(contextMenu, "command", this, true);
+      Services.els.addSystemEventListener(contextMenu, "command", this, true);
       let anchor = this._chevron.icon;
 
       let popupshown = false;
       this._panel.addEventListener(
         "popupshown",
         () => {
           popupshown = true;
           this._panel.addEventListener("dragover", this);
@@ -5165,17 +5158,22 @@ OverflowableToolbar.prototype = {
     this._chevron.open = false;
     this._panel.removeEventListener("dragover", this);
     this._panel.removeEventListener("dragend", this);
     let doc = aEvent.target.ownerDocument;
     doc.defaultView.updateEditUIVisibility();
     let contextMenuId = this._panel.getAttribute("context");
     if (contextMenuId) {
       let contextMenu = doc.getElementById(contextMenuId);
-      gELS.removeSystemEventListener(contextMenu, "command", this, true);
+      Services.els.removeSystemEventListener(
+        contextMenu,
+        "command",
+        this,
+        true
+      );
     }
   },
 
   /**
    * Returns an array with two elements, the first one a boolean representing
    * whether we're overflowing, the second one a number representing the
    * maximum width items may occupy so we don't overflow.
    */
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -228,18 +228,18 @@ const CustomizableWidgets = [
       }
       panelview.appendChild(body);
       panelview.appendChild(separator);
       panelview.appendChild(footer);
     },
   },
   {
     id: "save-page-button",
+    l10nId: "toolbar-button-save-page",
     shortcutId: "key_savePage",
-    tooltiptext: "save-page-button.tooltiptext3",
     onCommand(aEvent) {
       let win = aEvent.target.ownerGlobal;
       win.saveBrowser(win.gBrowser.selectedBrowser);
     },
   },
   {
     id: "find-button",
     shortcutId: "key_find",
@@ -248,18 +248,18 @@ const CustomizableWidgets = [
       let win = aEvent.target.ownerGlobal;
       if (win.gLazyFindCommand) {
         win.gLazyFindCommand("onFindCommand");
       }
     },
   },
   {
     id: "open-file-button",
+    l10nId: "toolbar-button-open-file",
     shortcutId: "openFileKb",
-    tooltiptext: "open-file-button.tooltiptext3",
     onCommand(aEvent) {
       let win = aEvent.target.ownerGlobal;
       win.BrowserOpenFileWindow();
     },
   },
   {
     id: "sidebar-button",
     tooltiptext: "sidebar-button.tooltiptext2",
@@ -433,29 +433,28 @@ const CustomizableWidgets = [
     id: "characterencoding-button",
     l10nId: "repair-text-encoding-button",
     onCommand(aEvent) {
       aEvent.view.BrowserForceEncodingDetection();
     },
   },
   {
     id: "email-link-button",
-    tooltiptext: "email-link-button.tooltiptext3",
+    l10nId: "toolbar-button-email-link",
     onCommand(aEvent) {
       let win = aEvent.view;
       win.MailIntegration.sendLinkForBrowser(win.gBrowser.selectedBrowser);
     },
   },
 ];
 
 if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
   CustomizableWidgets.push({
     id: "sync-button",
-    label: "remotetabs-panelmenu.label",
-    tooltiptext: "remotetabs-panelmenu.tooltiptext2",
+    l10nId: "toolbar-button-synced-tabs",
     type: "view",
     viewId: "PanelUI-remotetabs",
     onViewShowing(aEvent) {
       let panelview = aEvent.target;
       let doc = panelview.ownerDocument;
 
       let syncNowBtn = panelview.querySelector(".syncnow-label");
       let l10nId = syncNowBtn.getAttribute(
@@ -601,15 +600,16 @@ if (Services.prefs.getBoolPref("privacy.
       forgetButton.removeEventListener("command", this);
     },
   });
 }
 
 if (PrivateBrowsingUtils.enabled) {
   CustomizableWidgets.push({
     id: "privatebrowsing-button",
+    l10nId: "toolbar-button-new-private-window",
     shortcutId: "key_privatebrowsing",
     onCommand(e) {
       let win = e.target.ownerGlobal;
       win.OpenBrowserWindow({ private: true });
     },
   });
 }
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -458,16 +458,21 @@ const PanelUI = {
     if (container) {
       container.showSubView(aViewId, aAnchor);
     } else if (!aAnchor.open) {
       aAnchor.open = true;
 
       let tempPanel = document.createXULElement("panel");
       tempPanel.setAttribute("type", "arrow");
       tempPanel.setAttribute("id", "customizationui-widget-panel");
+
+      if (viewNode.getAttribute("remote") == "true") {
+        tempPanel.setAttribute("remote", "true");
+      }
+
       tempPanel.setAttribute("class", "cui-widget-panel panel-no-padding");
       tempPanel.setAttribute("viewId", aViewId);
       if (aAnchor.getAttribute("tabspecific")) {
         tempPanel.setAttribute("tabspecific", true);
       }
       if (aAnchor.getAttribute("locationspecific")) {
         tempPanel.setAttribute("locationspecific", true);
       }
--- a/browser/components/doh/TRRPerformance.jsm
+++ b/browser/components/doh/TRRPerformance.jsm
@@ -46,23 +46,16 @@ XPCOMUtils.defineLazyServiceGetter(
 
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "gDNSService",
   "@mozilla.org/network/dns-service;1",
   "nsIDNSService"
 );
 
-XPCOMUtils.defineLazyServiceGetter(
-  this,
-  "gUUIDGenerator",
-  "@mozilla.org/uuid-generator;1",
-  "nsIUUIDGenerator"
-);
-
 // The canonical domain whose subdomains we will be resolving.
 XPCOMUtils.defineLazyPreferenceGetter(
   this,
   "kCanonicalDomain",
   "doh-rollout.trrRace.canonicalDomain",
   "firefox-dns-perf-test.net."
 );
 
@@ -89,17 +82,17 @@ XPCOMUtils.defineLazyPreferenceGetter(
           "youtube.com.",
           "amazon.com.",
           "facebook.com.",
           "yahoo.com.",
         ]
 );
 
 function getRandomSubdomain() {
-  let uuid = gUUIDGenerator
+  let uuid = Services.uuid
     .generateUUID()
     .toString()
     .slice(1, -1); // Discard surrounding braces
   return `${uuid}.${kCanonicalDomain}`;
 }
 
 // A wrapper around async DNS lookups. The results are passed on to the supplied
 // callback. The wrapper attempts the lookup 3 times before passing on a failure.
--- a/browser/components/doh/test/unit/xpcshell.ini
+++ b/browser/components/doh/test/unit/xpcshell.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = toolkit == 'android' # bug 1730213
 head = head.js
 firefox-appdir = browser
 support-files =
   ../../../../../netwerk/test/unit/http2-ca.pem
 
 [test_heuristics.js]
 [test_DNSLookup.js]
 skip-if = debug # Bug 1617845
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -314,16 +314,27 @@ var DownloadsCommon = {
     }
     return DownloadsCommon.DOWNLOAD_NOTSTARTED;
   },
 
   /**
    * Removes a Download object from both session and history downloads.
    */
   async deleteDownload(download) {
+    // Check hasBlockedData to avoid double counting if you click the X button
+    // in the Libarary view and then delete the download from the history.
+    if (
+      download.error?.becauseBlockedByReputationCheck &&
+      download.hasBlockedData
+    ) {
+      Services.telemetry
+        .getKeyedHistogramById("DOWNLOADS_USER_ACTION_ON_BLOCKED_DOWNLOAD")
+        .add(download.error.reputationCheckVerdict, 1); // confirm block
+    }
+
     // Remove the associated history element first, if any, so that the views
     // that combine history and session downloads won't resurrect the history
     // download into the view just before it is deleted permanently.
     try {
       await PlacesUtils.history.remove(download.source.url);
     } catch (ex) {
       Cu.reportError(ex);
     }
--- a/browser/components/downloads/DownloadsViewUI.jsm
+++ b/browser/components/downloads/DownloadsViewUI.jsm
@@ -279,37 +279,32 @@ DownloadsViewUI.DownloadElementShell.pro
     return !!this._active;
   },
 
   connect() {
     let document = this.element.ownerDocument;
     let downloadListItemFragment = gDownloadListItemFragments.get(document);
     // When changing the markup within the fragment, please ensure that
     // the functions within DownloadsView still operate correctly.
-    // E.g. onDownloadClick() relies on brittle logic and performs/prevents
-    // actions based on the check if originaltarget was not a button.
     if (!downloadListItemFragment) {
       let MozXULElement = document.defaultView.MozXULElement;
       downloadListItemFragment = MozXULElement.parseXULToFragment(`
         <hbox class="downloadMainArea" flex="1" align="center">
-          <stack>
-            <image class="downloadTypeIcon" validate="always"/>
-            <image class="downloadBlockedBadge" />
-          </stack>
+          <image class="downloadTypeIcon" validate="always"/>
           <vbox class="downloadContainer" flex="1" pack="center">
             <description class="downloadTarget" crop="center"/>
             <description class="downloadDetails downloadDetailsNormal"
                          crop="end"/>
             <description class="downloadDetails downloadDetailsHover"
                          crop="end"/>
             <description class="downloadDetails downloadDetailsButtonHover"
                          crop="end"/>
           </vbox>
+          <image class="downloadBlockedBadge" />
         </hbox>
-        <image class="downloadBlockedBadgeNew" />
         <button class="downloadButton"/>
       `);
       gDownloadListItemFragments.set(document, downloadListItemFragment);
     }
     this.element.setAttribute("active", true);
     this.element.setAttribute("orient", "horizontal");
     this.element.addEventListener("click", ev => {
       ev.target.ownerGlobal.DownloadsView.onDownloadClick(ev);
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -86,16 +86,16 @@
 #downloadsPanel-multiView > .panel-viewcontainer,
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack {
   max-width: unset;
 }
 
 #downloadsPanel-blockedSubview,
 #downloadsPanel-mainView {
   font: caption;
-  width: 35em;
+  min-width: 37em;
   padding: 0.62em;
 }
 
 #downloadsHistory,
 #downloadsFooterButtons {
   margin: 0;
 }
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -814,18 +814,18 @@ var DownloadsView = {
     }
     this._visibleViewItems.delete(download);
     this._itemsForElements.delete(element);
   },
 
   // User interface event functions
 
   onDownloadClick(aEvent) {
-    // Handle primary clicks only, and exclude the action button.
-    if (aEvent.button == 0 && aEvent.originalTarget.localName != "button") {
+    // Handle primary clicks in the main area only:
+    if (aEvent.button == 0 && aEvent.target.closest(".downloadMainArea")) {
       let target = aEvent.target;
       while (target.nodeName != "richlistitem") {
         target = target.parentNode;
       }
       let download = DownloadsView.itemForElement(target).download;
       if (download.succeeded) {
         download._launchedFromPanel = true;
       }
@@ -910,16 +910,21 @@ var DownloadsView = {
     if (item.localName != "richlistitem") {
       return;
     }
 
     if (aEvent.target.classList.contains("downloadButton")) {
       item.classList.add("downloadHoveringButton");
     }
 
+    item.classList.toggle(
+      "hoveringMainArea",
+      aEvent.target.closest(".downloadMainArea")
+    );
+
     if (!this.contextMenuOpen && !this.subViewOpen) {
       this.richListBox.selectedItem = item;
     }
   },
 
   onDownloadMouseOut(aEvent) {
     let item = aEvent.target.closest("richlistitem,richlistbox");
     if (item.localName != "richlistitem") {
--- a/browser/components/downloads/content/downloadsPanel.inc.xhtml
+++ b/browser/components/downloads/content/downloadsPanel.inc.xhtml
@@ -112,17 +112,17 @@
   </menupopup>
 
   <panelmultiview id="downloadsPanel-multiView"
                   mainViewId="downloadsPanel-mainView">
 
     <panelview id="downloadsPanel-mainView">
       <vbox class="panel-view-body-unscrollable">
         <richlistbox id="downloadsListBox"
-                     data-l10n-id="downloads-panel-list"
+                     data-l10n-id="downloads-panel-items"
                      data-l10n-attrs="style"
                      context="downloadsContextMenu"
                      onmouseover="DownloadsView.onDownloadMouseOver(event);"
                      onmouseout="DownloadsView.onDownloadMouseOut(event);"
                      oncontextmenu="DownloadsView.onDownloadContextMenu(event);"
                      ondragstart="DownloadsView.onDownloadDragStart(event);"/>
         <description id="emptyDownloads"
                      data-l10n-id="downloads-panel-empty"/>
--- a/browser/components/downloads/test/browser/browser.ini
+++ b/browser/components/downloads/test/browser/browser.ini
@@ -26,15 +26,18 @@ skip-if = os == "linux" # Bug 952422
 [browser_libraryDrop.js]
 skip-if = (os == 'win' && os_version == '10.0' && ccov) # Bug 1306510
 [browser_library_clearall.js]
 [browser_download_opens_on_click.js]
 [browser_download_is_clickable.js]
 [browser_downloads_panel_block.js]
 skip-if = true # Bug 1352792
 [browser_downloads_panel_context_menu.js]
+skip-if =
+  os == "win" && os_version == "10.0" && bits == 64 && !debug # Bug 1719949
+  win10_2004 && bits == 32 && debug # Bug 1727925
 [browser_downloads_panel_ctrl_click.js]
 [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]
--- a/browser/components/downloads/test/browser/browser_download_is_clickable.js
+++ b/browser/components/downloads/test/browser/browser_download_is_clickable.js
@@ -49,24 +49,24 @@ add_task(async function test_download_cl
     listbox.itemChildren[0].classList.contains("openWhenFinished"),
     "Download should have clickable style when in progress"
   );
 
   ok(!download.launchWhenSucceeded, "launchWhenSucceeded should set to false");
 
   ok(!download._launchedFromPanel, "LaunchFromPanel should set to false");
 
-  listbox.itemChildren[0].click();
+  EventUtils.synthesizeMouseAtCenter(listbox.itemChildren[0], {});
   ok(
     download.launchWhenSucceeded,
     "Should open the file when download is finished"
   );
   ok(download._launchedFromPanel, "File was scheduled to launch from panel");
 
-  listbox.itemChildren[0].click();
+  EventUtils.synthesizeMouseAtCenter(listbox.itemChildren[0], {});
 
   ok(
     !download.launchWhenSucceeded,
     "Should NOT open the file when download is finished"
   );
 
   ok(!download._launchedFromPanel, "File launch from panel was reset");
 
--- a/browser/components/downloads/test/browser/browser_download_opens_on_click.js
+++ b/browser/components/downloads/test/browser/browser_download_opens_on_click.js
@@ -70,17 +70,17 @@ add_task(async function test_download_op
     listbox.itemChildren[0].classList.contains("openWhenFinished"),
     "Download should have clickable style when in progress"
   );
 
   ok(!download.launchWhenSucceeded, "launchWhenSucceeded should set to false");
 
   ok(!download._launchedFromPanel, "LaunchFromPanel should set to false");
 
-  listbox.itemChildren[0].click();
+  EventUtils.synthesizeMouseAtCenter(listbox.itemChildren[0], {});
 
   ok(
     download.launchWhenSucceeded,
     "Should open the file when download is finished"
   );
   ok(download._launchedFromPanel, "File was scheduled to launch from panel");
 
   continueResponses();
--- a/browser/components/downloads/test/browser/browser_download_overwrite.js
+++ b/browser/components/downloads/test/browser/browser_download_overwrite.js
@@ -18,24 +18,28 @@ Services.scriptloader.loadSubScript(
 );
 
 add_task(async function setup() {
   // head.js has helpers that write to a nice unique file we can use.
   await createDownloadedFile(gTestTargetFile.path, "Hello.\n");
   ok(gTestTargetFile.exists(), "We created a test file.");
 
   await SpecialPowers.pushPrefEnv({
-    set: [["browser.download.useDownloadDir", false]],
+    set: [
+      ["browser.download.improvements_to_download_panel", true],
+      ["browser.download.useDownloadDir", false],
+    ],
   });
   // Set up the file picker.
   let destDir = gTestTargetFile.parent;
 
   MockFilePicker.displayDirectory = destDir;
   MockFilePicker.showCallback = function(fp) {
     MockFilePicker.setFiles([gTestTargetFile]);
+    return MockFilePicker.returnOK;
   };
   registerCleanupFunction(function() {
     MockFilePicker.cleanup();
     if (gTestTargetFile.exists()) {
       gTestTargetFile.remove(false);
     }
   });
 });
@@ -52,28 +56,36 @@ add_task(async function test_overwrite_d
 
   registerCleanupFunction(function() {
     if (!unregisteredTransfer) {
       mockTransferRegisterer.unregister();
     }
   });
 
   let dialogPromise = BrowserTestUtils.domWindowOpenedAndLoaded();
+
   // Now try and download a thing to the file:
   await BrowserTestUtils.withNewTab(TEST_ROOT + "foo.txt", async function() {
-    let dialog = await dialogPromise;
-    info("Got dialog.");
-    let saveEl = dialog.document.getElementById("save");
-    dialog.document.getElementById("mode").selectedItem = saveEl;
-    // Allow accepting the dialog (to avoid the delay helper):
-    dialog.document
-      .getElementById("unknownContentType")
-      .getButton("accept").disabled = false;
-    // Then accept it:
-    dialog.document.querySelector("dialog").acceptDialog();
+    if (
+      !Services.prefs.getBoolPref(
+        "browser.download.improvements_to_download_panel"
+      )
+    ) {
+      let dialog = await dialogPromise;
+      info("Got dialog.");
+      let saveEl = dialog.document.getElementById("save");
+      dialog.document.getElementById("mode").selectedItem = saveEl;
+      // Allow accepting the dialog (to avoid the delay helper):
+      dialog.document
+        .getElementById("unknownContentType")
+        .getButton("accept").disabled = false;
+      // Then accept it:
+      dialog.document.querySelector("dialog").acceptDialog();
+    }
+
     ok(await transferCompletePromise, "download should succeed");
     ok(
       gTestTargetFile.exists(),
       "File should still exist and not have been deleted."
     );
     // Note: the download transfer is fake so data won't have been written to
     // the file, so we can't verify that the download actually overwrites data
     // like this.
@@ -82,42 +94,49 @@ add_task(async function test_overwrite_d
   });
 });
 
 // If we download a file and the user accepts overwriting an existing one,
 // we should successfully overwrite its contents.
 add_task(async function test_overwrite_works() {
   let dialogPromise = BrowserTestUtils.domWindowOpenedAndLoaded();
   let publicDownloads = await Downloads.getList(Downloads.PUBLIC);
+  // First ensure we catch the download finishing.
+  let downloadFinishedPromise = new Promise(resolve => {
+    publicDownloads.addView({
+      onDownloadChanged(download) {
+        info("Download changed!");
+        if (download.succeeded || download.error) {
+          info("Download succeeded or errored");
+          publicDownloads.removeView(this);
+          publicDownloads.removeFinished();
+          resolve(download);
+        }
+      },
+    });
+  });
   // Now try and download a thing to the file:
   await BrowserTestUtils.withNewTab(TEST_ROOT + "foo.txt", async function() {
-    let dialog = await dialogPromise;
-    info("Got dialog.");
-    // First ensure we catch the download finishing.
-    let downloadFinishedPromise = new Promise(resolve => {
-      publicDownloads.addView({
-        onDownloadChanged(download) {
-          info("Download changed!");
-          if (download.succeeded || download.error) {
-            info("Download succeeded or errored");
-            publicDownloads.removeView(this);
-            publicDownloads.removeFinished();
-            resolve(download);
-          }
-        },
-      });
-    });
-    let saveEl = dialog.document.getElementById("save");
-    dialog.document.getElementById("mode").selectedItem = saveEl;
-    // Allow accepting the dialog (to avoid the delay helper):
-    dialog.document
-      .getElementById("unknownContentType")
-      .getButton("accept").disabled = false;
-    // Then accept it:
-    dialog.document.querySelector("dialog").acceptDialog();
+    if (
+      !Services.prefs.getBoolPref(
+        "browser.download.improvements_to_download_panel"
+      )
+    ) {
+      let dialog = await dialogPromise;
+      info("Got dialog.");
+      let saveEl = dialog.document.getElementById("save");
+      dialog.document.getElementById("mode").selectedItem = saveEl;
+      // Allow accepting the dialog (to avoid the delay helper):
+      dialog.document
+        .getElementById("unknownContentType")
+        .getButton("accept").disabled = false;
+      // Then accept it:
+      dialog.document.querySelector("dialog").acceptDialog();
+    }
+
     info("wait for download to finish");
     let download = await downloadFinishedPromise;
     ok(download.succeeded, "Download should succeed");
     ok(
       gTestTargetFile.exists(),
       "File should still exist and not have been deleted."
     );
     let contents = new TextDecoder().decode(
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -44,22 +44,31 @@ add_task(async function test_first_downl
   // If we got here, that means the panel opened.
   DownloadsPanel.hidePanel();
 
   ok(
     DownloadsCommon.getData(window).panelHasShownBefore,
     "Should have recorded that the panel was opened on a download."
   );
 
-  // Next, make sure that if we start another download, we don't open the
-  // panel automatically.
-  let originalOnPopupShown = DownloadsPanel.onPopupShown;
-  DownloadsPanel.onPopupShown = function() {
-    originalOnPopupShown.apply(this, arguments);
-    ok(false, "Should not have opened the downloads panel.");
-  };
+  // If browser.download.improvements_to_download_panel is enabled, this will fail
+  // because we always open a downloads panel as long as there is no other simulatenous
+  // download. So, first ensure that this pref is already false.
+  if (
+    !SpecialPowers.getBoolPref(
+      "browser.download.improvements_to_download_panel"
+    )
+  ) {
+    // Next, make sure that if we start another download, we don't open the
+    // panel automatically.
+    let originalOnPopupShown = DownloadsPanel.onPopupShown;
+    DownloadsPanel.onPopupShown = function() {
+      originalOnPopupShown.apply(this, arguments);
+      ok(false, "Should not have opened the downloads panel.");
+    };
 
-  DownloadsCommon.getData(window)._notifyDownloadEvent("start");
+    DownloadsCommon.getData(window)._notifyDownloadEvent("start");
 
-  // Wait 2 seconds to ensure that the panel does not open.
-  await new Promise(resolve => setTimeout(resolve, 2000));
-  DownloadsPanel.onPopupShown = originalOnPopupShown;
+    // Wait 2 seconds to ensure that the panel does not open.
+    await new Promise(resolve => setTimeout(resolve, 2000));
+    DownloadsPanel.onPopupShown = originalOnPopupShown;
+  }
 });
--- a/browser/components/downloads/test/browser/browser_image_mimetype_issues.js
+++ b/browser/components/downloads/test/browser/browser_image_mimetype_issues.js
@@ -51,16 +51,22 @@ add_task(async function test_save_image_
     }
   );
 });
 
 /**
  * Test with the "save link as" context menu.
  */
 add_task(async function test_save_link_webp_with_jpeg_extension() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.download.improvements_to_download_panel", true],
+      ["browser.download.useDownloadDir", false],
+    ],
+  });
   await BrowserTestUtils.withNewTab(
     `data:text/html,<a href="${TEST_ROOT}/not-really-a-jpeg.jpeg?convert=webp">Nice image</a>`,
     async browser => {
       let menu = document.getElementById("contentAreaContextMenu");
       let popupShown = BrowserTestUtils.waitForEvent(menu, "popupshown");
       BrowserTestUtils.synthesizeMouse(
         "a[href]",
         5,
--- a/browser/components/downloads/test/unit/xpcshell.ini
+++ b/browser/components/downloads/test/unit/xpcshell.ini
@@ -1,9 +1,9 @@
 [DEFAULT]
 head = head.js
 firefox-appdir = browser
-skip-if = toolkit == 'android'
+skip-if = toolkit == 'android' # bug 1730213
 
 
 [test_DownloadsCommon_getMimeInfo.js]
 [test_DownloadsCommon_isFileOfType.js]
 [test_DownloadsViewableInternally.js]
--- a/browser/components/enterprisepolicies/tests/browser/disable_app_update/browser_policy_disable_app_update.js
+++ b/browser/components/enterprisepolicies/tests/browser/disable_app_update/browser_policy_disable_app_update.js
@@ -43,19 +43,17 @@ add_task(async function test_update_pref
   BrowserTestUtils.removeTab(tab);
 });
 
 add_task(async function test_update_about_ui() {
   let aboutDialog = await waitForAboutDialog();
   let panelId = "policyDisabled";
 
   await BrowserTestUtils.waitForCondition(
-    () =>
-      aboutDialog.gAppUpdater.selectedPanel &&
-      aboutDialog.gAppUpdater.selectedPanel.id == panelId,
+    () => aboutDialog.gAppUpdater?.selectedPanel?.id == panelId,
     'Waiting for expected panel ID - expected "' + panelId + '"'
   );
   is(
     aboutDialog.gAppUpdater.selectedPanel.id,
     panelId,
     "The About Dialog panel Id should equal " + panelId
   );
 
--- a/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.ini
+++ b/browser/components/enterprisepolicies/tests/xpcshell/xpcshell.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = toolkit == 'android' # bug 1730213
 firefox-appdir = browser
 head = head.js
 support-files =
   policytest_v0.1.xpi
 
 [test_3rdparty.js]
 [test_addon_update.js]
 [test_appupdateurl.js]
--- a/browser/components/extensions/parent/.eslintrc.js
+++ b/browser/components/extensions/parent/.eslintrc.js
@@ -21,14 +21,13 @@ module.exports = {
     isPrivateCookieStoreId: true,
     isValidCookieStoreId: true,
     makeWidgetId: true,
     openOptionsPage: true,
     pageActionFor: true,
     replaceUrlInTab: true,
     searchInitialized: true,
     sidebarActionFor: true,
-    tabGetSender: true,
     tabTracker: true,
     waitForTabLoaded: true,
     windowTracker: true,
   },
 };
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -39,44 +39,16 @@ const READER_MODE_PREFIX = "about:reader
 
 let tabTracker;
 let windowTracker;
 
 function isPrivateTab(nativeTab) {
   return PrivateBrowsingUtils.isBrowserPrivate(nativeTab.linkedBrowser);
 }
 
-// This function is pretty tightly tied to Extension.jsm.
-// Its job is to fill in the |tab| property of the sender.
-const getSender = (extension, target, sender) => {
-  let tabId;
-  if ("tabId" in sender) {
-    // The message came from a privileged extension page running in a tab. In
-    // that case, it should include a tabId property (which is filled in by the
-    // page-open listener below).
-    tabId = sender.tabId;
-    delete sender.tabId;
-  } else if (
-    ExtensionCommon.instanceOf(target, "XULFrameElement") ||
-    ExtensionCommon.instanceOf(target, "HTMLIFrameElement")
-  ) {
-    tabId = tabTracker.getBrowserData(target).tabId;
-  }
-
-  if (tabId) {
-    let tab = extension.tabManager.get(tabId, null);
-    if (tab) {
-      sender.tab = tab.convert();
-    }
-  }
-};
-
-// Used by Extension.jsm
-global.tabGetSender = getSender;
-
 /* eslint-disable mozilla/balanced-listeners */
 extensions.on("uninstalling", (msg, extension) => {
   if (extension.uninstallURL) {
     let browser = windowTracker.topWindow.gBrowser;
     browser.addTab(extension.uninstallURL, {
       disallowInheritPrincipal: true,
       relatedToCurrent: true,
       triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
@@ -388,16 +360,20 @@ class TabTracker extends TabTrackerBase 
     }
     return -1;
   }
 
   setId(nativeTab, id) {
     if (!nativeTab.parentNode) {
       throw new Error("Cannot attach ID to a destroyed tab.");
     }
+    if (nativeTab.ownerGlobal.closed) {
+      throw new Error("Cannot attach ID to a tab in a closed window.");
+    }
+
     this._tabs.set(nativeTab, id);
     if (nativeTab.linkedBrowser) {
       this._browsers.set(nativeTab.linkedBrowser, id);
     }
     this._tabIds.set(id, nativeTab);
   }
 
   /**
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -100,17 +100,17 @@ support-files = !/browser/components/pla
 [browser_ext_contextMenus_checkboxes.js]
 [browser_ext_contextMenus_commands.js]
 [browser_ext_contextMenus_icons.js]
 [browser_ext_contextMenus_onclick.js]
 https_first_disabled = true
 [browser_ext_contextMenus_radioGroups.js]
 [browser_ext_contextMenus_targetUrlPatterns.js]
 skip-if =
-  apple_silicon
+  apple_silicon # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
 [browser_ext_contextMenus_uninstall.js]
 [browser_ext_contextMenus_urlPatterns.js]
 [browser_ext_currentWindow.js]
 [browser_ext_devtools_inspectedWindow.js]
 [browser_ext_devtools_inspectedWindow_eval_bindings.js]
 [browser_ext_devtools_inspectedWindow_eval_file.js]
 [browser_ext_devtools_inspectedWindow_reload.js]
 [browser_ext_devtools_inspectedWindow_targetSwitch.js]
@@ -129,17 +129,18 @@ support-files =
 [browser_ext_find.js]
 https_first_disabled = true
 skip-if = (verify && (os == 'linux' || os == 'mac'))
 [browser_ext_getViews.js]
 [browser_ext_history_redirect.js]
 [browser_ext_identity_indication.js]
 [browser_ext_incognito_views.js]
 skip-if =
-  apple_silicon && !fission
+  apple_silicon && !fission # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
+  win10_2004 && bits == 32 && debug # Bug 1727925
 [browser_ext_incognito_popup.js]
 [browser_ext_lastError.js]
 [browser_ext_management.js]
 [browser_ext_menus.js]
 https_first_disabled = true
 [browser_ext_menus_accesskey.js]
 [browser_ext_menus_activeTab.js]
 [browser_ext_menus_capture_secondary_click.js]
@@ -151,22 +152,22 @@ https_first_disabled = true
 [browser_ext_menus_refresh.js]
 [browser_ext_menus_replace_menu.js]
 [browser_ext_menus_replace_menu_context.js]
 https_first_disabled = true
 [browser_ext_menus_replace_menu_permissions.js]
 [browser_ext_menus_targetElement.js]
 [browser_ext_menus_targetElement_extension.js]
 skip-if =
-  apple_silicon
+  apple_silicon # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
 [browser_ext_menus_targetElement_shadow.js]
 [browser_ext_menus_visible.js]
 [browser_ext_menus_viewType.js]
 skip-if =
-  apple_silicon
+  apple_silicon # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
 [browser_ext_mousewheel_zoom.js]
 [browser_ext_nontab_process_switch.js]
 https_first_disabled = true
 [browser_ext_omnibox.js]
 [browser_ext_openPanel.js]
 skip-if =
   (verify && !debug && (os == 'linux' || os == 'mac'))
 [browser_ext_optionsPage_browser_style.js]
@@ -226,17 +227,18 @@ https_first_disabled = true
 skip-if = debug # Bug 1394984 disable debug builds on all platforms
 [browser_ext_settings_overrides_default_search.js]
 [browser_ext_sidebarAction.js]
 [browser_ext_sidebarAction_browser_style.js]
 [browser_ext_sidebarAction_click.js]
 [browser_ext_sidebarAction_context.js]
 [browser_ext_sidebarAction_contextMenu.js]
 skip-if =
-  apple_silicon
+  apple_silicon # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
+  win10_2004 && bits == 32 && debug && !fission # Bug 1727925
 [browser_ext_sidebarAction_httpAuth.js]
 support-files =
   authenticate.sjs
 [browser_ext_sidebarAction_incognito.js]
 skip-if = true # Bug 1575369
 [browser_ext_sidebarAction_runtime.js]
 [browser_ext_sidebarAction_tabs.js]
 [browser_ext_sidebarAction_windows.js]
--- a/browser/components/extensions/test/browser/browser_ext_incognito_views.js
+++ b/browser/components/extensions/test/browser/browser_ext_incognito_views.js
@@ -77,18 +77,16 @@ add_task(async function testIncognitoVie
         {
           let window = await browser.windows.create({
             incognito: true,
             url: URL,
           });
           await windowReady;
 
           await testWindow(window);
-
-          await browser.windows.remove(window.id);
         }
 
         browser.test.notifyPass("incognito-views");
       } catch (error) {
         browser.test.fail(`Error: ${error} :: ${error.stack}`);
         browser.test.notifyFail("incognito-views");
       }
     },
@@ -158,25 +156,29 @@ add_task(async function testIncognitoVie
         }
 
         let win = await browser.windows.getCurrent();
         browser.runtime.sendMessage({
           message: "popup-details",
           windowId: win.id,
           incognito: browser.extension.inIncognitoContext,
         });
-
-        window.close();
       },
     },
   });
 
+  let win;
+  let promiseBrowserActionOpened;
   extension.onMessage("click-browserAction", () => {
-    clickBrowserAction(
-      extension,
-      Services.wm.getMostRecentWindow("navigator:browser")
-    );
+    win = Services.wm.getMostRecentWindow("navigator:browser");
+    promiseBrowserActionOpened = openBrowserActionPanel(extension, win, true);
   });
 
   await extension.startup();
   await extension.awaitFinish("incognito-views");
+  // Prevent intermittent failures of this test in optimized builds due to a race between
+  // opening/closing the browserAction and closing the related window at the end
+  // of the test (e.g. Bug 1707305).
+  await promiseBrowserActionOpened;
+  await closeBrowserAction(extension, win);
   await extension.unload();
+  await BrowserTestUtils.closeWindow(win);
 });
--- a/browser/components/extensions/test/browser/browser_ext_nontab_process_switch.js
+++ b/browser/components/extensions/test/browser/browser_ext_nontab_process_switch.js
@@ -65,17 +65,14 @@ add_task(async function process_switch_i
   isnot(popup.pid, cs2.pid, "Navigating to example.com changed process");
 
   let popup2 = await extension.awaitMessage("extension_page");
   is(popup2.place, "?popup_back", "Back at extension page in popup");
   is(popup.pid, popup2.pid, "Same process as original popup page");
 
   is(sidebar.pid, popup.pid, "Sidebar and popup pages from the same process");
 
-  // Before Fission, there's no guarantee that two (independent) pages
-  // from the same domain will end up in the same process.
-  if (Services.appinfo.fissionAutostart) {
-    is(cs1.pid, cs2.pid, "Both example.com CSs from the same process");
-  }
+  // There's no guarantee that two (independent) pages from the same domain will
+  // end up in the same process.
 
   await closeBrowserAction(extension);
   await extension.unload();
 });
--- a/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js
+++ b/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js
@@ -91,8 +91,45 @@ add_task(async function test_popup_sendM
     pong = await extension.awaitMessage("popup-ping-response");
     is(pong, "popup-pong", "Got pong");
 
     await closePageAction(extension);
   }
 
   await extension.unload();
 });
+
+add_task(async function test_popup_close_then_sendMessage() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      browser_action: {
+        default_popup: "popup.html",
+      },
+    },
+
+    files: {
+      "popup.html": `<meta charset="utf-8"><script src="popup.js" defer></script>ghost`,
+      "popup.js"() {
+        browser.tabs.query({ active: true }).then(() => {
+          // NOTE: the message will be sent _after_ the popup is closed below.
+          browser.runtime.sendMessage("sent-after-closed");
+        });
+        window.close();
+      },
+    },
+
+    async background() {
+      browser.runtime.onMessage.addListener(msg => {
+        browser.test.assertEq(msg, "sent-after-closed", "Message from popup.");
+        browser.test.sendMessage("done");
+      });
+      browser.test.sendMessage("ready");
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("ready");
+
+  clickBrowserAction(extension);
+  await extension.awaitMessage("done");
+
+  await extension.unload();
+});
--- a/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated_filter.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated_filter.js
@@ -267,20 +267,22 @@ add_task(async function test_filter_isAr
         },
         { properties: ["isArticle"] }
       );
     },
   });
   await extension.startup();
   let ok = extension.awaitFinish("isArticle");
 
-  let tab = await BrowserTestUtils.openNewForegroundTab(
-    gBrowser,
+  const baseUrl = getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
     "http://mochi.test:8888/"
   );
+  const url = `${baseUrl}/readerModeArticle.html`;
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
   await ok;
 
   await extension.unload();
 
   await BrowserTestUtils.removeTab(tab);
 });
 
 add_task(async function test_filter_property() {
--- a/browser/components/extensions/test/mochitest/mochitest.ini
+++ b/browser/components/extensions/test/mochitest/mochitest.ini
@@ -1,7 +1,8 @@
 [DEFAULT]
+skip-if = toolkit == 'android' # bug 1730213
 support-files =
   ../../../../../toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
   ../../../../../toolkit/components/extensions/test/mochitest/file_sample.html
 tags = webextensions
 
 [test_ext_all_apis.html]
--- a/browser/components/extensions/test/xpcshell/test_ext_browsingData_passwords.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_browsingData_passwords.js
@@ -1,49 +1,42 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-XPCOMUtils.defineLazyServiceGetter(
-  this,
-  "loginManager",
-  "@mozilla.org/login-manager;1",
-  "nsILoginManager"
-);
-
 const REFERENCE_DATE = Date.now();
 const LOGIN_USERNAME = "username";
 const LOGIN_PASSWORD = "password";
 const OLD_HOST = "http://mozilla.org";
 const NEW_HOST = "http://mozilla.com";
 const FXA_HOST = "chrome://FirefoxAccounts";
 
 function checkLoginExists(host, shouldExist) {
-  let logins = loginManager.findLogins(host, "", null);
+  let logins = Services.logins.findLogins(host, "", null);
   equal(
     logins.length,
     shouldExist ? 1 : 0,
     `Login was ${shouldExist ? "" : "not "} found.`
   );
 }
 
 function addLogin(host, timestamp) {
   checkLoginExists(host, false);
   let login = Cc["@mozilla.org/login-manager/loginInfo;1"].createInstance(
     Ci.nsILoginInfo
   );
   login.init(host, "", null, LOGIN_USERNAME, LOGIN_PASSWORD);
   login.QueryInterface(Ci.nsILoginMetaInfo);
   login.timePasswordChanged = timestamp;
-  loginManager.addLogin(login);
+  Services.logins.addLogin(login);
   checkLoginExists(host, true);
 }
 
 async function setupPasswords() {
-  loginManager.removeAllUserFacingLogins();
+  Services.logins.removeAllUserFacingLogins();
   addLogin(FXA_HOST, REFERENCE_DATE);
   addLogin(NEW_HOST, REFERENCE_DATE);
   addLogin(OLD_HOST, REFERENCE_DATE - 10000);
 }
 
 add_task(async function testPasswords() {
   function background() {
     browser.test.onMessage.addListener(async (msg, options) => {
--- a/browser/components/extensions/test/xpcshell/xpcshell.ini
+++ b/browser/components/extensions/test/xpcshell/xpcshell.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = toolkit == 'android' # bug 1730213
 head = head.js
 firefox-appdir = browser
 tags = webextensions
 dupe-manifest =
 
 [test_ext_bookmarks.js]
 [test_ext_browsingData_downloads.js]
 [test_ext_browsingData_passwords.js]
--- a/browser/components/ion/content/ion.js
+++ b/browser/components/ion/content/ion.js
@@ -397,20 +397,17 @@ async function updateStudy(studyAddonId)
     study.style.opacity = 0.5;
     joinBtn.disabled = true;
   }
 }
 
 // equivalent to what we use for Telemetry IDs
 // https://searchfox.org/mozilla-central/rev/9193635dca8cfdcb68f114306194ffc860456044/toolkit/components/telemetry/app/TelemetryUtils.jsm#222
 function generateUUID() {
-  let str = Cc["@mozilla.org/uuid-generator;1"]
-    .getService(Ci.nsIUUIDGenerator)
-    .generateUUID()
-    .toString();
+  let str = Services.uuid.generateUUID().toString();
   return str.substring(1, str.length - 1);
 }
 
 async function setup(cachedAddons) {
   document
     .getElementById("enrollment-button")
     .addEventListener("click", async () => {
       const ionId = Services.prefs.getStringPref(PREF_ION_ID, null);
--- a/browser/components/migration/tests/unit/xpcshell.ini
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -1,12 +1,12 @@
 [DEFAULT]
 head = head_migration.js
 firefox-appdir = browser
-skip-if = toolkit == 'android'
+skip-if = toolkit == 'android' # bug 1730213
 prefs =
   browser.migrate.showBookmarksToolbarAfterMigration=true
 support-files =
   Library/**
   AppData/**
 
 [test_360se_bookmarks.js]
 skip-if = os != "win"
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -49,16 +49,17 @@ DIRS += [
     "protections",
     "protocolhandler",
     "resistfingerprinting",
     "screenshots",
     "search",
     "sessionstore",
     "shell",
     "syncedtabs",
+    "tabunloader",
     "uitour",
     "urlbar",
     "translation",
 ]
 
 DIRS += ["build"]
 
 if CONFIG["NIGHTLY_BUILD"]:
--- a/browser/components/newtab/AboutNewTabService.jsm
+++ b/browser/components/newtab/AboutNewTabService.jsm
@@ -438,22 +438,18 @@ class BaseAboutNewTabService {
 
   get welcomeURL() {
     /*
      * Returns the about:welcome URL
      *
      * This is calculated in the same way the default URL is.
      */
 
-    if (
-      NimbusFeatures.aboutwelcome.isEnabled({
-        defaultValue: true,
-        sendExposureEvent: true,
-      })
-    ) {
+    NimbusFeatures.aboutwelcome.recordExposureEvent({ once: true });
+    if (NimbusFeatures.aboutwelcome.isEnabled({ defaultValue: true })) {
       return ABOUT_WELCOME_URL;
     }
     return this.defaultURL;
   }
 
   aboutHomeChannel(uri, loadInfo) {
     throw Components.Exception(
       "AboutHomeChannel not implemented for this process.",
--- a/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm
+++ b/browser/components/newtab/aboutwelcome/AboutWelcomeParent.jsm
@@ -10,17 +10,16 @@ const { Services } = ChromeUtils.import(
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   FxAccounts: "resource://gre/modules/FxAccounts.jsm",
   MigrationUtils: "resource:///modules/MigrationUtils.jsm",
-  OS: "resource://gre/modules/osfile.jsm",
   SpecialMessageActions:
     "resource://messaging-system/lib/SpecialMessageActions.jsm",
   AboutWelcomeTelemetry:
     "resource://activity-stream/aboutwelcome/lib/AboutWelcomeTelemetry.jsm",
   AboutWelcomeDefaults:
     "resource://activity-stream/aboutwelcome/lib/AboutWelcomeDefaults.jsm",
   PromiseUtils: "resource://gre/modules/PromiseUtils.jsm",
   Region: "resource://gre/modules/Region.jsm",
@@ -63,19 +62,19 @@ async function getImportableSites() {
     const migrator = await MigrationUtils.getMigrator(browserId);
     if (!migrator) {
       continue;
     }
 
     // Check each profile for top sites
     const dataPath = await migrator.wrappedJSObject._getChromeUserDataPathIfExists();
     for (const profile of await migrator.getSourceProfiles()) {
-      let path = OS.Path.join(dataPath, profile.id, "Top Sites");
+      let path = PathUtils.join(dataPath, profile.id, "Top Sites");
       // Skip if top sites data is missing
-      if (!(await OS.File.exists(path))) {
+      if (!(await IOUtils.exists(path))) {
         Cu.reportError(`Missing file at ${path}`);
         continue;
       }
 
       try {
         for (const row of await MigrationUtils.getRowsFromDBWithoutLocks(
           path,
           `Importable ${browserId} top sites`,
--- a/browser/components/newtab/aboutwelcome/lib/AboutWelcomeTelemetry.jsm
+++ b/browser/components/newtab/aboutwelcome/lib/AboutWelcomeTelemetry.jsm
@@ -10,19 +10,16 @@ const { XPCOMUtils } = ChromeUtils.impor
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   PingCentre: "resource:///modules/PingCentre.jsm",
   ClientID: "resource://gre/modules/ClientID.jsm",
   Services: "resource://gre/modules/Services.jsm",
   TelemetrySession: "resource://gre/modules/TelemetrySession.jsm",
   AttributionCode: "resource:///modules/AttributionCode.jsm",
 });
-XPCOMUtils.defineLazyServiceGetters(this, {
-  gUUIDGenerator: ["@mozilla.org/uuid-generator;1", "nsIUUIDGenerator"],
-});
 XPCOMUtils.defineLazyPreferenceGetter(
   this,
   "structuredIngestionEndpointBase",
   "browser.newtabpage.activity-stream.telemetry.structuredIngestion.endpoint",
   ""
 );
 XPCOMUtils.defineLazyGetter(this, "telemetryClientId", () =>
   ClientID.getClientID()
@@ -53,18 +50,18 @@ class AboutWelcomeTelemetry {
   get pingCentre() {
     Object.defineProperty(this, "pingCentre", {
       value: new PingCentre({ topic: TELEMETRY_TOPIC }),
     });
     return this.pingCentre;
   }
 
   _generateStructuredIngestionEndpoint() {
-    const uuid = gUUIDGenerator.generateUUID().toString();
-    // Structured Ingestion does not support the UUID generated by gUUIDGenerator,
+    const uuid = Services.uuid.generateUUID().toString();
+    // Structured Ingestion does not support the UUID generated by Services.uuid,
     // because it contains leading and trailing braces. Need to trim them first.
     const docID = uuid.slice(1, -1);
     const extension = `${STRUCTURED_INGESTION_NAMESPACE_MS}/${PING_TYPE}/${PING_VERSION}/${docID}`;
     return `${structuredIngestionEndpointBase}/${extension}`;
   }
 
   /**
    * Attach browser attribution data to a ping payload.
--- a/browser/components/newtab/common/ActorConstants.jsm
+++ b/browser/components/newtab/common/ActorConstants.jsm
@@ -10,16 +10,17 @@ const MESSAGE_TYPE_LIST = [
   "IMPRESSION",
   "TRIGGER",
   "NEWTAB_MESSAGE_REQUEST",
   "DOORHANGER_TELEMETRY",
   "TOOLBAR_BADGE_TELEMETRY",
   "TOOLBAR_PANEL_TELEMETRY",
   "MOMENTS_PAGE_TELEMETRY",
   "INFOBAR_TELEMETRY",
+  "SPOTLIGHT_TELEMETRY",
   "AS_ROUTER_TELEMETRY_USER_EVENT",
 
   // Admin types
   "ADMIN_CONNECT_STATE",
   "UNBLOCK_MESSAGE_BY_ID",
   "UNBLOCK_ALL",
   "BLOCK_BUNDLE",
   "UNBLOCK_BUNDLE",
--- a/browser/components/newtab/content-src/asrouter/components/Button/_Button.scss
+++ b/browser/components/newtab/content-src/asrouter/components/Button/_Button.scss
@@ -20,75 +20,32 @@
     overflow: hidden;
     display: block;
     visibility: hidden;
   }
 
   &.primary {
     border: 1px solid var(--newtab-button-primary-color);
     background-color: var(--newtab-button-primary-color);
-    color: $grey-10;
+    color: var(--newtab-button-primary-text-color);
 
     &:hover {
-      background-color: $blue-70;
+      background-color: var(--newtab-button-primary-hover-color);
     }
 
     &:active {
-      background-color: $blue-80;
-    }
-  }
-
-  &.secondary {
-    background-color: $grey-90-10;
-
-    &:hover {
-      background-color: $grey-90-20;
-    }
-
-    &:active {
-      background-color: $grey-90-30;
-    }
-
-    &:focus {
-      box-shadow: 0 0 0 1px $blue-50 inset, 0 0 0 1px $blue-50, 0 0 0 4px $blue-50-30;
+      background-color: var(--newtab-button-primary-active-color);
     }
   }
 
   &.slim {
-    background-color: $grey-90-10;
+    border: $border-primary;
     margin-inline-start: 0;
     font-size: 12px;
     padding: 6px 12px;
 
-    &:hover {
-      background-color: $grey-90-20;
+    &:hover,
+    &:focus {
+      box-shadow: $shadow-primary;
+      transition: box-shadow 150ms;
     }
   }
 }
-
-[lwt-newtab-brighttext] {
-  .secondary {
-    background-color: $grey-10-10;
-
-    &:hover {
-      background-color: $grey-10-20;
-    }
-
-    &:active {
-      background-color: $grey-10-30;
-    }
-  }
-
-  // Snippets scene 2 footer
-  .footer {
-    .secondary {
-      background-color: $grey-10-30;
-
-      &:hover {
-        background-color: $grey-10-40;
-      }
-
-      &:active {
-        background-color: $grey-10-50;
-      }
-    }
-  }
-}
--- a/browser/components/newtab/content-src/asrouter/components/ModalOverlay/_ModalOverlay.scss
+++ b/browser/components/newtab/content-src/asrouter/components/ModalOverlay/_ModalOverlay.scss
@@ -25,17 +25,17 @@
 
 .modalOverlayInner {
   min-width: min-content;
   width: 100%;
   max-width: 960px;
   position: relative;
   margin: auto;
   background: var(--newtab-modal-color);
-  box-shadow: 0 1px 15px 0 $black-30;
+  box-shadow: $shadow-large;
   border-radius: 4px;
   display: none;
   z-index: $modal-scrollbar-z-index;
 
   // modal takes over entire screen
   @media(max-width: 960px) {
     height: 100%;
     top: 0;
@@ -44,17 +44,17 @@
     border-radius: 0;
   }
 
   &.active {
     display: block;
   }
 
   h2 {
-    color: $grey-60;
+    color: var(--newtab-section-header-text-color);
     text-align: center;
     font-weight: 200;
     margin-top: 30px;
     font-size: 28px;
     line-height: 37px;
     letter-spacing: -0.13px;
 
     @media(max-width: 960px) {
@@ -62,17 +62,17 @@
     }
 
     @media(max-width: 850px) {
       margin-top: 30px;
     }
   }
 
   .footer {
-    border-top: 1px solid $grey-30;
+    border-top: $border-secondary;
     border-radius: 4px;
     height: 70px;
     width: 100%;
     position: absolute;
     bottom: 0;
     text-align: center;
     background-color: $white;
 
@@ -91,14 +91,13 @@
       min-width: 150px;
       height: 30px;
       padding: 4px 30px 6px;
       font-size: 15px;
 
       &:focus,
       &.active,
       &:hover {
-        box-shadow: 0 0 0 5px $grey-30;
-        transition: box-shadow 150ms;
+        @include fade-in-card;
       }
     }
   }
 }
--- a/browser/components/newtab/content-src/asrouter/components/SnippetBase/_SnippetBase.scss
+++ b/browser/components/newtab/content-src/asrouter/components/SnippetBase/_SnippetBase.scss
@@ -79,18 +79,18 @@
     margin-inline-end: 12px;
     flex-shrink: 0;
   }
 }
 
 .snippets-preview-banner {
   font-size: 15px;
   line-height: 42px;
-  color: $grey-60-70;
-  background: $grey-30-60;
+  color: var(--newtab-text-primary-color);
+  background: var(--newtab-background-color-secondary);
   text-align: center;
   position: absolute;
   top: 0;
   width: 100%;
 
   span {
     vertical-align: middle;
   }
--- a/browser/components/newtab/content-src/asrouter/templates/EOYSnippet/_EOYSnippet.scss
+++ b/browser/components/newtab/content-src/asrouter/templates/EOYSnippet/_EOYSnippet.scss
@@ -9,33 +9,34 @@
   .donation-form-url {
     white-space: nowrap;
     font-size: 14px;
     padding: 8px 20px;
     border-radius: 2px;
   }
 
   .donation-amount {
-    color: $grey-90;
+    color: var(--newtab-text-primary-color);
     margin-inline-end: 18px;
-    border: 1px solid $grey-40;
+    border: $input-border;
     padding: 5px 14px;
-    background: $grey-10;
+    background: var(--newtab-textbox-background-color);
     cursor: pointer;
   }
 
   input {
     &[type='radio'] {
       opacity: 0;
       margin-inline-end: -18px;
 
       &:checked + .donation-amount {
-        background: $grey-50;
-        color: $white;
-        border: 1px solid $grey-60;
+        // Use a text color for the background to achieve an inverted look.
+        background: var(--newtab-text-secondary-color);
+        color: var(--newtab-background-color-secondary);
+        border: $border-secondary;
       }
 
       // accessibility
       &:checked:focus + .donation-amount,
       &:not(:checked):focus + .donation-amount {
         border: 1px dotted var(--newtab-link-primary-color);
       }
     }
new file mode 100644
--- /dev/null
+++ b/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/Spotlight.schema.json
@@ -0,0 +1,140 @@
+{
+  "title": "Spotlight",
+  "description": "A template with an image, title, content and two buttons.",
+  "version": "1.0.0",
+  "type": "object",
+  "properties": {
+    "template": {
+      "type": "string",
+      "description": "Specify the layout template for the Spotlight",
+      "enum": ["logo-and-content"]
+    },
+    "logoImageURL": {
+      "type": "string",
+      "description": "URL for image to use with the content"
+    },
+    "body": {
+      "properties": {
+        "title": {
+          "type": "object",
+          "properties": {
+            "label": {
+              "description": "The title shown in the Spotlight message",
+              "oneOf": [
+                {
+                  "type": "string",
+                  "description": "Message shown in the header element"
+                },
+                {
+                  "type": "object",
+                  "properties": {
+                    "string_id": {
+                      "type": "string",
+                      "description": "Id of localized string for the header element"
+                    }
+                  },
+                  "required": ["string_id"]
+                }
+              ]
+            },
+            "required": ["label"]
+          }
+        },
+        "text": {
+          "type": "object",
+          "properties": {
+            "label": {
+              "description": "The content shown in the Spotlight message",
+              "oneOf": [
+                {
+                  "type": "string",
+                  "description": "Message shown in the paragraph element"
+                },
+                {
+                  "type": "object",
+                  "properties": {
+                    "string_id": {
+                      "type": "string",
+                      "description": "Id of localized string for the paragraph element"
+                    }
+                  },
+                  "required": ["string_id"]
+                }
+              ]
+            },
+            "required": ["label"]
+          }
+        },
+        "primary": {
+          "type": "object",
+          "properties": {
+            "label": {
+              "description": "The label for the primary button",
+              "oneOf": [
+                {
+                  "type": "string",
+                  "description": "Message shown in the button element"
+                },
+                {
+                  "type": "object",
+                  "properties": {
+                    "string_id": {
+                      "type": "string",
+                      "description": "Id of localized string for the button element"
+                    }
+                  },
+                  "required": ["string_id"]
+                }
+              ]
+            },
+            "action": {
+              "type": "object",
+              "properties": {
+                "type": {
+                  "type": "string",
+                  "description": "Action dispatched by the button."
+                },
+                "data": {
+                  "type": "object"
+                }
+              },
+              "required": ["type"],
+              "additionalProperties": false
+            },
+            "required": ["label", "action"]
+          }
+        },
+        "secondary": {
+          "type": "object",
+          "properties": {
+            "label": {
+              "description": "The label for the secondary button",
+              "oneOf": [
+                {
+                  "type": "string",
+                  "description": "Message shown in the button element"
+                },
+                {
+                  "type": "object",
+                  "properties": {
+                    "string_id": {
+                      "type": "string",
+                      "description": "Id of localized string for the button element"
+                    }
+                  },
+                  "required": ["string_id"]
+                }
+              ]
+            },
+            "required": ["label", "action"]
+          }
+        }
+      },
+      "additionalProperties": false,
+      "required": ["title", "text", "primary", "secondary"]
+    }
+  },
+  "additionalProperties": false,
+  "required": ["body"]
+}
+
--- a/browser/components/newtab/content-src/asrouter/templates/SimpleBelowSearchSnippet/_SimpleBelowSearchSnippet.scss
+++ b/browser/components/newtab/content-src/asrouter/templates/SimpleBelowSearchSnippet/_SimpleBelowSearchSnippet.scss
@@ -77,17 +77,17 @@
 
   .blockButton {
     display: block;
     inset-inline-end: 10px;
     opacity: 1;
     top: 50%;
 
     &:focus {
-      box-shadow: 0 0 0 1px $blue-50 inset, 0 0 0 1px $blue-50, 0 0 0 4px $blue-50-30;
+      box-shadow: $shadow-primary;
       border-radius: 2px;
     }
   }
 
   .title {
     font-size: inherit;
     margin: 0;
   }
--- a/browser/components/newtab/content-src/asrouter/templates/SimpleSnippet/_SimpleSnippet.scss
+++ b/browser/components/newtab/content-src/asrouter/templates/SimpleSnippet/_SimpleSnippet.scss
@@ -2,19 +2,19 @@
 $icon-width: 54px; // width of primary icon + margin
 
 .SimpleSnippet {
   &.tall {
     padding: 27px 0;
   }
 
   p em {
-    color: $grey-90;
+    color: var(--newtab-text-empahsis-text-color);
     font-style: normal;
-    background: $yellow-50;
+    background: var(--newtab-text-emphasis-background);
   }
 
   &.bold,
   &.takeover {
     .donation-form-url,
     .donation-amount {
       padding-top: 8px;
       padding-bottom: 8px;
--- a/browser/components/newtab/content-src/components/ASRouterAdmin/ASRouterAdmin.scss
+++ b/browser/components/newtab/content-src/components/ASRouterAdmin/ASRouterAdmin.scss
@@ -105,17 +105,17 @@
   .json-button {
     display: inline-flex;
     font-size: 10px;
     padding: 4px 10px;
     margin-bottom: 6px;
     margin-inline-end: 4px;
 
     &:hover {
-      background-color: $grey-20-60;
+      background-color: var(--newtab-element-hover-color);
       box-shadow: none;
     }
   }
 
   table {
     border-collapse: collapse;
     width: 100%;
 
@@ -154,17 +154,17 @@
 
   .sourceLabel {
     background: var(--newtab-textbox-background-color);
     padding: 2px 5px;
     border-radius: 3px;
 
     &.isDisabled {
       background: $email-input-invalid;
-      color: $red-60;
+      color: var(--newtab-status-error);
     }
   }
 
   .message-item {
     &:first-child td {
       border-top: 1px solid $border-color;
     }
 
@@ -222,17 +222,17 @@
     padding: 8px;
     font-size: 12px;
     max-width: 750px;
     overflow: auto;
     font-family: $monospace;
   }
 
   .errorState {
-    border: 1px solid $red-60;
+    border: $input-error-border;
   }
 
   .helpLink {
     padding: 10px;
     display: flex;
     background: $black-10;
     border-radius: 3px;
 
--- a/browser/components/newtab/content-src/components/Base/Base.jsx
+++ b/browser/components/newtab/content-src/components/Base/Base.jsx
@@ -159,38 +159,30 @@ export class BaseContent extends React.P
   }
 
   render() {
     const { props } = this;
     const { App } = props;
     const { initialized } = App;
     const prefs = props.Prefs.values;
 
-    // Values from experiment data
-    const { prefsButtonIcon } = prefs.featureConfig || {};
-
     const isDiscoveryStream =
       props.DiscoveryStream.config && props.DiscoveryStream.config.enabled;
     let filteredSections = props.Sections.filter(
       section => section.id !== "topstories"
     );
 
     const pocketEnabled =
       prefs["feeds.section.topstories"] && prefs["feeds.system.topstories"];
     const noSectionsEnabled =
       !prefs["feeds.topsites"] &&
       !pocketEnabled &&
       filteredSections.filter(section => section.enabled).length === 0;
     const searchHandoffEnabled = prefs["improvesearch.handoffToAwesomebar"];
-    const { customizationMenuEnabled, newNewtabExperienceEnabled } =
-      prefs.featureConfig || {};
-    const canShowCustomizationMenu =
-      customizationMenuEnabled || newNewtabExperienceEnabled;
-    const showCustomizationMenu =
-      canShowCustomizationMenu && this.state.customizeMenuVisible;
+    const showCustomizationMenu = this.state.customizeMenuVisible;
     const enabledSections = {
       topSitesEnabled: prefs["feeds.topsites"],
       pocketEnabled: prefs["feeds.section.topstories"],
       highlightsEnabled: prefs["feeds.section.highlights"],
       showSponsoredTopSitesEnabled: prefs.showSponsoredTopSites,
       showSponsoredPocketEnabled: prefs.showSponsored,
       topSitesRowsCount: prefs.topSitesRows,
     };
@@ -202,43 +194,38 @@ export class BaseContent extends React.P
       isDiscoveryStream && pocketEnabled && "ds-outer-wrapper-search-alignment",
       isDiscoveryStream && "ds-outer-wrapper-breakpoint-override",
       prefs.showSearch &&
         this.state.fixedSearch &&
         !noSectionsEnabled &&
         "fixed-search",
       prefs.showSearch && noSectionsEnabled && "only-search",
       prefs["logowordmark.alwaysVisible"] && "visible-logo",
-      newNewtabExperienceEnabled && "newtab-experience",
     ]
       .filter(v => v)
       .join(" ");
 
     const hasSnippet =
       prefs["feeds.snippets"] &&
       this.props.adminContent &&
       this.props.adminContent.message &&
       this.props.adminContent.message.id;
 
     return (
       <div>
-        {canShowCustomizationMenu ? (
-          <CustomizeMenu
-            onClose={this.closeCustomizationMenu}
-            onOpen={this.openCustomizationMenu}
-            openPreferences={this.openPreferences}
-            setPref={this.setPref}
-            enabledSections={enabledSections}
-            pocketRegion={pocketRegion}
-            mayHaveSponsoredTopSites={mayHaveSponsoredTopSites}
-            showing={showCustomizationMenu}
-          />
-        ) : (
-          <PrefsButton onClick={this.openPreferences} icon={prefsButtonIcon} />
-        )}
+        <CustomizeMenu
+          onClose={this.closeCustomizationMenu}
+          onOpen={this.openCustomizationMenu}
+          openPreferences={this.openPreferences}
+          setPref={this.setPref}
+          enabledSections={enabledSections}
+          pocketRegion={pocketRegion}
+          mayHaveSponsoredTopSites={mayHaveSponsoredTopSites}
+          showing={showCustomizationMenu}
+        />
         {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions*/}
         <div className={outerClassName} onClick={this.closeCustomizationMenu}>
           <main className={hasSnippet ? "has-snippet" : ""}>
             {prefs.showSearch && (
               <div className="non-collapsible-section">
                 <ErrorBoundary>
                   <Search
                     showLogo={
--- a/browser/components/newtab/content-src/components/Card/_Card.scss
+++ b/browser/components/newtab/content-src/components/Card/_Card.scss
@@ -1,100 +1,60 @@
 @use 'sass:math';
 
-// Special styling for the New Tab Experience styles,
-// This is to be incorporated once the styles are made permanent
-.outer-wrapper.newtab-experience {
-  .card-outer {
-    border-radius: $border-radius-new;
-
-    &:is(:focus):not(.placeholder) {
-      @include ds-focus-nte;
-
-      transition: none;
-    }
-
-    &:hover {
-      box-shadow: none;
-      transition: none;
-    }
-
-    .card {
-      box-shadow: var(--newtab-card-first-shadow);
-      border-radius: $border-radius-new;
-    }
-
-    .card-preview-image-outer {
-      border-radius: $border-radius-new $border-radius-new 0 0;
-    }
-
-    // Temporary fix to have the context button focus blend in with other New Tab Experience context menu focus
-    .context-menu-button {
-      &:is(:active, :focus) {
-        outline: 0;
-        fill: var(--newtab-primary-action-background);
-        border: 1px solid var(--newtab-primary-action-background);
-      }
-    }
-
-    > a {
-      &:is(:focus) {
-        .card {
-          @include ds-focus-nte;
-        }
-      }
-    }
-  }
-}
-
-.outer-wrapper:not(.newtab-experience) {
-  .card-outer {
-    &:is(:hover, :focus, .active):not(.placeholder) {
-      @include fade-in-card;
-    }
-  }
-}
-
 .card-outer {
   @include context-menu-button;
   background: var(--newtab-card-background-color);
-  border-radius: $border-radius;
+  border-radius: $border-radius-new;
   display: inline-block;
   height: $card-height;
   margin-inline-end: $base-gutter;
   position: relative;
   width: 100%;
 
+  &:is(:focus):not(.placeholder) {
+    @include ds-focus;
+
+    transition: none;
+  }
+
+  &:hover {
+    box-shadow: none;
+    transition: none;
+  }
+
   &.placeholder {
     background: transparent;
 
-    .card {
-      box-shadow: inset $inner-box-shadow;
-    }
-
     .card-preview-image-outer,
     .card-context {
       display: none;
     }
   }
 
   .card {
-    border-radius: $border-radius;
-    box-shadow: var(--newtab-card-shadow);
+    border-radius: $border-radius-new;
+    box-shadow: var(--newtab-card-first-shadow);
     height: 100%;
   }
 
   > a {
     color: inherit;
     display: block;
     height: 100%;
     outline: none;
     position: absolute;
     width: 100%;
 
+    &:is(:focus) {
+      .card {
+        @include ds-focus;
+      }
+    }
+
     &:is(.active, :focus) {
       .card {
         @include fade-in-card;
       }
 
       .card-title {
         color: var(--newtab-link-primary-color);
       }
@@ -114,26 +74,22 @@
     }
 
     .card-host-name.alternate {
       display: block;
     }
   }
 
   .card-preview-image-outer {
-    background-color: $grey-30;
-    border-radius: $border-radius $border-radius 0 0;
+    background-color: var(--newtab-card-placeholder-color);
+    border-radius: $border-radius-new $border-radius-new 0 0;
     height: $card-preview-image-height;
     overflow: hidden;
     position: relative;
 
-    [lwt-newtab-brighttext] & {
-      background-color: $grey-60;
-    }
-
     &::after {
       border-bottom: 1px solid var(--newtab-card-hairline-color);
       bottom: 0;
       content: '';
       position: absolute;
       width: 100%;
     }
 
--- a/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx
+++ b/browser/components/newtab/content-src/components/CollapsibleSection/CollapsibleSection.jsx
@@ -1,332 +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/. */
 
-import { actionCreators as ac } from "common/Actions.jsm";
 import { ErrorBoundary } from "content-src/components/ErrorBoundary/ErrorBoundary";
 import { FluentOrText } from "content-src/components/FluentOrText/FluentOrText";
 import React from "react";
 import { connect } from "react-redux";
-import { SectionMenu } from "content-src/components/SectionMenu/SectionMenu";
-import { SectionMenuOptions } from "content-src/lib/section-menu-options";
-import { ContextMenuButton } from "content-src/components/ContextMenu/ContextMenuButton";
 
-const VISIBLE = "visible";
-const VISIBILITY_CHANGE_EVENT = "visibilitychange";
-
+/**
+ * A section that can collapse. As of bug 1710937, it can no longer collapse.
+ * See bug 1727365 for follow-up work to simplify this component.
+ */
 export class _CollapsibleSection extends React.PureComponent {
   constructor(props) {
     super(props);
     this.onBodyMount = this.onBodyMount.bind(this);
-    this.collapseOrExpandSection = this.collapseOrExpandSection.bind(this);
-    this.onHeaderClick = this.onHeaderClick.bind(this);
-    this.onKeyPress = this.onKeyPress.bind(this);
-    this.onTransitionEnd = this.onTransitionEnd.bind(this);
-    this.enableOrDisableAnimation = this.enableOrDisableAnimation.bind(this);
     this.onMenuButtonMouseEnter = this.onMenuButtonMouseEnter.bind(this);
     this.onMenuButtonMouseLeave = this.onMenuButtonMouseLeave.bind(this);
     this.onMenuUpdate = this.onMenuUpdate.bind(this);
     this.state = {
-      enableAnimation: true,
-      isAnimating: false,
       menuButtonHover: false,
       showContextMenu: false,
     };
     this.setContextMenuButtonRef = this.setContextMenuButtonRef.bind(this);
   }
 
-  componentWillMount() {
-    this.props.document.addEventListener(
-      VISIBILITY_CHANGE_EVENT,
-      this.enableOrDisableAnimation
-    );
-  }
-
-  componentWillUpdate(nextProps) {
-    // Check if we're about to go from expanded to collapsed
-    if (!this.props.collapsed && nextProps.collapsed) {
-      // This next line forces a layout flush of the section body, which has a
-      // max-height style set, so that the upcoming collapse animation can
-      // animate from that height to the collapsed height. Without this, the
-      // update is coalesced and there's no animation from no-max-height to 0.
-      this.sectionBody.scrollHeight; // eslint-disable-line no-unused-expressions
-    }
-  }
-
   setContextMenuButtonRef(element) {
     this.contextMenuButtonRef = element;
   }
 
-  componentDidMount() {
-    if (!this.props.Prefs.values.featureConfig.newNewtabExperienceEnabled) {
-      this.contextMenuButtonRef.addEventListener(
-        "mouseenter",
-        this.onMenuButtonMouseEnter
-      );
-      this.contextMenuButtonRef.addEventListener(
-        "mouseleave",
-        this.onMenuButtonMouseLeave
-      );
-    }
-  }
-
-  componentWillUnmount() {
-    this.props.document.removeEventListener(
-      VISIBILITY_CHANGE_EVENT,
-      this.enableOrDisableAnimation
-    );
-
-    if (!this.props.Prefs.values.featureConfig.newNewtabExperienceEnabled) {
-      this.contextMenuButtonRef.removeEventListener(
-        "mouseenter",
-        this.onMenuButtonMouseEnter
-      );
-      this.contextMenuButtonRef.removeEventListener(
-        "mouseleave",
-        this.onMenuButtonMouseLeave
-      );
-    }
-  }
-
-  enableOrDisableAnimation() {
-    // Only animate the collapse/expand for visible tabs.
-    const visible = this.props.document.visibilityState === VISIBLE;
-    if (this.state.enableAnimation !== visible) {
-      this.setState({ enableAnimation: visible });
-    }
-  }
-
   onBodyMount(node) {
     this.sectionBody = node;
   }
 
-  collapseOrExpandSection() {
-    // If this.sectionBody is unset, it means that we're in some sort of error
-    // state, probably displaying the error fallback, so we won't be able to
-    // compute the height, and we don't want to persist the preference.
-    if (!this.sectionBody) {
-      return;
-    }
-
-    // Get the current height of the body so max-height transitions can work
-    this.setState({
-      isAnimating: true,
-      maxHeight: `${this._getSectionBodyHeight()}px`,
-    });
-    const { action } = SectionMenuOptions.CheckCollapsed(this.props);
-    this.props.dispatch(action);
-  }
-
-  onHeaderClick() {
-    // If the new new tab experience pref is turned on,
-    // sections should not be collapsible.
-    // If this.sectionBody is unset, it means that we're in some sort of error
-    // state, probably displaying the error fallback, so we won't be able to
-    // compute the height, and we don't want to persist the preference.
-    // If props.collapsed is undefined handler shouldn't do anything.
-    if (
-      this.props.Prefs.values.featureConfig.newNewtabExperienceEnabled ||
-      !this.sectionBody ||
-      this.props.collapsed === undefined
-    ) {
-      return;
-    }
-
-    this.collapseOrExpandSection();
-    const { userEvent } = SectionMenuOptions.CheckCollapsed(this.props);
-    this.props.dispatch(
-      ac.UserEvent({
-        event: userEvent,
-        source: this.props.eventSource,
-      })
-    );
-  }
-
-  onKeyPress(event) {
-    if (event.key === "Enter" || event.key === " ") {
-      event.preventDefault();
-      this.onHeaderClick();
-    }
-  }
-
-  _getSectionBodyHeight() {
-    const div = this.sectionBody;
-    if (div.style.display === "none") {
-      // If the div isn't displayed, we can't get it's height. So we display it
-      // to get the height (it doesn't show up because max-height is set to 0px
-      // in CSS). We don't undo this because we are about to expand the section.
-      div.style.display = "block";
-    }
-    return div.scrollHeight;
-  }
-
-  onTransitionEnd(event) {
-    // Only update the animating state for our own transition (not a child's)
-    if (event.target === event.currentTarget) {
-      this.setState({ isAnimating: false });
-    }
-  }
-
-  renderIcon() {
-    const { icon } = this.props;
-    if (icon && icon.startsWith("moz-extension://")) {
-      return (
-        <span
-          className="icon icon-small-spacer"
-          style={{ backgroundImage: `url('${icon}')` }}
-        />
-      );
-    }
-    return (
-      <span
-        className={`icon icon-small-spacer icon-${icon || "webextension"}`}
-      />
-    );
-  }
-
   onMenuButtonMouseEnter() {
     this.setState({ menuButtonHover: true });
   }
 
   onMenuButtonMouseLeave() {
     this.setState({ menuButtonHover: false });
   }
 
   onMenuUpdate(showContextMenu) {
     this.setState({ showContextMenu });
   }
 
   render() {
-    const isCollapsible = this.props.collapsed !== undefined;
-    const isNewNewtabExperienceEnabled = this.props.Prefs.values.featureConfig
-      .newNewtabExperienceEnabled;
-
-    // If new new tab prefs are set to true, sections should not be
-    // collapsible. Expand and make the section visible, if it has been
-    // previously collapsed.
-    if (isNewNewtabExperienceEnabled && this.props.collapsed) {
-      this.collapseOrExpandSection();
-    }
-
     const {
-      enableAnimation,
       isAnimating,
       maxHeight,
       menuButtonHover,
       showContextMenu,
     } = this.state;
-    const {
-      id,
-      eventSource,
-      collapsed,
-      learnMore,
-      title,
-      extraMenuOptions,
-      showPrefName,
-      privacyNoticeURL,
-      dispatch,
-      isFixed,
-      isFirst,
-      isLast,
-      isWebExtension,
-    } = this.props;
+    const { id, collapsed, learnMore, title } = this.props;
     const active = menuButtonHover || showContextMenu;
     let bodyStyle;
     if (isAnimating && !collapsed) {
       bodyStyle = { maxHeight };
     } else if (!isAnimating && collapsed) {
       bodyStyle = { display: "none" };
     }
     let titleStyle;
     if (this.props.hideTitle) {
       titleStyle = { visibility: "hidden" };
     }
     return (
-      // TODO: Bug 1702140: re-enable this rule.
-      // eslint-disable-next-line jsx-a11y/role-supports-aria-props
       <section
         className={`collapsible-section ${this.props.className}${
-          enableAnimation ? " animation-enabled" : ""
-        }${collapsed ? " collapsed" : ""}${active ? " active" : ""}`}
-        aria-expanded={!collapsed}
+          active ? " active" : ""
+        }`}
         // Note: data-section-id is used for web extension api tests in mozilla central
         data-section-id={id}
       >
         <div className="section-top-bar">
-          <h3 className="section-title" style={titleStyle}>
-            <span className="click-target-container">
-              {/* Click-targets that toggle a collapsible section should have an aria-expanded attribute; see bug 1553234 */}
-              <span
-                className="click-target"
-                role="button"
-                tabIndex="0"
-                onKeyPress={this.onKeyPress}
-                onClick={this.onHeaderClick}
-              >
-                {!isNewNewtabExperienceEnabled && this.renderIcon()}
-                <FluentOrText message={title} />
-                {!isNewNewtabExperienceEnabled && isCollapsible && (
-                  <span
-                    data-l10n-id={
-                      collapsed
-                        ? "newtab-section-expand-section-label"
-                        : "newtab-section-collapse-section-label"
-                    }
-                    className={`collapsible-arrow icon ${
-                      collapsed
-                        ? "icon-arrowhead-forward-small"
-                        : "icon-arrowhead-down-small"
-                    }`}
-                  />
-                )}
-              </span>
-              <span className="learn-more-link-wrapper">
-                {learnMore && (
-                  <span className="learn-more-link">
-                    <FluentOrText message={learnMore.link.message}>
-                      <a href={learnMore.link.href} />
-                    </FluentOrText>
-                  </span>
-                )}
-              </span>
+          <h3 className="section-title-container" style={titleStyle}>
+            <span className="section-title">
+              <FluentOrText message={title} />
+            </span>
+            <span className="learn-more-link-wrapper">
+              {learnMore && (
+                <span className="learn-more-link">
+                  <FluentOrText message={learnMore.link.message}>
+                    <a href={learnMore.link.href} />
+                  </FluentOrText>
+                </span>
+              )}
             </span>
           </h3>
-          {!isNewNewtabExperienceEnabled && (
-            <div>
-              <ContextMenuButton
-                tooltip="newtab-menu-section-tooltip"
-                onUpdate={this.onMenuUpdate}
-                refFunction={this.setContextMenuButtonRef}
-              >
-                <SectionMenu
-                  id={id}
-                  extraOptions={extraMenuOptions}
-                  source={eventSource}
-                  showPrefName={showPrefName}
-                  privacyNoticeURL={privacyNoticeURL}
-                  collapsed={collapsed}
-                  isFixed={isFixed}
-                  isFirst={isFirst}
-                  isLast={isLast}
-                  dispatch={dispatch}
-                  isWebExtension={isWebExtension}
-                />
-              </ContextMenuButton>
-            </div>
-          )}
         </div>
         <ErrorBoundary className="section-body-fallback">
-          <div
-            className={`section-body${isAnimating ? " animating" : ""}`}
-            onTransitionEnd={this.onTransitionEnd}
-            ref={this.onBodyMount}
-            style={bodyStyle}
-          >
+          <div ref={this.onBodyMount} style={bodyStyle}>
             {this.props.children}
           </div>
         </ErrorBoundary>
       </section>
     );
   }
 }
 
--- a/browser/components/newtab/content-src/components/CollapsibleSection/_CollapsibleSection.scss
+++ b/browser/components/newtab/content-src/components/CollapsibleSection/_CollapsibleSection.scss
@@ -1,146 +1,59 @@
-.outer-wrapper.newtab-experience {
-  .collapsible-section {
-    &[data-section-id='topsites'] {
-      .section-top-bar {
-        display: none;
-      }
-    }
-
-    .click-target-container {
-      .click-target {
-        span {
-          cursor: default;
-          font-weight: 600;
-          font-size: 17px;
-          color: var(--newtab-background-primary-text-color);
-        }
-      }
-    }
-  }
-}
-
-.outer-wrapper:not(.newtab-experience) {
-  .collapsible-section {
-    .section-title {
-      span {
-        vertical-align: middle;
-      }
-    }
-  }
-}
-
 .collapsible-section {
   padding: $section-vertical-padding $section-horizontal-padding;
-  transition-delay: 100ms;
-  transition-duration: 100ms;
-  transition-property: background-color;
+
+  .section-title-container {
+    margin: 0;
+  }
 
   .section-title {
     font-size: $section-title-font-size;
-    font-weight: bold;
-    margin: 0;
+    font-weight: 600;
+    color: var(--newtab-background-primary-text-color);
 
-    &.grey-title,
-    span {
+    &.grey-title {
       color: var(--newtab-section-header-text-color);
       display: inline-block;
       fill: var(--newtab-section-header-text-color);
-    }
-
-    &.grey-title {
       vertical-align: middle;
     }
 
-    .click-target-container {
+    .section-title-contents {
       // Center "What's Pocket?" for "mobile" viewport
       @media (max-width: $break-point-medium - 1) {
         display: block;
 
         .learn-more-link-wrapper {
           display: block;
           text-align: center;
 
           .learn-more-link {
             margin-inline-start: 0;
           }
         }
       }
 
       vertical-align: top;
-
-      .click-target {
-        white-space: nowrap;
-        cursor: pointer;
-      }
-    }
-
-    .collapsible-arrow {
-      margin-inline-start: 8px;
-      margin-top: -1px;
     }
   }
 
   .section-top-bar {
     min-height: 19px;
     margin-bottom: 13px;
     position: relative;
-
-    .context-menu-button {
-      background: url('chrome://global/skin/icons/more.svg') no-repeat right center;
-      border: 0;
-      cursor: pointer;
-      fill: var(--newtab-section-header-text-color);
-      height: 100%;
-      inset-inline-end: 0;
-      opacity: 0;
-      position: absolute;
-      top: 0;
-      transition-duration: 200ms;
-      transition-property: opacity;
-      width: $context-menu-button-size;
-
-      &:is(:active, :focus, :hover) {
-        fill: var(--newtab-section-header-text-color);
-        opacity: 1;
-      }
-    }
-
-    .context-menu {
-      top: 16px;
-    }
-
-    @media (max-width: $break-point-widest + $card-width * 1.5) {
-      @include context-menu-open-left;
-    }
-  }
-
-  &:hover,
-  &.active {
-    .section-top-bar {
-      .context-menu-button {
-        opacity: 1;
-      }
-    }
   }
 
   &.active {
     background: var(--newtab-element-hover-color);
     border-radius: 4px;
-
-    .section-top-bar {
-      .context-menu-button {
-        fill: var(--newtab-section-active-contextmenu-color);
-      }
-    }
   }
 
   .learn-more-link {
-    font-size: 11px;
+    font-size: 13px;
     margin-inline-start: 12px;
 
     a {
       color: var(--newtab-link-secondary-color);
     }
   }
 
   .section-body-fallback {
@@ -154,35 +67,21 @@
     padding: 0 $horizontal-padding;
 
     &.animating {
       overflow: hidden;
       pointer-events: none;
     }
   }
 
-  &.animation-enabled {
-    .section-title {
-      .collapsible-arrow {
-        transition: transform 0.5s $photon-easing;
-      }
-    }
-
-    .section-body {
-      transition: max-height 0.5s $photon-easing;
-    }
-  }
-
-  &.collapsed {
-    .section-body {
-      max-height: 0;
-      overflow: hidden;
+  &[data-section-id='topsites'] {
+    .section-top-bar {
+      display: none;
     }
   }
 
   // Hide first story card for the medium breakpoint to prevent orphaned third story
   &[data-section-id='topstories'] .card-outer:first-child {
     @media (min-width: $break-point-medium) and (max-width: $break-point-large - 1) {
       display: none;
     }
   }
 }
-
--- a/browser/components/newtab/content-src/components/ConfirmDialog/_ConfirmDialog.scss
+++ b/browser/components/newtab/content-src/components/ConfirmDialog/_ConfirmDialog.scss
@@ -1,11 +1,11 @@
 .confirmation-dialog {
   .modal {
-    box-shadow: 0 2px 2px 0 $black-10;
+    box-shadow: $shadow-secondary;
     left: 0;
     margin: auto;
     position: fixed;
     right: 0;
     top: 20%;
     width: 400px;
   }
 
--- a/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
+++ b/browser/components/newtab/content-src/components/ContextMenu/ContextMenu.jsx
@@ -146,32 +146,26 @@ export class _ContextMenuItem extends Re
   onKeyUp(event) {
     if (event.key === " ") {
       event.preventDefault();
     }
   }
 
   render() {
     const { option } = this.props;
-    const {
-      newNewtabExperienceEnabled,
-    } = this.props.Prefs.values.featureConfig;
     return (
       <li role="presentation" className="context-menu-item">
         <button
           className={option.disabled ? "disabled" : ""}
           role="menuitem"
           onClick={this.onClick}
           onKeyDown={this.onKeyDown}
           onKeyUp={this.onKeyUp}
           ref={option.first ? this.focusFirst : null}
         >
-          {!newNewtabExperienceEnabled && option.icon && (
-            <span className={`icon icon-spacer icon-${option.icon}`} />
-          )}
           <span data-l10n-id={option.string_id || option.id} />
         </button>
       </li>
     );
   }
 }
 
 export const ContextMenuItem = connect(state => ({
--- a/browser/components/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss
+++ b/browser/components/newtab/content-src/components/CustomizeMenu/_CustomizeMenu.scss
@@ -26,17 +26,17 @@
     background-color: var(--newtab-background-button-hover-color);
   }
 
   &:active {
     background-color: var(--newtab-background-button-active-color);
   }
 
   &:focus-visible {
-    @include ds-focus-nte;
+    @include ds-focus;
   }
 
   &.personalize-animate-exit-done {
     visibility: hidden;
   }
 }
 
 .customize-menu {
@@ -213,27 +213,27 @@
 
       &::before {
         position: absolute;
         content: '';
         height: 8px;
         width: 8px;
         inset-inline-start: 3px;
         bottom: 3px;
-        background-color: var(--customize-menu-primary-action-text);
+        background-color: var(--customize-menu-slider-fill);
         transition: transform 250ms;
         border-radius: 50%;
         outline: $customize-menu-border-tint;
       }
     }
 
     .switch input:focus-visible + .slider {
       border: 1px solid var(--newtab-focus-border-selected);
       outline: 0;
-      box-shadow: 0 0 0 2px var(--newtab-focus-outline);
+      box-shadow: $shadow-focus;
     }
 
     .switch input:not(:checked):focus-visible + .slider {
       border: 1px solid var(--newtab-focus-border);
     }
 
     input:checked + .slider {
       background-color: var(--newtab-primary-action-background);
@@ -322,10 +322,10 @@
 }
 
 .home-section .section .sponsored-checkbox:focus-visible,
 .selector:focus-visible,
 .external-link:focus-visible,
 .close-button:focus-visible {
   border: 1px solid var(--newtab-focus-border);
   outline: 0;
-  box-shadow: 0 0 0 2px var(--newtab-focus-outline);
+  box-shadow: $shadow-focus;
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamBase/DiscoveryStreamBase.jsx
@@ -322,17 +322,16 @@ export class _DiscoveryStreamBase extend
               components: [sponsoredCollection],
             },
           ])}
         {!!layoutRender.length && (
           <CollapsibleSection
             className="ds-layout"
             collapsed={topStories.pref.collapsed}
             dispatch={this.props.dispatch}
-            icon={topStories.icon}
             id={topStories.id}
             isFixed={true}
             learnMore={{
               link: {
                 href: message.header.link_url,
                 message: message.header.link_text,
               },
             }}
--- a/browser/components/newtab/content-src/components/DiscoveryStreamBase/_DiscoveryStreamBase.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamBase/_DiscoveryStreamBase.scss
@@ -35,22 +35,18 @@
 
   .ds-context {
     font-weight: 400;
   }
 }
 
 .ds-header,
 .ds-layout .section-title span {
-  @include dark-theme-only {
-    color: $grey-30;
-  }
-
-  color: $grey-50;
-  font-size: 13px;
+  color: var(--newtab-section-header-text-color);
+  font-size: $section-title-font-size;
   font-weight: 600;
   line-height: 20px;
 
   .icon {
     fill: var(--newtab-text-secondary-color);
   }
 }
 
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/_CardGrid.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CardGrid/_CardGrid.scss
@@ -1,34 +1,11 @@
 $col4-header-line-height: 20;
 $col4-header-font-size: 14;
 
-// Special styling for the New Tab Experience styles,
-// This is to be incorporated once the styles are made permanent
-.outer-wrapper.newtab-experience {
-  .ds-card-grid {
-    &.ds-card-grid-border {
-      .ds-card:not(.placeholder) {
-        border-radius: $border-radius-new;
-        box-shadow: var(--newtab-card-first-shadow);
-
-        .img-wrapper .img img {
-          border-radius: $border-radius-new $border-radius-new 0 0;
-        }
-      }
-    }
-
-    .ds-card-link:focus {
-      @include ds-focus-nte;
-      transition: none;
-      border-radius: $border-radius-new;
-    }
-  }
-}
-
 .ds-card-grid {
   display: grid;
   grid-gap: 24px;
 
   .ds-card {
     @include dark-theme-only {
       background: none;
     }
@@ -98,38 +75,43 @@
         min-height: 222px;
       }
     }
   }
 
   &.ds-card-grid-border {
     .ds-card:not(.placeholder) {
       @include dark-theme-only {
-        box-shadow: 0 1px 4px $shadow-10;
-        background: $proton-dark-grey-30;
+        background: var(--newtab-card-background-color);
       }
-
-      box-shadow: 0 1px 4px 0 $grey-90-10;
+      border-radius: $border-radius-new;
+      box-shadow: var(--newtab-card-first-shadow);
 
       .img-wrapper .img img {
-        border-radius: 4px 4px 0 0;
+        border-radius: $border-radius-new $border-radius-new 0 0;
       }
     }
   }
 
   &.ds-card-grid-no-border {
     .ds-card {
       background: none;
 
       .meta {
         padding: 12px 0;
       }
     }
   }
 
+  .ds-card-link:focus {
+    @include ds-focus;
+    transition: none;
+    border-radius: $border-radius-new;
+  }
+
   // "2/3 width layout"
   .ds-column-5 &,
   .ds-column-6 &,
   .ds-column-7 &,
   .ds-column-8 & {
     grid-template-columns: repeat(2, 1fr);
   }
 
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CollectionCardGrid/_CollectionCardGrid.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/CollectionCardGrid/_CollectionCardGrid.scss
@@ -17,30 +17,22 @@
     display: none;
   }
 
   .ds-header {
     padding: 0 40px 0 0;
     margin-bottom: 12px;
 
     .title {
-      @include dark-theme-only {
-        color: $grey-30;
-      }
-
-      color: $grey-90;
+      color: var(--newtab-card-header-color);
       font-weight: 600;
       font-size: 17px;
       line-height: 24px;
     }
 
     .ds-context {
-      @include dark-theme-only {
-        color: $grey-40;
-      }
-
-      color: $grey-50;
+      color: var(--newtab-text-secondary-color);
       font-weight: normal;
       font-size: 13px;
       line-height: 24px;
     }
   }
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSCard/_DSCard.scss
@@ -1,66 +1,36 @@
 // Type sizes
 $header-font-size: 17;
 $header-line-height: 24;
 $excerpt-font-size: 14;
 $excerpt-line-height: 20;
 
-.outer-wrapper:not(.newtab-experience) {
-  .ds-card {
-    .ds-card-link {
-      &:hover {
-        @include ds-fade-in($grey-30);
-
-        @include dark-theme-only {
-          @include ds-fade-in($grey-60);
-        }
-      }
-
-      &:focus {
-        @include ds-fade-in;
-
-        @include dark-theme-only {
-          @include ds-fade-in($blue-40-40);
-        }
-      }
-
-      &:active {
-        @include ds-fade-in($grey-30);
-
-        @include dark-theme-only {
-          @include ds-fade-in($grey-60);
-        }
-      }
-    }
-  }
-}
-
 .ds-card {
   display: flex;
   flex-direction: column;
   position: relative;
 
   .playhead {
-    background: $blue-60 url('chrome://activity-stream/content/data/content/assets/glyph-playhead.svg') no-repeat 12px center;
+    background: var(--newtab-button-primary-color) url('chrome://activity-stream/content/data/content/assets/glyph-playhead.svg') no-repeat 12px center;
     border-radius: 20px;
     bottom: -16px;
-    color: $white-0;
+    color: color-mix(in srgb, var(--newtab-button-primary-text-color) 0%, transparent);
     display: flex;
     flex-direction: column;
     height: 40px;
     justify-content: center;
     left: 16px;
     min-width: 40px;
     padding: 0 0 0 40px;
     position: absolute;
     transition: padding 100ms ease-in-out 0ms, color 100ms linear 100ms;
 
     &:hover {
-      color: $white-100;
+      color: var(--newtab-button-primary-text-color);
       padding: 0 20px 0 40px;
     }
 
     span {
       display: none;
     }
 
     &:hover span {
@@ -84,52 +54,44 @@
   }
 
   .img {
     height: 0;
     padding-top: 50%; // 2:1 aspect ratio
 
     img {
       border-radius: 4px;
-      box-shadow: inset 0 0 0 0.5px $black-15;
+      box-shadow: $shadow-image-inset;
     }
   }
 
   .ds-card-link {
     height: 100%;
     display: flex;
     flex-direction: column;
 
     &:hover {
       header {
-        @include dark-theme-only {
-          color: $blue-40;
-        }
-
-        color: $blue-60;
+        color: var(--newtab-link-primary-color);
       }
     }
 
     &:focus {
+      @include ds-focus;
+
+      transition: none;
+
       header {
-        @include dark-theme-only {
-          color: $blue-40;
-        }
-
-        color: $blue-60;
+        color: var(--newtab-link-primary-color);
       }
     }
 
     &:active {
       header {
-        @include dark-theme-only {
-          color: $blue-50;
-        }
-
-        color: $blue-70;
+        color: var(--newtab-link-primary-active-color);
       }
     }
   }
 
   &.video-card .meta {
     margin-top: 4px;
   }
 
@@ -154,160 +116,76 @@
       @include limit-visible-lines(
         3,
         $excerpt-line-height,
         $excerpt-font-size
       );
     }
 
     .source {
-      @include dark-theme-only {
-        color: $grey-40;
-      }
-
       -webkit-line-clamp: 1;
       margin-bottom: 2px;
       font-size: 13px;
-      color: $grey-50;
+      color: var(--newtab-text-secondary-color);
     }
 
     .cta-button {
-      @include dark-theme-only {
-        color: $grey-10;
-        background: $grey-90-70;
-      }
-
+      background-color: var(--newtab-button-secondary-color);
       width: 100%;
       margin: 12px 0 4px;
       box-shadow: none;
       border-radius: 4px;
       height: 32px;
       font-size: 14px;
       font-weight: 600;
       padding: 5px 8px 7px;
-      border: 0;
-      color: $grey-90;
-      background: $grey-90-10;
-
-      &:focus {
-        @include dark-theme-only {
-          background: $grey-90-70;
-          box-shadow: 0 0 0 2px $grey-80, 0 0 0 5px $blue-50-50;
-        }
-
-        background: $grey-90-10;
-        box-shadow: 0 0 0 2px $white, 0 0 0 5px $blue-50-50;
-      }
-
-      &:hover {
-        @include dark-theme-only {
-          background: $grey-90-50;
-        }
-
-        background: $grey-90-20;
-      }
+      border: $border-primary;
 
       &:active {
-        @include dark-theme-only {
-          background: $grey-90-70;
-        }
-
-        background: $grey-90-30;
+        box-shadow: $shadow-primary;
       }
     }
 
     .cta-link {
-      @include dark-theme-only {
-        color: $blue-40;
-        fill: $blue-40;
-      }
+      color: var(--newtab-link-primary-color);
+      fill: var(--newtab-link-primary-color);
 
       font-size: 15px;
       font-weight: 600;
       line-height: 24px;
       height: 24px;
       width: auto;
       background-size: auto;
       background-position: right 1.5px;
       padding-right: 9px;
-      color: $blue-60;
-      fill: $blue-60;
 
       &:focus {
-        @include dark-theme-only {
-          box-shadow: 0 0 0 1px $grey-80, 0 0 0 4px $blue-50-50;
-        }
+        box-shadow: $shadow-primary;
 
-        box-shadow: 0 0 0 1px $white, 0 0 0 4px $blue-50-50;
         border-radius: 4px;
         outline: 0;
       }
 
       &:active {
-        @include dark-theme-only {
-          color: $blue-50;
-          fill: $blue-50;
-          box-shadow: none;
-        }
-
-        color: $blue-70;
-        fill: $blue-70;
+        color: var(--newtab-link-primary-active-color);
+        fill: var(--newtab-link-primary-active-color);
         box-shadow: none;
       }
 
       &:hover {
         text-decoration: underline;
       }
     }
   }
 
   header {
-    @include dark-theme-only {
-      color: $grey-10;
-    }
-
     line-height: $header-line-height * 1px;
     font-size: $header-font-size * 1px;
-    color: $grey-90;
+    color: var(--newtab-card-header-color);
   }
 
   p {
-    @include dark-theme-only {
-      color: $grey-10;
-    }
-
     font-size: $excerpt-font-size * 1px;
     line-height: $excerpt-line-height * 1px;
-    color: $grey-90;
+    color: var(--newtab-card-header-color);
     margin: 0;
   }
 }
-
-// Special styling for the New Tab Experience styles,
-// This is to be incorporated once the styles are made permanent
-.outer-wrapper.newtab-experience {
-  .ds-card {
-    // Temporary fix to have the context button focus blend in with other New Tab Experience context menu focus
-    .context-menu-button {
-      &:is(:active, :focus) {
-        outline: 0;
-        fill: var(--newtab-primary-action-background);
-        border: 1px solid var(--newtab-primary-action-background);
-      }
-    }
-
-    .ds-card-link {
-      &:focus {
-        @include ds-focus-nte;
-
-        transition: none;
-
-        header {
-          @include dark-theme-only {
-            color: $blue-40;
-          }
-
-          color: $blue-60;
-        }
-      }
-    }
-  }
-}
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/_DSContextFooter.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSContextFooter/_DSContextFooter.scss
@@ -1,55 +1,40 @@
-$status-green: #058B00;
-$status-dark-green: #7C6;
-
 .story-footer {
   color: var(--newtab-text-secondary-color);
   inset-inline-start: 0;
   margin-top: 12px;
   position: relative;
 
   .story-sponsored-label,
   .story-view-count,
   .status-message {
-    @include dark-theme-only {
-      color: $grey-40;
-    }
-
     -webkit-line-clamp: 1;
     font-size: 13px;
     line-height: 24px;
-    color: $grey-50;
+    color: var(--newtab-text-secondary-color);
   }
 
   .status-message {
     display: flex;
     align-items: center;
     height: 24px;
 
     .story-badge-icon {
-      @include dark-theme-only {
-        fill: $grey-40;
-      }
-
-      fill: $grey-50;
+      fill: var(--newtab-icon-secondary-color);
       height: 16px;
       margin-inline-end: 6px;
 
       &.icon-bookmark-removed {
         background-image: url('#{$image-path}icon-removed-bookmark.svg');
       }
     }
 
     .story-context-label {
-      @include dark-theme-only {
-        color: $grey-40;
-      }
-
-      color: $grey-50;
+      color: var(--newtab-text-secondary-color);
       flex-grow: 1;
       font-size: 13px;
       line-height: 24px;
       overflow: hidden;
       text-overflow: ellipsis;
       white-space: nowrap;
     }
   }
@@ -60,43 +45,27 @@
 }
 
 .story-animate-enter-active {
   opacity: 1;
   transition: opacity 150ms ease-in 300ms;
 
   .story-badge-icon,
   .story-context-label {
-    @include dark-theme-only {
-      animation: dark-color 3s ease-out 0.3s;
-    }
-
     animation: color 3s ease-out 0.3s;
 
     @keyframes color {
       0% {
-        color: $status-green;
-        fill: $status-green;
+        color: var(--newtab-status-success);
+        fill: var(--newtab-status-success);
       }
 
       100% {
-        color: $grey-50;
-        fill: $grey-50;
-      }
-    }
-
-    @keyframes dark-color {
-      0% {
-        color: $status-dark-green;
-        fill: $status-dark-green;
-      }
-
-      100% {
-        color: $grey-40;
-        fill: $grey-40;
+        color: var(--newtab-text-secondary-color);
+        fill: var(--newtab-icon-secondary-color);
       }
     }
   }
 }
 
 .story-animate-exit {
   position: absolute;
   top: 0;
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSDismiss/_DSDismiss.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSDismiss/_DSDismiss.scss
@@ -21,48 +21,24 @@
     justify-content: center;
     position: absolute;
     inset-inline-end: 0;
     top: 0;
     border-radius: 50%;
     background-color: transparent;
 
     .icon {
-      @include dark-theme-only {
-        fill: $grey-20;
-      }
-
-      fill: $grey-50;
+      fill: var(--newtab-icon-primary-color);
     }
 
     &:hover {
-      @include dark-theme-only {
-        background: $grey-90-50;
-
-        .icon {
-          fill: $grey-10;
-        }
-      }
-
-      background: $grey-90-20;
-
-      .icon {
-        fill: $grey-80;
-      }
+      background: var(--newtab-element-hover-color);
     }
 
     &:active {
-      @include dark-theme-only {
-        background: $grey-90-70;
-      }
-
-      background: $grey-90-30;
+      background: var(--newtab-element-active-color);
     }
 
     &:focus {
-      @include dark-theme-only {
-        box-shadow: 0 0 0 2px $grey-80, 0 0 0 5px $blue-50-50;
-      }
-
-      box-shadow: 0 0 0 2px $white, 0 0 0 5px $blue-50-50;
+      box-shadow: $shadow-secondary;
     }
   }
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSEmptyState/_DSEmptyState.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSEmptyState/_DSEmptyState.scss
@@ -30,21 +30,17 @@
         @include ds-fade-in;
 
         @include dark-theme-only {
           @include ds-fade-in($blue-40-40);
         }
       }
 
       &:hover {
-        @include ds-fade-in($grey-30);
-
-        @include dark-theme-only {
-          @include ds-fade-in($grey-60);
-        }
+        @include ds-fade-in(var(--newtab-card-placeholder-color));
       }
     }
 
     &::after {
       content: '';
       height: 20px;
       width: 20px;
       animation: spinner 1s linear infinite;
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSMessage/_DSMessage.scss
@@ -1,38 +1,30 @@
 .ds-message {
   margin: 8px 0 0;
 
   .title {
     display: flex;
     align-items: center;
 
     .glyph {
-      @include dark-theme-only {
-        fill: $grey-30;
-      }
-
       width: 16px;
       height: 16px;
       margin: 0 6px 0 0;
       -moz-context-properties: fill;
-      fill: $grey-50;
+      fill: var(--newtab-icon-secondary-color);
       background-position: center center;
       background-size: 16px;
       background-repeat: no-repeat;
     }
 
     .title-text {
-      @include dark-theme-only {
-        color: $grey-30;
-      }
-
       line-height: 20px;
       font-size: 13px;
-      color: $grey-50;
+      color: var(--newtab-text-secondary-color);
       font-weight: 600;
       padding-right: 12px;
     }
 
     .link {
       line-height: 20px;
       font-size: 13px;
 
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/_DSPrivacyModal.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSPrivacyModal/_DSPrivacyModal.scss
@@ -1,26 +1,26 @@
 .ds-privacy-modal {
   .modal-link {
     display: flex;
     align-items: center;
     margin: 0 0 8px;
     border: 0;
     padding: 0;
-    color: $blue-60;
+    color: var(--newtab-link-primary-color);
     width: max-content;
 
     &:hover {
       text-decoration: underline;
       cursor: pointer;
     }
 
     &::before {
       -moz-context-properties: fill;
-      fill: $blue-60;
+      fill: var(--newtab-link-primary-color);
       content: '';
       display: inline-block;
       width: 16px;
       height: 16px;
       margin: 0;
       margin-inline-end: 8px;
       background-position: center center;
       background-repeat: no-repeat;
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSSignup/DSSignup.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSSignup/DSSignup.scss
@@ -11,17 +11,17 @@
     background: var(--newtab-element-hover-color);
     border-radius: 4px;
   }
 
   .icon-mail {
     height: 40px;
     width: 40px;
     margin-inline-end: 8px;
-    fill: #{$grey-40};
+    fill: var(--newtab-icon-secondary-color);
     background-size: 30px;
     flex-shrink: 0;
   }
 
   .ds-signup-content {
     display: flex;
     align-items: center;
     justify-content: center;
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSTextPromo/_DSTextPromo.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/DSTextPromo/_DSTextPromo.scss
@@ -1,19 +1,15 @@
 .ds-dismiss-ds-text-promo {
   max-width: 744px;
   margin: auto;
   overflow: hidden;
 
   &.hovering {
-    @include dark-theme-only {
-      background: $grey-90-30;
-    }
-
-    background: $grey-90-10;
+    background: var(--newtab-element-hover-color);
   }
 
   .ds-dismiss-button {
     margin-inline: 0 18px;
     margin-block: 18px 0;
   }
 }
 
@@ -43,77 +39,54 @@
     }
   }
 
   .text {
     line-height: 24px;
   }
 
   h3 {
-    @include dark-theme-only {
-      color: $grey-10;
-    }
-
+    color: var(--newtab-card-header-color);
     margin: 0;
     font-weight: 600;
     font-size: 15px;
   }
 
   .subtitle {
-    @include dark-theme-only {
-      color: $grey-40;
-    }
-
     font-size: 13px;
     margin: 0;
-    color: $grey-50;
+    color: var(--newtab-text-primary-color);
   }
 }
 
 .ds-chevron-link {
-  color: $blue-60;
+  color: var(--newtab-link-primary-color);
   display: inline-block;
   outline: 0;
 
   &:hover {
     text-decoration: underline;
   }
 
   &:active {
-    @include dark-theme-only {
-      color: $blue-50;
-    }
-
-    color: $blue-70;
+    color: var(--newtab-link-primary-active-color);
 
     &::after {
-      @include dark-theme-only {
-        background-color: $blue-50;
-      }
-
-      background-color: $blue-70;
+      background-color: var(--newtab-link-primary-active-color);
     }
   }
 
   &:focus {
-    @include dark-theme-only {
-      box-shadow: 0 0 0 2px $grey-80, 0 0 0 5px $blue-50-50;
-    }
-
-    box-shadow: 0 0 0 2px $white, 0 0 0 5px $blue-50-50;
+    box-shadow: $shadow-secondary;
     border-radius: 2px;
   }
 
   &::after {
-    @include dark-theme-only {
-      background-color: $blue-40;
-    }
-
+    background-color: var(--newtab-link-primary-color);
     content: ' ';
     mask: url('chrome://global/skin/icons/arrow-right-12.svg') 0 -8px no-repeat;
-    background-color: $blue-60;
     margin: 0 0 0 4px;
     width: 5px;
     height: 8px;
     text-decoration: none;
     display: inline-block;
   }
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Hero/_Hero.scss
@@ -10,21 +10,17 @@
 
   p {
     line-height: 1.538;
     margin: 8px 0;
   }
 
   .excerpt {
     @include limit-visible-lines(3, 24, 15);
-    @include dark-theme-only {
-      color: $grey-10;
-    }
-
-    color: $grey-90;
+    color: var(--newtab-card-header-color);
     margin: 0 0 10px;
   }
 
   .ds-card:not(.placeholder) {
     border: 0;
     padding-bottom: 20px;
 
     &:hover {
@@ -54,105 +50,81 @@
 
   .ds-hero-item {
     position: relative;
   }
 
   // "1/3 width layout" (aka "Mobile First")
   .wrapper {
     @include ds-border-top;
-    @include dark-theme-only {
-      color: $grey-30;
-    }
-
-    color: $grey-50;
+    color: var(--newtab-text-secondary-color);
     display: block;
     margin: 12px 0 16px;
     padding-top: 16px;
     height: 100%;
 
     &:focus {
       @include ds-fade-in;
     }
 
     @at-root .ds-hero-no-border .ds-hero-item .wrapper {
       border-top: 0;
       border-bottom: 0;
       padding: 0 0 8px;
     }
 
     &:hover .meta header {
-      @include dark-theme-only {
-        color: $blue-40;
-      }
-
-      color: $blue-60;
+      color: var(--newtab-link-primary-color);
     }
 
     &:active .meta header {
-      @include dark-theme-only {
-        color: $blue-40;
-      }
-
-      color: $blue-70;
+      color: var(--newtab-link-primary-active-color);
     }
 
     .img-wrapper {
       width: 100%;
     }
 
     .img {
       height: 0;
       padding-top: 50%; // 2:1 aspect ratio
 
       img {
         border-radius: 4px;
-        box-shadow: inset 0 0 0 0.5px $black-15;
+        box-shadow: $shadow-image-inset;
       }
     }
 
     .meta {
       display: block;
       flex-direction: column;
       justify-content: space-between;
 
       .header-and-excerpt {
         flex: 1;
       }
 
       header {
-        @include dark-theme-only {
-          color: $white;
-        }
-
         @include limit-visible-lines(4, 28, 22);
-        color: $grey-90;
+        color: var(--newtab-card-header-color);
         margin-bottom: 0;
       }
 
       .context,
       .source {
         margin: 0 0 4px;
       }
 
       .context {
-        @include dark-theme-only {
-          color: $teal-10;
-        }
-
-        color: $teal-70;
+        color: var(--newtab-link-secondary-color);
       }
 
       .source {
-        @include dark-theme-only {
-          color: $grey-40;
-        }
-
         font-size: 13px;
-        color: $grey-50;
+        color: var(--newtab-text-secondary-color);
         -webkit-line-clamp: 1;
         margin-bottom: 0;
       }
     }
   }
 
   // "2/3 width layout"
   .ds-column-5 &,
@@ -249,30 +221,30 @@
       grid-auto-rows: min-content;
 
       .ds-card {
         &:hover {
           @include dark-theme-only {
             background: none;
 
             .title {
-              color: $blue-40;
+              color: var(--newtab-link-primary-color);
             }
           }
         }
 
         &:active .title {
           @include dark-theme-only {
-            color: $blue-50;
+            color: var(--newtab-link-primary-active-color);
           }
         }
 
         .title {
           @include dark-theme-only {
-            color: $white;
+            color: var(--newtab-text-primary-color);
           }
 
           @include limit-visible-lines(3, 20, 14);
         }
       }
     }
   }
 
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/_List.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/List/_List.scss
@@ -75,21 +75,17 @@
 
   &:not(.ds-list-images) {
     .ds-list-image {
       display: none;
     }
   }
 
   a {
-    @include dark-theme-only {
-      color: $grey-10;
-    }
-
-    color: $grey-90;
+    color: var(--newtab-card-header-color);
   }
 }
 
 .ds-list-item-link:focus {
   @include ds-fade-in;
 }
 
 .ds-list-numbers {
@@ -100,48 +96,36 @@
   .ds-list-item {
     counter-increment: list;
   }
 
   .ds-list-item:not(.placeholder) > .ds-list-item-link {
     padding-inline-start: $counter-padded-size;
 
     &::before {
-      @include dark-theme-only {
-        background-color: $teal-70;
-      }
-
-      background-color: $pocket-teal;
+      background-color: var(--newtab-link-secondary-color);
       border-radius: $counter-size;
       color: $white;
       content: counter(list);
       font-size: 17px;
       height: $counter-size;
       line-height: $counter-size;
       margin-inline-start: -$counter-padded-size;
       margin-top: math.div($counter-whitespace, 2);
       position: absolute;
       text-align: center;
       width: $counter-size;
     }
 
     &:hover::before {
-      @include dark-theme-only {
-        background-color: $blue-40;
-      }
-
-      background-color: $blue-40;
+      background-color: var(--newtab-link-primary-color);
     }
 
     &:active::before {
-      @include dark-theme-only {
-        background-color: $blue-60;
-      }
-
-      background-color: $blue-70;
+      background-color: var(--newtab-link-primary-active-color);
     }
   }
 }
 
 .ds-list-borders {
   @include ds-border-top;
   grid-row-gap: $bordered-spacing;
   padding-top: $bordered-spacing;
@@ -203,36 +187,30 @@
 
     display: flex;
     justify-content: space-between;
     height: 100%;
   }
 
   .ds-list-item-excerpt {
     @include limit-visible-lines(2, $item-line-height, $item-font-size);
-    @include dark-theme-only {
-      color: $grey-10-80;
-    }
-    color: $grey-50;
+    color: var(--newtab-text-primary-color);
     margin: 4px 0 8px;
   }
 
   p {
     font-size: $item-font-size * 1px;
     line-height: $item-line-height * 1px;
     margin: 0;
   }
 
   .ds-list-item-info {
     @include limit-visible-lines(1, $item-line-height, $item-font-size);
-    @include dark-theme-only {
-      color: $grey-40;
-    }
 
-    color: $grey-50;
+    color: var(--newtab-text-secondary-color);
     font-size: 13px;
   }
 
   .ds-list-item-title {
     font-weight: 600;
     margin-bottom: 4px;
   }
 
@@ -248,24 +226,24 @@
 
   .ds-list-image {
     height: $item-image-size;
     margin-inline-start: $item-font-size * 1px;
     min-height: $item-image-size;
 
     img {
       border-radius: 4px;
-      box-shadow: inset 0 0 0 0.5px $black-15;
+      box-shadow: $shadow-image-inset;
     }
   }
 
   &:hover {
     .ds-list-item-title {
-      color: $blue-40;
+      color: var(--newtab-link-primary-color);
     }
   }
 
   &:active {
     .ds-list-item-title {
-      color: $blue-70;
+      color: var(--newtab-link-primary-active-color);
     }
   }
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/_Navigation.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/Navigation/_Navigation.scss
@@ -1,20 +1,10 @@
-.outer-wrapper.newtab-experience {
-  .ds-navigation {
-    color: var(--newtab-background-primary-text-color);
-  }
-}
-
 .ds-navigation {
-  @include dark-theme-only {
-    color: $grey-30;
-  }
-
-  color: $grey-50;
+  color: var(--newtab-background-primary-text-color);
   font-size: 11.5px;
   font-weight: 500;
   line-height: 22px;
   padding: 4px 0;
 
   @media (min-width: $break-point-widest) {
     line-height: 32px;
     font-size: 14px;
@@ -48,17 +38,17 @@
 
     a {
       &:hover,
       &:active {
         text-decoration: underline;
       }
 
       &:active {
-        color: $blue-70;
+        color: var(--newtab-link-primary-active-color);
       }
     }
   }
 
   .ds-navigation-header {
     padding-inline-end: 6px;
   }
 
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SectionTitle/_SectionTitle.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/SectionTitle/_SectionTitle.scss
@@ -1,26 +1,18 @@
 .ds-section-title {
   text-align: center;
   margin-top: 24px;
 
   .title {
-    @include dark-theme-only {
-      color: $white;
-    }
-
+    color: var(--newtab-section-header-text-color);
     line-height: 48px;
     font-size: 36px;
     font-weight: 300;
-    color: $grey-90;
   }
 
   .subtitle {
-    @include dark-theme-only {
-      color: $grey-30;
-    }
-
     line-height: 24px;
     font-size: 14px;
-    color: $grey-50;
+    color: var(--newtab-text-secondary-color);
     margin-top: 4px;
   }
 }
--- a/browser/components/newtab/content-src/components/DiscoveryStreamComponents/TopSites/_TopSites.scss
+++ b/browser/components/newtab/content-src/components/DiscoveryStreamComponents/TopSites/_TopSites.scss
@@ -1,57 +1,18 @@
-// ds topsites wraps the original topsites, with a few css changes.
-.outer-wrapper:not(.newtab-experience) {
+.outer-wrapper {
   .ds-top-sites {
-    // This is the override layer.
-    .top-sites {
-      .top-site-outer {
-        padding: 0 12px;
-
-        .top-site-inner > a:is(.active, :focus) .tile {
-          @include ds-fade-in;
-
-          @include dark-theme-only {
-            @include ds-fade-in($blue-40-40);
-          }
-        }
-
-        .top-site-inner > a:is(:hover) .tile {
-
-          @include ds-fade-in($grey-30);
-
-          @include dark-theme-only {
-            @include ds-fade-in($grey-60);
-          }
-        }
-      }
-
-      .top-sites-list {
-        margin: 0 -12px;
-      }
-    }
-  }
-}
-
-.outer-wrapper.newtab-experience {
-  .ds-top-sites {
-    // This is the override layer.
     .top-sites {
       .top-site-outer {
         .top-site-inner > a:is(.active, :focus) .tile {
-          @include ds-focus-nte;
+          @include ds-focus;
         }
 
         .top-site-inner > a:is(:hover) .top-site-inner {
-
-          @include ds-fade-in($grey-30);
-
-          @include dark-theme-only {
-            @include ds-fade-in($grey-60);
-          }
+          @include ds-fade-in(var(--newtab-topsites-background-color));
         }
       }
 
       .top-sites-list {
         margin: 0 -12px;
       }
     }
   }
@@ -111,9 +72,8 @@
       }
 
       .title {
         width: var(--rightPanelIconWidth);
       }
     }
   }
 }
-
--- a/browser/components/newtab/content-src/components/PocketLoggedInCta/_PocketLoggedInCta.scss
+++ b/browser/components/newtab/content-src/components/PocketLoggedInCta/_PocketLoggedInCta.scss
@@ -3,17 +3,17 @@
   $min-button-height: 18px;
   font-size: 13px;
   margin-inline-end: 20px;
   display: flex;
   align-items: flex-start;
 
   .pocket-cta-button {
     white-space: nowrap;
-    background: $blue-60;
+    background: var(--newtab-button-primary-color);
     letter-spacing: -0.34px;
     color: $white;
     border-radius: 4px;
     cursor: pointer;
     max-width: $max-button-width;
     // The button height is 2px taller than the rest of the cta text.
     // So I move it up by 1px to align with the rest of the cta text.
     margin-top: -1px;
--- a/browser/components/newtab/content-src/components/Search/Search.jsx
+++ b/browser/components/newtab/content-src/components/Search/Search.jsx
@@ -155,36 +155,29 @@ export class _Search extends React.PureC
     const wrapperClassName = [
       "search-wrapper",
       this.props.disable && "search-disabled",
       this.props.fakeFocus && "fake-focus",
     ]
       .filter(v => v)
       .join(" ");
 
-    const isNewNewtabExperienceEnabled = this.props.Prefs.values.featureConfig
-      .newNewtabExperienceEnabled;
-
     return (
       <div className={wrapperClassName}>
         {this.props.showLogo && (
           <div className="logo-and-wordmark">
             <div className="logo" />
             <div className="wordmark" />
           </div>
         )}
         {!this.props.handoffEnabled && (
           <div className="search-inner-wrapper">
             <input
               id="newtab-search-text"
-              data-l10n-id={
-                isNewNewtabExperienceEnabled
-                  ? "newtab-search-box-input"
-                  : "newtab-search-box-search-the-web-input"
-              }
+              data-l10n-id="newtab-search-box-input"
               maxLength="256"
               ref={this.onInputMount}
               type="search"
             />
             <button
               id="searchSubmit"
               className="search-button"
               data-l10n-id="newtab-search-box-search-button"
--- a/browser/components/newtab/content-src/components/Search/_Search.scss
+++ b/browser/components/newtab/content-src/components/Search/_Search.scss
@@ -1,110 +1,21 @@
 $search-height: 48px;
 $search-height-new: 52px;
 $search-icon-size: 24px;
-$search-icon-padding: 12px;
-$search-icon-width: 2 * $search-icon-padding + $search-icon-size - 2px;
+$search-icon-padding: 16px;
+$search-icon-width: 2 * $search-icon-padding + $search-icon-size - 4px;
 $search-button-width: 48px;
 $glyph-forward: url('chrome://browser/skin/forward.svg');
 
-.outer-wrapper.newtab-experience {
-  $search-icon-padding: 16px;
-  $search-icon-width: 2 * $search-icon-padding + $search-icon-size - 4px;
-
-  &.visible-logo {
-    .logo-and-wordmark {
-      .wordmark {
-        fill: var(--newtab-wordmark-color);
-      }
-    }
-  }
-
-  .search-wrapper {
-    padding-bottom: 38px;
-
-    .search-inner-wrapper {
-      min-height: $search-height-new;
-      width: $searchbar-width-small-new;
-
-      @media (min-width: $break-point-medium) {
-        width: $searchbar-width-medium-new;
-      }
-
-      @media (min-width: $break-point-large) {
-        width: $searchbar-width-large-new;
-      }
-
-      @media (min-width: $break-point-widest) {
-        width: $searchbar-width-largest-new;
-      }
-    }
-
-    .search-button {
-      &:focus {
-        outline: 0;
-        box-shadow: 0 0 0 2px var(--newtab-focus-outline);
-        border: 1px solid var(--newtab-focus-border);
-        border-radius: 0 $border-radius-new $border-radius-new 0;
-      }
-    }
-
-    input:focus {
-      outline: 0;
-      border: 1px solid var(--newtab-focus-border);
-      box-shadow: 0 0 0 2px var(--newtab-focus-outline);
-    }
-
-    &.fake-focus:not(.search.disabled) {
-      .search-handoff-button {
-        border: 1px solid var(--newtab-focus-border);
-        box-shadow: 0 0 0 2px var(--newtab-focus-outline);
-      }
-    }
-
-    .search-handoff-button,
-    input {
-      background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center no-repeat;
-      background-size: $search-icon-size;
-      padding-inline-start: $search-icon-width;
-      padding-inline-end: 10px;
-      box-shadow: var(--newtab-search-first-shadow);
-      border: 1px solid transparent;
-      border-radius: 8px;
-      color: var(--newtab-search-text-color);
-      font-weight: 500;
-      font-size: 15px;
-
-      &:dir(rtl) {
-        background-position-x: right $search-icon-padding;
-      }
-    }
-
-    .search-handoff-button {
-      padding-inline-end: 15px;
-      fill: currentColor;
-      -moz-context-properties: fill;
-
-      .fake-caret {
-        top: 18px;
-        inset-inline-start: $search-icon-width;
-
-        &:dir(rtl) {
-          background-position-x: right $search-icon-padding;
-        }
-      }
-    }
-  }
-}
-
 .search-wrapper {
-  padding: 34px 0 64px;
+  padding: 34px 0 38px;
 
   .only-search & {
-    padding: 0 0 64px;
+    padding: 0 0 38px;
   }
 
   .logo-and-wordmark {
     $logo-size: 82px;
     $wordmark-size: 134px;
 
     align-items: center;
     display: flex;
@@ -122,17 +33,17 @@
       width: $logo-size;
     }
 
     .wordmark {
       background: url('chrome://branding/content/firefox-wordmark.svg') no-repeat center center;
       background-size: $wordmark-size;
       -moz-context-properties: fill;
       display: inline-block;
-      fill: var(--newtab-search-wordmark-color);
+      fill: var(--newtab-wordmark-color);
       height: $logo-size;
       margin-inline-start: 16px;
       width: $wordmark-size;
     }
 
     @media (max-width: $break-point-medium - 1) {
       $logo-size-small: 64px;
       $wordmark-small-size: 100px;
@@ -150,96 +61,124 @@
         margin-inline-start: 12px;
       }
     }
   }
 
   .search-inner-wrapper {
     cursor: default;
     display: flex;
-    min-height: $search-height;
+    min-height: $search-height-new;
     margin: 0 auto;
     position: relative;
     width: $searchbar-width-small;
 
-    .ds-outer-wrapper-breakpoint-override & {
-      width: 216px;
-    }
-
     @media (min-width: $break-point-medium) {
       width: $searchbar-width-medium;
-
-      .ds-outer-wrapper-breakpoint-override & {
-        width: 460px;
-      }
     }
 
     @media (min-width: $break-point-large) {
       width: $searchbar-width-large;
+    }
 
-      .ds-outer-wrapper-breakpoint-override & {
-        width: 696px;
-      }
+    @media (min-width: $break-point-widest) {
+      width: $searchbar-width-largest;
     }
   }
 
+  .search-handoff-button,
   input {
     background: var(--newtab-textbox-background-color) var(--newtab-search-icon) $search-icon-padding center no-repeat;
     background-size: $search-icon-size;
-    border: solid 1px var(--newtab-search-border-color);
-    box-shadow: $shadow-secondary, 0 0 0 1px $black-15;
-    font-size: 15px;
+    padding-inline-start: $search-icon-width;
+    padding-inline-end: 10px;
+    padding-block: 0;
+    width: 100%;
+    box-shadow: var(--newtab-search-first-shadow);
+    border: 1px solid transparent;
+    border-radius: 8px;
+    color: var(--newtab-search-text-color);
     -moz-context-properties: fill;
-    fill: var(--newtab-search-icon-color);
-    padding: 0;
-    padding-inline-end: $search-button-width;
-    padding-inline-start: $search-icon-width;
-    width: 100%;
+    fill: var(--newtab-icon-secondary-color);
 
     &:dir(rtl) {
       background-position-x: right $search-icon-padding;
     }
   }
 
-  &:hover input {
-    box-shadow: $shadow-secondary, 0 0 0 1px $black-25;
-  }
-
   .search-inner-wrapper:active input,
   input:focus {
-    border: $input-border-active;
-    box-shadow: var(--newtab-textbox-focus-boxshadow);
+    border: 1px solid var(--newtab-focus-border);
+    outline: 0;
+    box-shadow: $shadow-focus;
   }
 
   .search-button {
     background: $glyph-forward no-repeat center center;
     background-size: 16px 16px;
     border: 0;
     border-radius: 0 $border-radius $border-radius 0;
     -moz-context-properties: fill;
-    fill: var(--newtab-search-icon-color);
+    fill: var(--newtab-icon-secondary-color);
     height: 100%;
     inset-inline-end: 0;
     position: absolute;
     width: $search-button-width;
 
     &:focus,
     &:hover {
-      background-color: $grey-90-10;
+      background-color: var(--newtab-element-hover-color);
       cursor: pointer;
     }
 
+    &:focus {
+      outline: 0;
+      box-shadow: $shadow-focus;
+      border: 1px solid var(--newtab-focus-border);
+      border-radius: 0 $border-radius-new $border-radius-new 0;
+    }
+
     &:active {
-      background-color: $grey-90-20;
+      background-color: var(--newtab-element-hover-color);
     }
 
     &:dir(rtl) {
       transform: scaleX(-1);
     }
   }
+
+  &.fake-focus:not(.search.disabled) {
+    .search-handoff-button {
+      border: 1px solid var(--newtab-focus-border);
+      box-shadow: $shadow-focus;
+    }
+  }
+
+  .search-handoff-button {
+    padding-inline-end: 15px;
+    fill: currentColor;
+    -moz-context-properties: fill;
+
+    .fake-caret {
+      top: 18px;
+      inset-inline-start: $search-icon-width;
+
+      &:dir(rtl) {
+        background-position-x: right $search-icon-padding;
+      }
+    }
+  }
+
+  &.visible-logo {
+    .logo-and-wordmark {
+      .wordmark {
+        fill: var(--newtab-wordmark-color);
+      }
+    }
+  }
 }
 
 .non-collapsible-section + .below-search-snippet-wrapper {
   // If search is enabled, we need to invade its large bottom padding.
   margin-top: -48px;
 }
 
 @media (max-height: 700px) {
@@ -270,20 +209,16 @@
   padding-inline-start: 46px;
   opacity: 1;
   width: 100%;
 
   &:dir(rtl) {
     background-position-x: right $search-icon-padding;
   }
 
-  &:hover {
-    box-shadow: $shadow-secondary, 0 0 0 1px $black-25;
-  }
-
   .fake-focus:not(.search-disabled) & {
     border: $input-border-active;
     box-shadow: var(--newtab-textbox-focus-boxshadow);
 
     .fake-caret {
       display: block;
     }
   }
@@ -330,32 +265,32 @@
       }
     }
   }
 }
 
 @media (min-height: 701px) {
   body:not(.inline-onboarding) .fixed-search {
     main {
-      padding-top: 146px;
+      padding-top: 124px;
     }
 
     &.visible-logo {
       main {
-        padding-top: 276px;
+        padding-top: 254px;
       }
     }
 
     .search-wrapper {
-      $search-height: 35px;
-      $search-icon-size: 16px;
+      $search-height: 45px;
+      $search-icon-size: 24px;
       $search-icon-padding: 16px;
 
-      border-bottom: solid 1px var(--newtab-border-secondary-color);
-      padding: 30px 0;
+      border-bottom: solid 1px var(--newtab-seperator-line-color);
+      padding: 27px 0;
       $search-header-bar-height: 95px;
       background-color: var(--newtab-search-header-background-color);
       min-height: $search-header-bar-height;
       left: 0;
       position: fixed;
       top: 0;
       width: 100%;
       z-index: 9;
@@ -377,50 +312,16 @@
         top: 14px;
       }
 
       .logo-and-wordmark {
         display: none;
       }
     }
 
-    &.newtab-experience {
-      main {
-        padding-top: 124px;
-      }
-
-      &.visible-logo {
-        main {
-          padding-top: 254px;
-        }
-      }
-
-      .search-wrapper {
-        $search-height: 45px;
-        $search-icon-size: 24px;
-        $search-icon-padding: 16px;
-
-        border-bottom: solid 1px var(--newtab-seperator-line-color);
-        padding: 27px 0;
-
-        .search-inner-wrapper {
-          min-height: $search-height;
-        }
-
-        input {
-          background-position-x: $search-icon-padding;
-          background-size: $search-icon-size;
-
-          &:dir(rtl) {
-            background-position-x: right $search-icon-padding;
-          }
-        }
-      }
-    }
-
     .search-handoff-button {
       background-position-x: $search-icon-padding;
       background-size: $search-icon-size;
 
       &:dir(rtl) {
         background-position-x: right $search-icon-padding;
       }
 
deleted file mode 100644
--- a/browser/components/newtab/content-src/components/SectionMenu/SectionMenu.jsx
+++ /dev/null
@@ -1,122 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-import { actionCreators as ac } from "common/Actions.jsm";
-import { ContextMenu } from "content-src/components/ContextMenu/ContextMenu";
-import React from "react";
-import { connect } from "react-redux";
-import { SectionMenuOptions } from "content-src/lib/section-menu-options";
-
-const DEFAULT_SECTION_MENU_OPTIONS = [
-  "MoveUp",
-  "MoveDown",
-  "Separator",
-  "RemoveSection",
-  "CheckCollapsed",
-  "Separator",
-  "ManageSection",
-];
-const WEBEXT_SECTION_MENU_OPTIONS = [
-  "MoveUp",
-  "MoveDown",
-  "Separator",
-  "CheckCollapsed",
-  "Separator",
-  "ManageWebExtension",
-];
-
-export class _SectionMenu extends React.PureComponent {
-  handleAddWhileCollapsed() {
-    const { action, userEvent } = SectionMenuOptions.ExpandSection(this.props);
-    this.props.dispatch(action);
-    if (userEvent) {
-      this.props.dispatch(
-        ac.UserEvent({
-          event: userEvent,
-          source: this.props.source,
-        })
-      );
-    }
-  }
-
-  getOptions() {
-    const { props } = this;
-
-    const propOptions = props.isWebExtension
-      ? [...WEBEXT_SECTION_MENU_OPTIONS]
-      : [...DEFAULT_SECTION_MENU_OPTIONS];
-
-    // Remove Collapse/Expand related option if the `newNewtabExperience.enabled`
-    // pref is set to true.
-    if (props.Prefs.values.featureConfig.newNewtabExperienceEnabled) {
-      if (props.isWebExtension) {
-        propOptions.splice(2, 2);
-      } else {
-        propOptions.splice(4, 1);
-      }
-    }
-
-    // Remove the move related options if the section is fixed
-    if (props.isFixed) {
-      propOptions.splice(propOptions.indexOf("MoveUp"), 3);
-    }
-    // Prepend custom options and a separator
-    if (props.extraOptions) {
-      propOptions.splice(0, 0, ...props.extraOptions, "Separator");
-    }
-    // Insert privacy notice before the last option ("ManageSection")
-    if (props.privacyNoticeURL) {
-      propOptions.splice(-1, 0, "PrivacyNotice");
-    }
-
-    const options = propOptions
-      .map(o => SectionMenuOptions[o](props))
-      .map(option => {
-        const { action, id, type, userEvent } = option;
-        if (!type && id) {
-          option.onClick = () => {
-            const hasAddEvent =
-              userEvent === "MENU_ADD_TOPSITE" ||
-              userEvent === "MENU_ADD_SEARCH";
-
-            if (props.collapsed && hasAddEvent) {
-              this.handleAddWhileCollapsed();
-            }
-
-            props.dispatch(action);
-            if (userEvent) {
-              props.dispatch(
-                ac.UserEvent({
-                  event: userEvent,
-                  source: props.source,
-                })
-              );
-            }
-          };
-        }
-        return option;
-      });
-
-    // This is for accessibility to support making each item tabbable.
-    // We want to know which item is the first and which item
-    // is the last, so we can close the context menu accordingly.
-    options[0].first = true;
-    options[options.length - 1].last = true;
-    return options;
-  }
-
-  render() {
-    return (
-      <ContextMenu
-        onUpdate={this.props.onUpdate}
-        options={this.getOptions()}
-        keyboardAccess={this.props.keyboardAccess}
-      />
-    );
-  }
-}
-
-export const SectionMenu = connect(state => ({
-  Prefs: state.Prefs,
-}))(_SectionMenu);
--- a/browser/components/newtab/content-src/components/Sections/Sections.jsx
+++ b/browser/components/newtab/content-src/components/Sections/Sections.jsx
@@ -161,17 +161,16 @@ export class Section extends React.PureC
     }
   }
 
   render() {
     const {
       id,
       eventSource,
       title,
-      icon,
       rows,
       Pocket,
       topics,
       emptyState,
       dispatch,
       compactCards,
       read_more_endpoint,
       contextMenuOptions,
@@ -261,17 +260,16 @@ export class Section extends React.PureC
     ].join(" ");
 
     // <Section> <-- React component
     // <section> <-- HTML5 element
     return (
       <ComponentPerfTimer {...this.props}>
         <CollapsibleSection
           className={sectionClassName}
-          icon={icon}
           title={title}
           id={id}
           eventSource={eventSource}
           collapsed={this.props.pref.collapsed}
           showPrefName={(pref && pref.feed) || id}
           privacyNoticeURL={privacyNoticeURL}
           Prefs={this.props.Prefs}
           isFixed={this.props.isFixed}