Merge try head draft
authorConnor Sheehan <sheehan@mozilla.com>
Sat, 18 Sep 2021 21:46:00 -0400
changeset 3969597 8b0f227f713ed23e00735f5bc59255365313df74
parent 3969596 b0f591fb1f45c241eacc9418d5505b016a5197e8 (diff)
parent 3892140 6e4902227e5600c8d1b6fe6036330eab702a73ac (current diff)
child 3969598 7024082346fb5304120f8e38e20bb18a3496e6e4
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,65 +1,59 @@
 # 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.30"
+tag = "v0.5.0"
 
 [source."https://github.com/mozilla/mp4parse-rust"]
 git = "https://github.com/mozilla/mp4parse-rust"
 replace-with = "vendored-sources"
-rev = "62aa5d4e46289351ebfa2712668077ba8aa81064"
+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"
 replace-with = "vendored-sources"
 rev = "3ad5978575f501ab10b1753626f176f1bba3f584"
 
 [source."https://github.com/mozilla/cubeb-coreaudio-rs"]
 git = "https://github.com/mozilla/cubeb-coreaudio-rs"
 replace-with = "vendored-sources"
-rev = "8ae48c16b637d6d9a841cf4d01de06c16b1318b0"
+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 = "eb250ba0bda3551927a447db0335a309e3f296d3"
+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/.eslintrc.js
+++ b/.eslintrc.js
@@ -334,57 +334,16 @@ module.exports = {
         "no-undef": "off",
         "no-unsanitized/property": "off",
         "no-unused-vars": "off",
         "no-useless-call": "off",
       },
     },
     {
       files: [
-        "docshell/test/chrome/bug113934_window.xhtml",
-        "docshell/test/chrome/bug215405_window.xhtml",
-        "docshell/test/chrome/bug293235_window.xhtml",
-        "docshell/test/chrome/bug294258_window.xhtml",
-        "docshell/test/chrome/bug298622_window.xhtml",
-        "docshell/test/chrome/bug301397_window.xhtml",
-        "docshell/test/chrome/bug303267_window.xhtml",
-        "docshell/test/chrome/bug311007_window.xhtml",
-        "docshell/test/chrome/bug321671_window.xhtml",
-        "docshell/test/chrome/bug360511_window.xhtml",
-        "docshell/test/chrome/bug396519_window.xhtml",
-        "docshell/test/chrome/bug396649_window.xhtml",
-        "docshell/test/chrome/bug449778_window.xhtml",
-        "docshell/test/chrome/bug449780_window.xhtml",
-        "docshell/test/chrome/bug582176_window.xhtml",
-        "docshell/test/chrome/bug662200_window.xhtml",
-        "docshell/test/chrome/bug690056_window.xhtml",
-        "docshell/test/chrome/bug89419_window.xhtml",
-        "docshell/test/chrome/mozFrameType_window.xhtml",
-        "docshell/test/chrome/test_bug453650.xhtml",
-        "docshell/test/chrome/test_bug454235.xhtml",
-        "docshell/test/chrome/test_bug565388.xhtml",
-        "docshell/test/chrome/test_bug608669.xhtml",
-        "docshell/test/chrome/test_bug789773.xhtml",
-        "docshell/test/chrome/test_bug846906.xhtml",
-        "docshell/test/chrome/test_docRedirect.xhtml",
-        "docshell/test/chrome/test_principalInherit.xhtml",
-        "docshell/test/chrome/test_viewsource_forbidden_in_iframe.xhtml",
-      ],
-      rules: {
-        "no-global-assign": "off",
-        "no-octal": "off",
-        "mozilla/no-useless-removeEventListener": "off",
-        "mozilla/use-services": "off",
-        "mozilla/use-chromeutils-generateqi": "off",
-        "no-delete-var": "off",
-        "no-redeclare": "off",
-      },
-    },
-    {
-      files: [
         "dom/base/test/chrome/file_bug1139964.xhtml",
         "dom/base/test/chrome/file_bug549682.xhtml",
         "dom/base/test/chrome/file_bug616841.xhtml",
         "dom/base/test/chrome/file_bug990812-1.xhtml",
         "dom/base/test/chrome/file_bug990812-2.xhtml",
         "dom/base/test/chrome/file_bug990812-3.xhtml",
         "dom/base/test/chrome/file_bug990812-4.xhtml",
         "dom/base/test/chrome/file_bug990812-5.xhtml",
--- a/.hgignore
+++ b/.hgignore
@@ -163,17 +163,16 @@ compile_commands\.json
 ^testing/talos/talos/fis/tp5n.tar.gz
 ^testing/talos/talos/fis/tp5n
 ^testing/talos/talos/tests/tp5n.zip
 ^testing/talos/talos/tests/tp5n.tar.gz
 ^testing/talos/talos/tests/tp5n
 ^testing/talos/talos/tests/devtools/damp.manifest.develop
 ^testing/talos/talos/startup_test/startup_about_home_paint/startup_about_home_paint.manifest.develop
 ^testing/talos/talos/webextensions/
-^testing/talos/talos/tests/about-newtab/about_newtab.manifest.develop
 ^talos-venv
 ^py3venv
 ^testing/talos/talos/mitmproxy/mitmdump
 ^testing/talos/talos/mitmproxy/mitmproxy
 ^testing/talos/talos/mitmproxy/mitmweb
 
 # Ignore talos webkit benchmark files; source is copied from in-tree /third_party
 # into testing/talos/talos/tests/webkit/PerformanceTests/ when run locally
--- 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",
 ]
@@ -753,17 +757,17 @@ source = "registry+https://github.com/ru
 checksum = "2b7e3347be6a09b46aba228d6608386739fb70beff4f61e07422da87b0bb31fa"
 dependencies = [
  "bindgen",
 ]
 
 [[package]]
 name = "coreaudio-sys-utils"
 version = "0.1.0"
-source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=8ae48c16b637d6d9a841cf4d01de06c16b1318b0#8ae48c16b637d6d9a841cf4d01de06c16b1318b0"
+source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=4ee3b283182926f9a8bb31d603219ff2db70d2fd#4ee3b283182926f9a8bb31d603219ff2db70d2fd"
 dependencies = [
  "core-foundation-sys",
  "coreaudio-sys",
 ]
 
 [[package]]
 name = "cose"
 version = "0.1.4"
@@ -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",
@@ -1008,17 +1026,17 @@ checksum = "40aa77fed9bd627aaf11654c4d27
 dependencies = [
  "bitflags",
  "cubeb-sys",
 ]
 
 [[package]]
 name = "cubeb-coreaudio"
 version = "0.1.0"
-source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=8ae48c16b637d6d9a841cf4d01de06c16b1318b0#8ae48c16b637d6d9a841cf4d01de06c16b1318b0"
+source = "git+https://github.com/mozilla/cubeb-coreaudio-rs?rev=4ee3b283182926f9a8bb31d603219ff2db70d2fd#4ee3b283182926f9a8bb31d603219ff2db70d2fd"
 dependencies = [
  "atomic",
  "audio-mixer",
  "bitflags",
  "coreaudio-sys-utils",
  "cubeb-backend",
  "float-cmp",
  "lazy_static",
@@ -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,92 +2440,97 @@ 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=eb250ba0bda3551927a447db0335a309e3f296d3#eb250ba0bda3551927a447db0335a309e3f296d3"
+source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 dependencies = [
  "jsparagus-ast",
  "jsparagus-emitter",
  "jsparagus-generated-parser",
  "jsparagus-json-log",
  "jsparagus-parser",
  "jsparagus-scope",
  "jsparagus-stencil",
 ]
 
 [[package]]
 name = "jsparagus-ast"
 version = "0.1.0"
-source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eb250ba0bda3551927a447db0335a309e3f296d3#eb250ba0bda3551927a447db0335a309e3f296d3"
+source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 dependencies = [
  "bumpalo",
  "indexmap",
 ]
 
 [[package]]
 name = "jsparagus-emitter"
 version = "0.1.0"
-source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eb250ba0bda3551927a447db0335a309e3f296d3#eb250ba0bda3551927a447db0335a309e3f296d3"
+source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 dependencies = [
  "bumpalo",
  "byteorder",
  "indexmap",
  "jsparagus-ast",
  "jsparagus-scope",
  "jsparagus-stencil",
 ]
 
 [[package]]
 name = "jsparagus-generated-parser"
 version = "0.1.0"
-source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eb250ba0bda3551927a447db0335a309e3f296d3#eb250ba0bda3551927a447db0335a309e3f296d3"
+source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 dependencies = [
  "bumpalo",
  "jsparagus-ast",
  "static_assertions",
 ]
 
 [[package]]
 name = "jsparagus-json-log"
 version = "0.1.0"
-source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eb250ba0bda3551927a447db0335a309e3f296d3#eb250ba0bda3551927a447db0335a309e3f296d3"
+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=eb250ba0bda3551927a447db0335a309e3f296d3#eb250ba0bda3551927a447db0335a309e3f296d3"
-dependencies = [
- "arrayvec",
+source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
+dependencies = [
+ "arrayvec 0.5.2",
  "bumpalo",
  "jsparagus-ast",
  "jsparagus-generated-parser",
  "jsparagus-json-log",
 ]
 
 [[package]]
 name = "jsparagus-scope"
 version = "0.1.0"
-source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eb250ba0bda3551927a447db0335a309e3f296d3#eb250ba0bda3551927a447db0335a309e3f296d3"
+source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 dependencies = [
  "indexmap",
  "jsparagus-ast",
  "jsparagus-stencil",
 ]
 
 [[package]]
 name = "jsparagus-stencil"
 version = "0.1.0"
-source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=eb250ba0bda3551927a447db0335a309e3f296d3#eb250ba0bda3551927a447db0335a309e3f296d3"
+source = "git+https://github.com/mozilla-spidermonkey/jsparagus?rev=2e56bb9bae5d8211137980a717ee991cc4a5eb98#2e56bb9bae5d8211137980a717ee991cc4a5eb98"
 dependencies = [
  "jsparagus-ast",
 ]
 
 [[package]]
 name = "jsrust"
 version = "0.1.0"
 dependencies = [
@@ -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=62aa5d4e46289351ebfa2712668077ba8aa81064#62aa5d4e46289351ebfa2712668077ba8aa81064"
+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=62aa5d4e46289351ebfa2712668077ba8aa81064#62aa5d4e46289351ebfa2712668077ba8aa81064"
+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.30"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.30#77070a9df73680edc58e54f143b3252ab2ee59cd"
+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.30"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.30#77070a9df73680edc58e54f143b3252ab2ee59cd"
+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.30"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.30#77070a9df73680edc58e54f143b3252ab2ee59cd"
+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.30"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.30#77070a9df73680edc58e54f143b3252ab2ee59cd"
+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.30"
-source = "git+https://github.com/mozilla/neqo?tag=v0.4.30#77070a9df73680edc58e54f143b3252ab2ee59cd"
+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
@@ -510,21 +510,18 @@ void finalizeCB(GObject* aObj) {
   // finalize of GObjectClass will unref the accessible parent if has
   if (G_OBJECT_CLASS(parent_class)->finalize) {
     G_OBJECT_CLASS(parent_class)->finalize(aObj);
   }
 }
 
 const gchar* getNameCB(AtkObject* aAtkObj) {
   nsAutoString name;
-  AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
-  if (accWrap) {
-    accWrap->Name(name);
-  } else if (RemoteAccessible* proxy = GetProxy(aAtkObj)) {
-    proxy->Name(name);
+  if (Accessible* acc = GetInternalObj(aAtkObj)) {
+    acc->Name(name);
   } else {
     return nullptr;
   }
 
   // XXX Firing an event from here does not seem right
   MaybeFireNameChange(aAtkObj, name);
 
   return aAtkObj->name;
@@ -549,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
@@ -13,81 +13,65 @@
 #include "mozilla/Likely.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 extern "C" {
 
 static void getCurrentValueCB(AtkValue* obj, GValue* value) {
-  RemoteAccessible* proxy = nullptr;
-  AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
-  if (!accWrap) {
-    proxy = GetProxy(ATK_OBJECT(obj));
-    if (!proxy) {
-      return;
-    }
+  Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+  if (!acc) {
+    return;
   }
 
   memset(value, 0, sizeof(GValue));
-  double accValue = accWrap ? accWrap->CurValue() : proxy->CurValue();
+  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) {
-  RemoteAccessible* proxy = nullptr;
-  AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
-  if (!accWrap) {
-    proxy = GetProxy(ATK_OBJECT(obj));
-    if (!proxy) {
-      return;
-    }
+  Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+  if (!acc) {
+    return;
   }
 
   memset(value, 0, sizeof(GValue));
-  double accValue = accWrap ? accWrap->MaxValue() : proxy->MaxValue();
+  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) {
-  RemoteAccessible* proxy = nullptr;
-  AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
-  if (!accWrap) {
-    proxy = GetProxy(ATK_OBJECT(obj));
-    if (!proxy) {
-      return;
-    }
+  Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+  if (!acc) {
+    return;
   }
 
   memset(value, 0, sizeof(GValue));
-  double accValue = accWrap ? accWrap->MinValue() : proxy->MinValue();
+  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) {
-  RemoteAccessible* proxy = nullptr;
-  AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
-  if (!accWrap) {
-    proxy = GetProxy(ATK_OBJECT(obj));
-    if (!proxy) {
-      return;
-    }
+  Accessible* acc = GetInternalObj(ATK_OBJECT(obj));
+  if (!acc) {
+    return;
   }
 
   memset(minimumIncrement, 0, sizeof(GValue));
-  double accValue = accWrap ? accWrap->Step() : proxy->Step();
+  double accValue = acc->Step();
   if (IsNaN(accValue)) {
     accValue = 0;  // zero if the minimum increment is undefined
   }
 
   g_value_init(minimumIncrement, G_TYPE_DOUBLE);
   g_value_set_double(minimumIncrement, accValue);
 }
 
--- a/accessible/base/ARIAMap.cpp
+++ b/accessible/base/ARIAMap.cpp
@@ -919,16 +919,26 @@ static const nsRoleMapEntry sWAIRoleMaps
     eNoValue,
     eClickAction,
     eNoLiveAttr,
     kGenericAccType,
     kNoReqStates,
     eARIACheckableBool,
     eARIAReadonly
   },
+  { // meter
+    nsGkAtoms::meter,
+    roles::METER,
+    kUseMapRole,
+    eHasValueMinMax,
+    eNoAction,
+    eNoLiveAttr,
+    kGenericAccType,
+    states::READONLY
+  },
   { // navigation
     nsGkAtoms::navigation,
     roles::LANDMARK,
     kUseMapRole,
     eNoValue,
     eNoAction,
     eNoLiveAttr,
     eLandmark,
--- a/accessible/base/AccAttributes.cpp
+++ b/accessible/base/AccAttributes.cpp
@@ -25,30 +25,46 @@ void AccAttributes::StringFromValueAndNa
   aValue.match(
       [&aValueString](const bool& val) {
         aValueString.Assign(val ? u"true" : u"false");
       },
       [&aValueString](const float& val) {
         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,29 @@ 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, int32_t, RefPtr<nsAtom>, CSSCoord, FontSize, Color>;
+      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;
   AccAttributes(AccAttributes&&) = delete;
--- a/accessible/base/AccIterator.cpp
+++ b/accessible/base/AccIterator.cpp
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AccIterator.h"
 
 #include "AccGroupInfo.h"
+#include "DocAccessible-inl.h"
 #ifdef MOZ_XUL
 #  include "XULTreeAccessible.h"
 #endif
 
 #include "mozilla/dom/DocumentOrShadowRoot.h"
 #include "mozilla/dom/HTMLLabelElement.h"
 
 using namespace mozilla;
new file mode 100644
--- /dev/null
+++ b/accessible/base/CacheConstants.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set expandtab shiftwidth=2 tabstop=2: */
+/* 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 _CacheConstants_h_
+#define _CacheConstants_h_
+
+namespace mozilla {
+namespace a11y {
+
+class CacheDomain {
+ public:
+  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/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -5,16 +5,17 @@
 
 #include "EventTree.h"
 
 #include "LocalAccessible-inl.h"
 #include "EmbeddedObjCollector.h"
 #include "NotificationController.h"
 #include "nsEventShell.h"
 #include "DocAccessible.h"
+#include "DocAccessible-inl.h"
 #ifdef A11Y_LOG
 #  include "Logging.h"
 #endif
 
 #include "mozilla/UniquePtr.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Logging.h"
 
 #include "LocalAccessible-inl.h"
 #include "AccEvent.h"
 #include "DocAccessible.h"
+#include "DocAccessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsCoreUtils.h"
 #include "OuterDocAccessible.h"
 
 #include "nsDocShellLoadTypes.h"
 #include "nsIChannel.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIWebProgress.h"
@@ -110,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/moz.build
+++ b/accessible/base/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS += ["AccEvent.h", "nsAccessibilityService.h"]
 
 EXPORTS.mozilla.a11y += [
     "AccAttributes.h",
     "AccTypes.h",
+    "CacheConstants.h",
     "DocManager.h",
     "FocusManager.h",
     "IDSet.h",
     "Platform.h",
     "RelationType.h",
     "Role.h",
     "SelectionManager.h",
     "States.h",
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -768,16 +768,19 @@ already_AddRefed<DOMStringList> nsAccess
     stringStates->Add(u"stale"_ns);
   }
   if (aStates & states::ENABLED) {
     stringStates->Add(u"enabled"_ns);
   }
   if (aStates & states::SENSITIVE) {
     stringStates->Add(u"sensitive"_ns);
   }
+  if (aStates & states::EXPANDABLE) {
+    stringStates->Add(u"expandable"_ns);
+  }
   if (aStates & states::PINNED) {
     stringStates->Add(u"pinned"_ns);
   }
   if (aStates & states::CURRENT) {
     stringStates->Add(u"current"_ns);
   }
 
   return stringStates.forget();
--- a/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -26,17 +26,19 @@
 #include "mozilla/PresShell.h"
 #include "mozilla/TouchEvents.h"
 #include "nsView.h"
 #include "nsGkAtoms.h"
 
 #include "nsComponentManagerUtils.h"
 
 #include "XULTreeElement.h"
+#include "nsIContentInlines.h"
 #include "nsTreeColumns.h"
+#include "mozilla/dom/DocumentInlines.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLLabelElement.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/Selection.h"
 
 using namespace mozilla;
 
 using mozilla::dom::DOMRect;
@@ -349,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/base/nsEventShell.cpp
+++ b/accessible/base/nsEventShell.cpp
@@ -5,16 +5,17 @@
 
 #include "nsEventShell.h"
 
 #include "nsAccUtils.h"
 #include "Logging.h"
 #include "AccAttributes.h"
 
 #include "mozilla/StaticPtr.h"
+#include "mozilla/dom/DOMStringList.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsEventShell
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -31,16 +32,25 @@ void nsEventShell::FireEvent(AccEvent* a
   }
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eEvents)) {
     logging::MsgBegin("EVENTS", "events fired");
     nsAutoString type;
     GetAccService()->GetStringEventType(aEvent->GetEventType(), type);
     logging::MsgEntry("type: %s", NS_ConvertUTF16toUTF8(type).get());
+    if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_STATE_CHANGE) {
+      AccStateChangeEvent* event = downcast_accEvent(aEvent);
+      RefPtr<dom::DOMStringList> stringStates =
+          GetAccService()->GetStringStates(event->GetState());
+      nsAutoString state;
+      stringStates->Item(0, state);
+      logging::MsgEntry("state: %s = %s", NS_ConvertUTF16toUTF8(state).get(),
+                        event->IsStateEnabled() ? "true" : "false");
+    }
     logging::AccessibleInfo("target", aEvent->GetAccessible());
     logging::MsgEnd();
   }
 #endif
 
   accessible->HandleAccEvent(aEvent);
   aEvent->mEventRule = AccEvent::eDoNotEmit;
 
--- a/accessible/base/nsTextEquivUtils.cpp
+++ b/accessible/base/nsTextEquivUtils.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsTextEquivUtils.h"
 
 #include "LocalAccessible-inl.h"
 #include "AccIterator.h"
 #include "nsCoreUtils.h"
 #include "mozilla/dom/Text.h"
+#include "nsIContentInlines.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 /**
  * The accessible for which we are computing a text equivalent. It is useful
  * for bailing out during recursive text computation, or for special cases
  * like step f. of the ARIA implementation guide.
--- a/accessible/basetypes/Accessible.h
+++ b/accessible/basetypes/Accessible.h
@@ -1,30 +1,58 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 _Accessible_H_
 #define _Accessible_H_
 
-#include <stdint.h>
 #include "mozilla/a11y/Role.h"
 #include "mozilla/a11y/AccTypes.h"
+#include "nsString.h"
 
 class nsAtom;
 
 struct nsRoleMapEntry;
 
 namespace mozilla {
 namespace a11y {
 
 class LocalAccessible;
 class RemoteAccessible;
 
+/**
+ * Name type flags.
+ */
+enum ENameValueFlag {
+  /**
+   * Name either
+   *  a) present (not empty): !name.IsEmpty()
+   *  b) no name (was missed): name.IsVoid()
+   */
+  eNameOK,
+
+  /**
+   * Name was left empty by the author on purpose:
+   * name.IsEmpty() && !name.IsVoid().
+   */
+  eNoNameOnPurpose,
+
+  /**
+   * Name was computed from the subtree.
+   */
+  eNameFromSubtree,
+
+  /**
+   * Tooltip was used as a name.
+   */
+  eNameFromTooltip
+};
+
 class Accessible {
  protected:
   Accessible();
 
   Accessible(AccType aType, AccGenericType aGenericTypes,
              uint8_t aRoleMapEntryIndex);
 
  public:
@@ -75,16 +103,33 @@ class Accessible {
   bool IsARIARole(nsAtom* aARIARole) const;
   bool HasStrongARIARole() const;
 
   /**
    * Return true if the accessible belongs to the given accessible type.
    */
   bool HasGenericType(AccGenericType aType) const;
 
+  // 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); }
 
   bool IsTableRow() const { return HasGenericType(eTableRow); }
 
   /**
    * Note: The eTable* types defined in the ARIA map are used in
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -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/. */
 
 #include "ApplicationAccessible.h"
 
 #include "AccAttributes.h"
+#include "LocalAccessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/Document.h"
@@ -52,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
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "LocalAccessible-inl.h"
 
 #include "EmbeddedObjCollector.h"
 #include "AccAttributes.h"
 #include "AccGroupInfo.h"
 #include "AccIterator.h"
+#include "CacheConstants.h"
+#include "DocAccessible-inl.h"
 #include "nsAccUtils.h"
 #include "nsAccessibilityService.h"
 #include "ApplicationAccessible.h"
 #include "nsAccessiblePivot.h"
 #include "nsGenericHTMLElement.h"
 #include "NotificationController.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
@@ -36,17 +38,16 @@
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsINodeList.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/dom/Document.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/dom/HTMLAnchorElement.h"
 #include "nsIContent.h"
-#include "nsIForm.h"
 #include "nsIFormControl.h"
 
 #include "nsDeckFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsIStringBundle.h"
 #include "nsPresContext.h"
 #include "nsIFrame.h"
 #include "nsView.h"
@@ -163,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;
 
@@ -950,32 +951,28 @@ nsresult LocalAccessible::HandleAccEvent
                 end->IsDoc() && end->AsDoc()->IPCDoc()
                     ? 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: {
-          if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
-            nsAutoString name;
-            int32_t nameFlag = Name(name);
-            RefPtr<AccAttributes> fields = new AccAttributes();
-            fields->SetAttribute(nsGkAtoms::explicit_name, nameFlag);
-            fields->SetAttribute(nsGkAtoms::name, name);
-            nsTArray<CacheData> data;
-            data.AppendElement(CacheData(
-                IsDoc() ? 0 : reinterpret_cast<uint64_t>(UniqueID()), fields));
-            ipcDoc->SendCache(1, data, true);
-          }
+          SendCacheUpdate(CacheDomain::NameAndDescription);
           ipcDoc->SendEvent(id, aEvent->GetEventType());
           break;
         }
-#endif
+        case nsIAccessibleEvent::EVENT_VALUE_CHANGE: {
+          SendCacheUpdate(CacheDomain::Value);
+          ipcDoc->SendEvent(id, aEvent->GetEventType());
+          break;
+        }
         default:
           ipcDoc->SendEvent(id, aEvent->GetEventType());
       }
     }
   }
 
   if (nsCoreUtils::AccEventObserversExist()) {
     nsCoreUtils::DispatchAccEvent(MakeXPCEvent(aEvent));
@@ -1230,29 +1227,42 @@ void LocalAccessible::DOMAttributeChange
             new AccObjectAttrChangedEvent(this, aAttribute);
         mDoc->FireDelayedEvent(event);
       }
     }
   }
 
   dom::Element* elm = Elm();
 
+  if (HasNumericValue() &&
+      (aAttribute == nsGkAtoms::aria_valuemax ||
+       aAttribute == nsGkAtoms::aria_valuemin || aAttribute == nsGkAtoms::min ||
+       aAttribute == nsGkAtoms::max || aAttribute == nsGkAtoms::step)) {
+    SendCacheUpdate(CacheDomain::Value);
+    return;
+  }
+
   // Fire text value change event whenever aria-valuetext is changed.
   if (aAttribute == nsGkAtoms::aria_valuetext) {
     mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE, this);
     return;
   }
 
-  // Fire numeric value change event when aria-valuenow is changed and
-  // aria-valuetext is empty
-  if (aAttribute == nsGkAtoms::aria_valuenow &&
-      (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
-       elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
-                        nsGkAtoms::_empty, eCaseMatters))) {
-    mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, this);
+  if (aAttribute == nsGkAtoms::aria_valuenow) {
+    if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext) ||
+        elm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_valuetext,
+                         nsGkAtoms::_empty, eCaseMatters)) {
+      // Fire numeric value change event when aria-valuenow is changed and
+      // aria-valuetext is empty
+      mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, this);
+    } else {
+      // We need to update the cache here since we won't get an event if
+      // aria-valuenow is shadowed by aria-valuetext.
+      SendCacheUpdate(CacheDomain::Value);
+    }
     return;
   }
 
   if (aAttribute == nsGkAtoms::aria_owns) {
     mDoc->Controller()->ScheduleRelocation(this);
   }
 
   // Fire name change and description change events.
@@ -2046,19 +2056,17 @@ Relation LocalAccessible::RelationByType
       return Relation();
 
     case RelationType::DEFAULT_BUTTON: {
       if (mContent->IsHTMLElement()) {
         // HTML form controls implements nsIFormControl interface.
         nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
         if (control) {
           if (dom::HTMLFormElement* form = control->GetForm()) {
-            nsCOMPtr<nsIContent> formContent =
-                do_QueryInterface(form->GetDefaultSubmitElement());
-            return Relation(mDoc, formContent);
+            return Relation(mDoc, form->GetDefaultSubmitElement());
           }
         }
       } else {
         // In XUL, use first <button default="true" .../> in the document
         dom::Document* doc = mContent->OwnerDoc();
         nsIContent* buttonEl = nullptr;
         if (doc->AllowXULXBL()) {
           nsCOMPtr<nsIHTMLCollection> possibleDefaultButtons =
@@ -2339,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);
@@ -2983,22 +2991,70 @@ AccGroupInfo* LocalAccessible::GetGroupI
     return mBits.groupInfo;
   }
 
   mBits.groupInfo = AccGroupInfo::CreateGroupInfo(this);
   mStateFlags &= ~eGroupInfoDirty;
   return mBits.groupInfo;
 }
 
-already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache() {
+void LocalAccessible::SendCacheUpdate(uint64_t aCacheDomain) {
+  if (!StaticPrefs::accessibility_cache_enabled_AtStartup()) {
+    return;
+  }
+
+  if (!IPCAccessibilityActive() || !Document()) {
+    return;
+  }
+
+  DocAccessibleChild* ipcDoc = mDoc->IPCDoc();
+  MOZ_ASSERT(ipcDoc);
+
+  RefPtr<AccAttributes> fields =
+      BundleFieldsForCache(aCacheDomain, CacheUpdateType::Update);
+  nsTArray<CacheData> data;
+  data.AppendElement(
+      CacheData(IsDoc() ? 0 : reinterpret_cast<uint64_t>(UniqueID()), fields));
+  ipcDoc->SendCache(CacheUpdateType::Update, data, true);
+}
+
+already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
+    uint64_t aCacheDomain, CacheUpdateType aUpdateType) {
   RefPtr<AccAttributes> fields = new AccAttributes();
-  nsAutoString name;
-  int32_t nameFlag = Name(name);
-  fields->SetAttribute(nsGkAtoms::explicit_name, nameFlag);
-  fields->SetAttribute(nsGkAtoms::name, name);
+
+  if (aCacheDomain & CacheDomain::NameAndDescription) {
+    nsAutoString name;
+    int32_t nameFlag = 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());
+  }
 
   return fields.forget();
 }
 
 void LocalAccessible::MaybeFireFocusableStateChange(bool aPreviouslyFocusable) {
   bool isFocusable = (State() & states::FOCUSABLE);
   if (isFocusable != aPreviouslyFocusable) {
     RefPtr<AccEvent> focusableChangeEvent =
--- a/accessible/generic/LocalAccessible.h
+++ b/accessible/generic/LocalAccessible.h
@@ -51,54 +51,28 @@ 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
 
 /**
- * Name type flags.
- */
-enum ENameValueFlag {
-  /**
-   * Name either
-   *  a) present (not empty): !name.IsEmpty()
-   *  b) no name (was missed): name.IsVoid()
-   */
-  eNameOK,
-
-  /**
-   * Name was left empty by the author on purpose:
-   * name.IsEmpty() && !name.IsVoid().
-   */
-  eNoNameOnPurpose,
-
-  /**
-   * Name was computed from the subtree.
-   */
-  eNameFromSubtree,
-
-  /**
-   * Tooltip was used as a name.
-   */
-  eNameFromTooltip
-};
-
-/**
  * Group position (level, position in set and set size).
  */
 struct GroupPos {
   GroupPos() : level(0), posInSet(0), setSize(0) {}
   GroupPos(int32_t aLevel, int32_t aPosInSet, int32_t aSetSize)
       : level(aLevel), posInSet(aPosInSet), setSize(aSetSize) {}
 
   int32_t level;
@@ -182,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.
@@ -200,17 +174,17 @@ class LocalAccessible : public nsISuppor
   void Help(nsString& aHelp) const { aHelp.Truncate(); }
 
   /**
    * Get the name of this accessible.
    *
    * Note: aName.IsVoid() when name was left empty by the author on purpose.
    * aName.IsEmpty() when the author missed name, AT can try to repair a name.
    */
-  virtual ENameValueFlag Name(nsString& aName) const;
+  virtual ENameValueFlag Name(nsString& aName) const override;
 
   /**
    * Maps ARIA state attributes to state of accessible. Note the given state
    * argument should hold states for accessible before you pass it into this
    * method.
    *
    * @param  [in/out] where to fill the states into.
    */
@@ -725,20 +699,20 @@ class LocalAccessible : public nsISuppor
   /**
    * Unselect all items. Return true if success.
    */
   virtual bool UnselectAll();
 
   //////////////////////////////////////////////////////////////////////////////
   // Value (numeric value interface)
 
-  virtual double MaxValue() const;
-  virtual double MinValue() const;
-  virtual double CurValue() const;
-  virtual double Step() const;
+  virtual double MaxValue() const override;
+  virtual double MinValue() const override;
+  virtual double CurValue() const override;
+  virtual double Step() const override;
   virtual bool SetCurValue(double aValue);
 
   //////////////////////////////////////////////////////////////////////////////
   // Widgets
 
   /**
    * Return true if accessible is a widget, i.e. control or accessible that
    * manages its items. Note, being a widget the accessible may be a part of
@@ -906,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();
+  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();
 
   /**
@@ -1091,16 +1066,21 @@ class LocalAccessible : public nsISuppor
    */
   uint32_t GetActionRule() const;
 
   /**
    * Return group info.
    */
   AccGroupInfo* GetGroupInfo() const;
 
+  /**
+   * Push fields to cache.
+   */
+  void SendCacheUpdate(uint64_t aCacheDomain);
+
   // Data Members
   nsCOMPtr<nsIContent> mContent;
   RefPtr<DocAccessible> mDoc;
 
   LocalAccessible* mParent;
   nsTArray<LocalAccessible*> mChildren;
   int32_t mIndexInParent;
 
--- a/accessible/html/HTMLFormControlAccessible.cpp
+++ b/accessible/html/HTMLFormControlAccessible.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HTMLFormControlAccessible.h"
 
+#include "DocAccessible-inl.h"
 #include "LocalAccessible-inl.h"
 #include "nsAccUtils.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
 
--- 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
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/a11y/DocAccessibleChildBase.h"
+#include "mozilla/a11y/CacheConstants.h"
 #include "mozilla/a11y/RemoteAccessible.h"
 #include "mozilla/StaticPrefs_accessibility.h"
 
 #include "LocalAccessible-inl.h"
 
 namespace mozilla {
 namespace a11y {
 
@@ -74,21 +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();
-      cache.AppendElement(CacheData(id, fields));
+      RefPtr<AccAttributes> 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
@@ -13,16 +13,17 @@
 #include "nsAccUtils.h"
 #include "TextRange.h"
 
 #if defined(XP_WIN)
 #  include "AccessibleWrap.h"
 #  include "Compatibility.h"
 #  include "mozilla/mscom/PassthruProxy.h"
 #  include "mozilla/mscom/Ptr.h"
+#  include "mozilla/StaticPrefs_accessibility.h"
 #  include "nsWinUtils.h"
 #  include "RootAccessible.h"
 #else
 #  include "mozilla/a11y/DocAccessiblePlatformExtParent.h"
 #endif
 
 namespace mozilla {
 
@@ -468,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
@@ -159,12 +159,66 @@ Derived* RemoteAccessibleBase<Derived>::
   // Finally if we are a non top level document then our parent id is for a
   // proxy in our parent document so get the proxy from there.
   DocAccessibleParent* parentDoc = AsDoc()->ParentDoc();
   MOZ_ASSERT(parentDoc);
   MOZ_ASSERT(mParent);
   return parentDoc->GetAccessible(mParent);
 }
 
+template <class Derived>
+ENameValueFlag RemoteAccessibleBase<Derived>::Name(nsString& aName) const {
+  if (mCachedFields && mCachedFields->GetAttribute(nsGkAtoms::name, aName)) {
+    auto nameFlag =
+        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>();
+}
+
+template <class Derived>
+double RemoteAccessibleBase<Derived>::MinValue() const {
+  if (auto min = mCachedFields->GetAttribute<double>(nsGkAtoms::min)) {
+    return *min;
+  }
+
+  return UnspecifiedNaN<double>();
+}
+
+template <class Derived>
+double RemoteAccessibleBase<Derived>::MaxValue() const {
+  if (auto max = mCachedFields->GetAttribute<double>(nsGkAtoms::max)) {
+    return *max;
+  }
+
+  return UnspecifiedNaN<double>();
+}
+
+template <class Derived>
+double RemoteAccessibleBase<Derived>::Step() const {
+  if (auto step = mCachedFields->GetAttribute<double>(nsGkAtoms::step)) {
+    return *step;
+  }
+
+  return UnspecifiedNaN<double>();
+}
+
 template class RemoteAccessibleBase<RemoteAccessible>;
 
 }  // namespace a11y
 }  // namespace mozilla
--- a/accessible/ipc/RemoteAccessibleBase.h
+++ b/accessible/ipc/RemoteAccessibleBase.h
@@ -3,21 +3,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #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 "nsString.h"
 #include "nsTArray.h"
 #include "nsRect.h"
 #include "LocalAccessible.h"
 
 namespace mozilla {
 namespace a11y {
 
 class Attribute;
@@ -160,16 +160,26 @@ class RemoteAccessibleBase : public Acce
   }
 
   virtual bool HasNumericValue() const override {
     // 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.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
@@ -179,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
@@ -21,32 +21,32 @@ uint64_t State() const;
  * Return the native states for the proxied accessible.
  */
 uint64_t NativeState() const;
 
 /*
  * Set aName to the name of the proxied accessible.
  * Return the ENameValueFlag passed from Accessible::Name
  */
-uint32_t Name(nsString& aName) const;
+ENameValueFlag Name(nsString& aName) const override;
 
 /*
  * Set aValue to the value of the proxied accessible.
  */
 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.
@@ -234,21 +234,21 @@ void SetSelected(bool aSelect);
 bool DoAction(uint8_t aIndex);
 uint8_t ActionCount();
 void ActionDescriptionAt(uint8_t aIndex, nsString& aDescription);
 void ActionNameAt(uint8_t aIndex, nsString& aName);
 KeyBinding AccessKey();
 KeyBinding KeyboardShortcut();
 void AtkKeyBinding(nsString& aBinding);
 
-double CurValue();
+double CurValue() const override;
+double MinValue() const override;
+double MaxValue() const override;
+double Step() const override;
 bool SetCurValue(double aValue);
-double MinValue();
-double MaxValue();
-double Step();
 
 void TakeFocus();
 RemoteAccessible* FocusedChild();
 virtual Accessible* ChildAtPoint(
     int32_t aX, int32_t aY,
     LocalAccessible::EWhichChildAtPoint aWhichChild) override;
 nsIntRect Bounds();
 nsIntRect BoundsInCSSPixels();
--- 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
@@ -26,39 +26,40 @@ uint64_t RemoteAccessible::State() const
 }
 
 uint64_t RemoteAccessible::NativeState() const {
   uint64_t state = 0;
   Unused << mDoc->SendNativeState(mID, &state);
   return state;
 }
 
-uint32_t RemoteAccessible::Name(nsString& aName) const {
-  uint32_t flag = 0;
+ENameValueFlag RemoteAccessible::Name(nsString& aName) const {
   if (mCachedFields) {
-    if (mCachedFields->GetAttribute(nsGkAtoms::name, aName)) {
-      auto nameFlag =
-          mCachedFields->GetAttribute<int32_t>(nsGkAtoms::explicit_name);
-      flag = nameFlag ? *nameFlag : 0;
-    }
-  } else {
-    Unused << mDoc->SendName(mID, &aName, &flag);
+    return RemoteAccessibleBase<RemoteAccessible>::Name(aName);
   }
-  return flag;
+
+  uint32_t flag = 0;
+  Unused << mDoc->SendName(mID, &aName, &flag);
+  return static_cast<ENameValueFlag>(flag);
 }
 
 void RemoteAccessible::Value(nsString& aValue) const {
   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(
@@ -743,41 +744,57 @@ KeyBinding RemoteAccessible::KeyboardSho
   Unused << mDoc->SendKeyboardShortcut(mID, &key, &modifierMask);
   return KeyBinding(key, modifierMask);
 }
 
 void RemoteAccessible::AtkKeyBinding(nsString& aBinding) {
   Unused << mDoc->SendAtkKeyBinding(mID, &aBinding);
 }
 
-double RemoteAccessible::CurValue() {
+double RemoteAccessible::CurValue() const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::CurValue();
+  }
+
   double val = UnspecifiedNaN<double>();
   Unused << mDoc->SendCurValue(mID, &val);
   return val;
 }
 
 bool RemoteAccessible::SetCurValue(double aValue) {
   bool success = false;
   Unused << mDoc->SendSetCurValue(mID, aValue, &success);
   return success;
 }
 
-double RemoteAccessible::MinValue() {
+double RemoteAccessible::MinValue() const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::MinValue();
+  }
+
   double val = UnspecifiedNaN<double>();
   Unused << mDoc->SendMinValue(mID, &val);
   return val;
 }
 
-double RemoteAccessible::MaxValue() {
+double RemoteAccessible::MaxValue() const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::MaxValue();
+  }
+
   double val = UnspecifiedNaN<double>();
   Unused << mDoc->SendMaxValue(mID, &val);
   return val;
 }
 
-double RemoteAccessible::Step() {
+double RemoteAccessible::Step() const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::Step();
+  }
+
   double step = UnspecifiedNaN<double>();
   Unused << mDoc->SendStep(mID, &step);
   return step;
 }
 
 void RemoteAccessible::TakeFocus() { Unused << mDoc->SendTakeFocus(mID); }
 
 RemoteAccessible* RemoteAccessible::FocusedChild() {
--- 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
@@ -113,17 +113,21 @@ static RemoteAccessible* GetProxyFor(Doc
   uint64_t id;
   if (FAILED(custom->get_ID(&id))) {
     return nullptr;
   }
 
   return aDoc->GetAccessible(id);
 }
 
-uint32_t RemoteAccessible::Name(nsString& aName) const {
+ENameValueFlag RemoteAccessible::Name(nsString& aName) const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::Name(aName);
+  }
+
   /* The return values here exist only to match behvaiour required
    * by the header declaration of this function. On Mac, we'd like
    * to return the associated ENameValueFlag, but we don't have
    * access to that here, so we return a dummy eNameOK value instead.
    */
   aName.Truncate();
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
@@ -151,32 +155,40 @@ void RemoteAccessible::Value(nsString& a
   HRESULT hr = acc->get_accValue(kChildIdSelf, &result);
   _bstr_t resultWrap(result, false);
   if (FAILED(hr)) {
     return;
   }
   aValue = (wchar_t*)resultWrap;
 }
 
-double RemoteAccessible::Step() {
+double RemoteAccessible::Step() const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::Step();
+  }
+
   RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
   if (!custom) {
     return 0;
   }
 
   double increment;
   HRESULT hr = custom->get_minimumIncrement(&increment);
   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);
@@ -385,17 +397,21 @@ nsTArray<RemoteAccessible*> RemoteAccess
     proxies.AppendElement(GetProxyFor(Document(), target));
     target->Release();
   }
   CoTaskMemFree(targets);
 
   return proxies;
 }
 
-double RemoteAccessible::CurValue() {
+double RemoteAccessible::CurValue() const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::CurValue();
+  }
+
   RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
   if (!acc) {
     return UnspecifiedNaN<double>();
   }
 
   VARIANT currentValue;
   HRESULT hr = acc->get_currentValue(&currentValue);
   if (FAILED(hr) || currentValue.vt != VT_R8) {
@@ -414,32 +430,40 @@ bool RemoteAccessible::SetCurValue(doubl
   VARIANT currentValue;
   VariantInit(&currentValue);
   currentValue.vt = VT_R8;
   currentValue.dblVal = aValue;
   HRESULT hr = acc->setCurrentValue(currentValue);
   return SUCCEEDED(hr);
 }
 
-double RemoteAccessible::MinValue() {
+double RemoteAccessible::MinValue() const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::MinValue();
+  }
+
   RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
   if (!acc) {
     return UnspecifiedNaN<double>();
   }
 
   VARIANT minimumValue;
   HRESULT hr = acc->get_minimumValue(&minimumValue);
   if (FAILED(hr) || minimumValue.vt != VT_R8) {
     return UnspecifiedNaN<double>();
   }
 
   return minimumValue.dblVal;
 }
 
-double RemoteAccessible::MaxValue() {
+double RemoteAccessible::MaxValue() const {
+  if (mCachedFields) {
+    return RemoteAccessibleBase<RemoteAccessible>::MaxValue();
+  }
+
   RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
   if (!acc) {
     return UnspecifiedNaN<double>();
   }
 
   VARIANT maximumValue;
   HRESULT hr = acc->get_maximumValue(&maximumValue);
   if (FAILED(hr) || maximumValue.vt != VT_R8) {
--- a/accessible/mac/AccessibleWrap.mm
+++ b/accessible/mac/AccessibleWrap.mm
@@ -380,19 +380,16 @@ Class a11y::GetTypeFromRole(roles::Role 
     case roles::MATHML_SUB_SUP:
       return [MOXMathSubSupAccessible class];
 
     case roles::MATHML_UNDER:
     case roles::MATHML_OVER:
     case roles::MATHML_UNDER_OVER:
       return [MOXMathUnderOverAccessible class];
 
-    case roles::SUMMARY:
-      return [MOXSummaryAccessible class];
-
     case roles::OUTLINE:
     case roles::TREE_TABLE:
       return [mozOutlineAccessible class];
 
     case roles::OUTLINEITEM:
       return [mozOutlineRowAccessible class];
 
     default:
--- a/accessible/mac/moz.build
+++ b/accessible/mac/moz.build
@@ -66,8 +66,10 @@ GeneratedFile(
     script="/accessible/mac/SelectorMapGen.py",
     entry_point="gen_mm",
     inputs=["MOXAccessibleProtocol.h"],
 )
 
 FINAL_LIBRARY = "xul"
 
 include("/ipc/chromium/chromium-config.mozbuild")
+
+REQUIRES_UNIFIED_BUILD = True
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -105,18 +105,19 @@ using namespace mozilla::a11y;
   Accessible* child = mGeckoAccessible->ChildAt(i);
   return child ? GetNativeFromGeckoAccessible(child) : nil;
 
   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
 }
 
 static const uint64_t kCachedStates =
     states::CHECKED | states::PRESSED | states::MIXED | states::EXPANDED |
-    states::CURRENT | states::SELECTED | states::TRAVERSED | states::LINKED |
-    states::HASPOPUP | states::BUSY | states::MULTI_LINE;
+    states::EXPANDABLE | states::CURRENT | states::SELECTED | states::TRAVERSED |
+    states::LINKED | states::HASPOPUP | states::BUSY | states::MULTI_LINE |
+    states::CHECKABLE;
 static const uint64_t kCacheInitialized = ((uint64_t)0x1) << 63;
 
 - (uint64_t)state {
   uint64_t state = 0;
 
   if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) {
     state = acc->State();
   }
@@ -211,18 +212,17 @@ static const uint64_t kCacheInitialized 
 
   if (selector == @selector(moxARIALive) ||
       selector == @selector(moxARIAAtomic) ||
       selector == @selector(moxARIARelevant)) {
     return ![self moxIsLiveRegion];
   }
 
   if (selector == @selector(moxExpanded)) {
-    return ([self stateWithMask:states::EXPANDED] == 0) &&
-           ([self stateWithMask:states::COLLAPSED] == 0);
+    return [self stateWithMask:states::EXPANDABLE] == 0;
   }
 
   return [super moxBlockSelector:selector];
 }
 
 - (id)moxFocusedUIElement {
   MOZ_ASSERT(mGeckoAccessible);
 
@@ -564,36 +564,27 @@ struct RoleDescrComparator {
   return NSAccessibilityRoleDescription([self moxRole], subrole);
 }
 
 - (NSString*)moxLabel {
   if ([self isExpired]) {
     return nil;
   }
 
-  LocalAccessible* acc = mGeckoAccessible->AsLocal();
-  RemoteAccessible* proxy = mGeckoAccessible->AsRemote();
   nsAutoString name;
 
   /* If our accessible is:
    * 1. Named by invisible text, or
    * 2. Has more than one labeling relation, or
    * 3. Is a special role defined in providesLabelNotTitle
    *   ... return its name as a label (AXDescription).
    */
-  if (acc) {
-    ENameValueFlag flag = acc->Name(name);
-    if (flag == eNameFromSubtree) {
-      return nil;
-    }
-  } else if (proxy) {
-    uint32_t flag = proxy->Name(name);
-    if (flag == eNameFromSubtree) {
-      return nil;
-    }
+  ENameValueFlag flag = mGeckoAccessible->Name(name);
+  if (flag == eNameFromSubtree) {
+    return nil;
   }
 
   if (![self providesLabelNotTitle]) {
     NSArray* relations = [self getRelationsByType:RelationType::LABELLED_BY];
     if ([relations count] == 1) {
       return nil;
     }
   }
@@ -605,21 +596,17 @@ struct RoleDescrComparator {
   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
 
   // In some special cases we provide the name in the label (AXDescription).
   if ([self providesLabelNotTitle]) {
     return nil;
   }
 
   nsAutoString title;
-  if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) {
-    acc->Name(title);
-  } else {
-    mGeckoAccessible->AsRemote()->Name(title);
-  }
+  mGeckoAccessible->Name(title);
 
   return nsCocoaUtils::ToNSString(title);
 
   NS_OBJC_END_TRY_BLOCK_RETURN(nil);
 }
 
 - (id)moxValue {
   NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
@@ -637,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/mac/mozActionElements.mm
+++ b/accessible/mac/mozActionElements.mm
@@ -183,46 +183,33 @@ enum CheckboxValue {
  *    apply step to the current value. A positive value will increment,
  *    while a negative one will decrement.
  * step: An unsigned integer specified by the webauthor and indicating the
  *    amount by which to increment/decrement the current value.
  */
 - (void)changeValueBySteps:(int)factor {
   MOZ_ASSERT(mGeckoAccessible, "mGeckoAccessible is null");
 
-  if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) {
-    double newValue = acc->CurValue() + (acc->Step() * factor);
-    [self setValue:(newValue)];
-  } else {
-    RemoteAccessible* proxy = mGeckoAccessible->AsRemote();
-    double newValue = proxy->CurValue() + (proxy->Step() * factor);
-    [self setValue:(newValue)];
-  }
+  double newValue =
+      mGeckoAccessible->CurValue() + (mGeckoAccessible->Step() * factor);
+  [self setValue:(newValue)];
 }
 
 /*
  * Updates the accessible's current value to the specified value
  */
 - (void)setValue:(double)value {
   MOZ_ASSERT(mGeckoAccessible, "mGeckoAccessible is null");
 
-  if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) {
-    double min = acc->MinValue();
-    double max = acc->MaxValue();
-    // Because min and max are not required attributes, we first check
-    // if the value is undefined. If this check fails,
-    // the value is defined, and we verify our new value falls
-    // within the bound (inclusive).
-    if ((IsNaN(min) || value >= min) && (IsNaN(max) || value <= max)) {
+  double min = mGeckoAccessible->MinValue();
+  double max = mGeckoAccessible->MaxValue();
+
+  if ((IsNaN(min) || value >= min) && (IsNaN(max) || value <= max)) {
+    if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) {
       acc->SetCurValue(value);
-    }
-  } else {
-    RemoteAccessible* proxy = mGeckoAccessible->AsRemote();
-    double min = proxy->MinValue();
-    double max = proxy->MaxValue();
-    // As above, check if the value is within bounds.
-    if ((IsNaN(min) || value >= min) && (IsNaN(max) || value <= max)) {
+    } else {
+      RemoteAccessible* proxy = mGeckoAccessible->AsRemote();
       proxy->SetCurValue(value);
     }
   }
 }
 
 @end
--- a/accessible/mac/mozHTMLAccessible.h
+++ b/accessible/mac/mozHTMLAccessible.h
@@ -31,21 +31,14 @@
 // override
 - (NSNumber*)moxVisited;
 
 // override
 - (NSArray*)moxLinkedUIElements;
 
 @end
 
-@interface MOXSummaryAccessible : mozAccessible
-
-// override
-- (NSNumber*)moxExpanded;
-
-@end
-
 @interface MOXListItemAccessible : mozAccessible
 
 // override
 - (NSString*)moxTitle;
 
 @end
--- a/accessible/mac/mozHTMLAccessible.mm
+++ b/accessible/mac/mozHTMLAccessible.mm
@@ -79,23 +79,15 @@ using namespace mozilla::a11y;
 }
 
 - (NSArray*)moxLinkedUIElements {
   return [self getRelationsByType:RelationType::LINKS_TO];
 }
 
 @end
 
-@implementation MOXSummaryAccessible
-
-- (NSNumber*)moxExpanded {
-  return @([self stateWithMask:states::EXPANDED] != 0);
-}
-
-@end
-
 @implementation MOXListItemAccessible
 
 - (NSString*)moxTitle {
   return @"";
 }
 
 @end
--- a/accessible/mac/mozTableAccessible.h
+++ b/accessible/mac/mozTableAccessible.h
@@ -170,11 +170,14 @@
 
 // override
 - (NSNumber*)moxIndex;
 
 // override
 - (NSString*)moxLabel;
 
 // override
+- (id)moxValue;
+
+// override
 - (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled;
 
 @end
--- a/accessible/mac/mozTableAccessible.mm
+++ b/accessible/mac/mozTableAccessible.mm
@@ -607,34 +607,68 @@ enum CachedBool { eCachedBoolMiss, eCach
       }];
 
   NSUInteger index = [[outline moxRows] indexOfObjectIdenticalTo:self];
   return index == NSNotFound ? nil : @(index);
 }
 
 - (NSString*)moxLabel {
   nsAutoString title;
-  if (LocalAccessible* acc = mGeckoAccessible->AsLocal()) {
-    acc->Name(title);
-  } else {
-    mGeckoAccessible->AsRemote()->Name(title);
-  }
+  mGeckoAccessible->Name(title);
+
   // XXX: When parsing outlines built with ul/lu's, we
   // include the bullet in this description even
   // though webkit doesn't. Not all outlines are built with
   // ul/lu's so we can't strip the first character here.
 
   return nsCocoaUtils::ToNSString(title);
 }
 
+enum CheckedState {
+  kUncheckable = -1,
+  kUnchecked = 0,
+  kChecked = 1,
+  kMixed = 2
+};
+
+- (int)checkedValue {
+  uint64_t state = [self
+      stateWithMask:(states::CHECKABLE | states::CHECKED | states::MIXED)];
+
+  if (state & states::CHECKABLE) {
+    if (state & states::CHECKED) {
+      return kChecked;
+    }
+
+    if (state & states::MIXED) {
+      return kMixed;
+    }
+
+    return kUnchecked;
+  }
+
+  return kUncheckable;
+}
+
+- (id)moxValue {
+  int checkedValue = [self checkedValue];
+  return checkedValue >= 0 ? @(checkedValue) : nil;
+}
+
 - (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled {
   [super stateChanged:state isEnabled:enabled];
 
-  if (state == states::EXPANDED) {
+  if (state & states::EXPANDED) {
     // If the EXPANDED state is updated, fire appropriate events on the
     // outline row.
     [self moxPostNotification:(enabled
                                    ? NSAccessibilityRowExpandedNotification
                                    : NSAccessibilityRowCollapsedNotification)];
   }
+
+  if (state & (states::CHECKED | states::CHECKABLE | states::MIXED)) {
+    // If the MIXED, CHECKED or CHECKABLE state changes, update the value we
+    // expose for the row, which communicates checked status.
+    [self moxPostNotification:NSAccessibilityValueChangedNotification];
+  }
 }
 
 @end
--- 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/browser/mac/browser_aria_expanded.js
+++ b/accessible/tests/browser/mac/browser_aria_expanded.js
@@ -2,28 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /* import-globals-from ../../mochitest/states.js */
 loadScripts({ name: "states.js", dir: MOCHITESTS_DIR });
 
-function waitForStateChange(id, state, isEnabled) {
-  return waitForEvent(EVENT_STATE_CHANGE, e => {
-    e.QueryInterface(nsIAccessibleStateChangeEvent);
-    return (
-      e.state == state &&
-      !e.isExtraState &&
-      isEnabled == e.isEnabled &&
-      id == getAccessibleDOMNodeID(e.accessible)
-    );
-  });
-}
-
 // Test aria-expanded on a button
 addAccessibleTask(
   `hello world<br>
   <button aria-expanded="false" id="b">I am a button</button><br>
   goodbye`,
   async (browser, accDoc) => {
     let button = getNativeInterface(accDoc, "b");
     is(button.getAttributeValue("AXExpanded"), 0, "button is not expanded");
@@ -35,17 +23,20 @@ addAccessibleTask(
     await SpecialPowers.spawn(browser, [], () => {
       content.document
         .getElementById("b")
         .setAttribute("aria-expanded", "true");
     });
     await stateChanged;
     is(button.getAttributeValue("AXExpanded"), 1, "button is expanded");
 
-    stateChanged = waitForStateChange("b", STATE_EXPANDED, false);
+    stateChanged = Promise.all([
+      waitForStateChange("b", STATE_EXPANDED, false),
+      waitForStateChange("b", EXT_STATE_EXPANDABLE, false, true),
+    ]);
     await SpecialPowers.spawn(browser, [], () => {
       content.document.getElementById("b").removeAttribute("aria-expanded");
     });
     await stateChanged;
 
     ok(
       !button.attributeNames.includes("AXExpanded"),
       "button has no expanded attr"
--- a/accessible/tests/browser/mac/browser_details_summary.js
+++ b/accessible/tests/browser/mac/browser_details_summary.js
@@ -10,17 +10,17 @@ loadScripts(
   { name: "role.js", dir: MOCHITESTS_DIR },
   { name: "states.js", dir: MOCHITESTS_DIR }
 );
 
 /**
  * Test details/summary
  */
 addAccessibleTask(
-  `<details id="details"><summary>Foo</summary><p>Bar</p></details>`,
+  `<details id="details"><summary id="summary">Foo</summary><p>Bar</p></details>`,
   async (browser, accDoc) => {
     let details = getNativeInterface(accDoc, "details");
     is(
       details.getAttributeValue("AXRole"),
       "AXGroup",
       "Correct role for details"
     );
     is(
@@ -43,18 +43,27 @@ addAccessibleTask(
       "AXSummary",
       "Correct subrole for summary"
     );
     is(summary.getAttributeValue("AXExpanded"), 0, "Summary is collapsed");
 
     let actions = summary.actionNames;
     ok(actions.includes("AXPress"), "Summary Has press action");
 
-    let reorder = waitForEvent(EVENT_REORDER, "details");
+    let stateChanged = waitForStateChange("summary", STATE_EXPANDED, true);
     summary.performAction("AXPress");
-    is(summary.getAttributeValue("AXExpanded"), 1, "Summary is collapsed");
     // The reorder gecko event notifies us of a tree change.
-    await reorder;
+    await stateChanged;
+    is(summary.getAttributeValue("AXExpanded"), 1, "Summary is expanded");
 
     detailsChildren = details.getAttributeValue("AXChildren");
     is(detailsChildren.length, 2, "collapsed details has only one child");
+
+    stateChanged = waitForStateChange("summary", STATE_EXPANDED, false);
+    summary.performAction("AXPress");
+    // The reorder gecko event notifies us of a tree change.
+    await stateChanged;
+    is(summary.getAttributeValue("AXExpanded"), 0, "Summary is collapsed 2");
+
+    detailsChildren = details.getAttributeValue("AXChildren");
+    is(detailsChildren.length, 1, "collapsed details has only one child");
   }
 );
--- a/accessible/tests/browser/mac/browser_outline.js
+++ b/accessible/tests/browser/mac/browser_outline.js
@@ -1,14 +1,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/. */
 
 "use strict";
 
+/* import-globals-from ../../mochitest/states.js */
+loadScripts({ name: "states.js", dir: MOCHITESTS_DIR });
+
 /**
  * Test outline, outline rows with computed properties
  */
 addAccessibleTask(
   `
   <h3 id="tree1">
     Foods
   </h3>
@@ -452,8 +455,112 @@ addAccessibleTask(
     treeItems[0].setAttributeValue("AXDisclosing", 1);
     treeItems[0].setAttributeValue("AXDisclosing", 0);
 
     // verify they're unchanged
     is(treeItems[0].getAttributeValue("AXDisclosing"), 0);
     is(treeItems[1].getAttributeValue("AXDisclosing"), 1);
   }
 );
+
+// Test outline rows correctly expose checkable, checked/unchecked/mixed status
+addAccessibleTask(
+  `
+  <div role="tree" id="tree">
+    <div role="treeitem" aria-checked="false" id="l1">
+      Leaf 1
+    </div>
+    <div role="treeitem" aria-checked="true" id="l2">
+      Leaf 2
+    </div>
+    <div role="treeitem" id="l3">
+      Leaf 3
+    </div>
+    <div role="treeitem" aria-checked="mixed" id="l4">
+      Leaf 4
+    </div>
+  </div>
+
+  `,
+  async (browser, accDoc) => {
+    const tree = getNativeInterface(accDoc, "tree");
+    const treeItems = tree.getAttributeValue("AXChildren");
+
+    is(treeItems.length, 4, "Outline has four direct children");
+    is(
+      treeItems[0].getAttributeValue("AXValue"),
+      0,
+      "Child one is not checked"
+    );
+    is(treeItems[1].getAttributeValue("AXValue"), 1, "Child two is checked");
+    is(
+      treeItems[2].getAttributeValue("AXValue"),
+      null,
+      "Child three is not checkable and has no val"
+    );
+    is(treeItems[3].getAttributeValue("AXValue"), 2, "Child four is mixed");
+
+    let stateChanged = Promise.all([
+      waitForMacEvent("AXValueChanged", "l1"),
+      waitForStateChange("l1", STATE_CHECKED, true),
+    ]);
+    // We should get a state change event for checked.
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document
+        .getElementById("l1")
+        .setAttribute("aria-checked", "true");
+    });
+    await stateChanged;
+    is(treeItems[0].getAttributeValue("AXValue"), 1, "Child one is checked");
+
+    stateChanged = Promise.all([
+      waitForMacEvent("AXValueChanged", "l2"),
+      waitForMacEvent("AXValueChanged", "l2"),
+      waitForStateChange("l2", STATE_CHECKED, false),
+      waitForStateChange("l2", STATE_CHECKABLE, false),
+    ]);
+    // We should get a state change event for both checked and checkable,
+    // and value changes for both.
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document.getElementById("l2").removeAttribute("aria-checked");
+    });
+    await stateChanged;
+    is(
+      treeItems[1].getAttributeValue("AXValue"),
+      null,
+      "Child two is not checkable and has no val"
+    );
+
+    stateChanged = Promise.all([
+      waitForMacEvent("AXValueChanged", "l3"),
+      waitForMacEvent("AXValueChanged", "l3"),
+      waitForStateChange("l3", STATE_CHECKED, true),
+      waitForStateChange("l3", STATE_CHECKABLE, true),
+    ]);
+    // We should get a state change event for both checked and checkable,
+    // and value changes for each.
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document
+        .getElementById("l3")
+        .setAttribute("aria-checked", "true");
+    });
+    await stateChanged;
+    is(treeItems[2].getAttributeValue("AXValue"), 1, "Child three is checked");
+
+    stateChanged = Promise.all([
+      waitForMacEvent("AXValueChanged", "l4"),
+      waitForMacEvent("AXValueChanged", "l4"),
+      waitForStateChange("l4", STATE_MIXED, false),
+      waitForStateChange("l4", STATE_CHECKABLE, false),
+    ]);
+    // We should get a state change event for both mixed and checkable,
+    // and value changes for each.
+    await SpecialPowers.spawn(browser, [], () => {
+      content.document.getElementById("l4").removeAttribute("aria-checked");
+    });
+    await stateChanged;
+    is(
+      treeItems[3].getAttributeValue("AXValue"),
+      null,
+      "Child four is not checkable and has no value"
+    );
+  }
+);
--- a/accessible/tests/browser/mac/head.js
+++ b/accessible/tests/browser/mac/head.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-/* exported getNativeInterface, waitForMacEventWithInfo, waitForMacEvent,
+/* exported getNativeInterface, waitForMacEventWithInfo, waitForMacEvent, waitForStateChange,
    NSRange, NSDictionary, stringForRange, AXTextStateChangeTypeEdit,
    AXTextEditTypeDelete, AXTextEditTypeTyping, AXTextStateChangeTypeSelectionMove,
    AXTextStateChangeTypeSelectionExtend, AXTextSelectionDirectionUnknown,
    AXTextSelectionDirectionPrevious, AXTextSelectionDirectionNext,
    AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown,
    AXTextSelectionGranularityCharacter, AXTextSelectionGranularityWord */
 
 // Load the shared-head file first.
@@ -122,8 +122,20 @@ function stringForRange(macDoc, range) {
   is(
     attrStr.map(({ string }) => string).join(""),
     str,
     "attributed text matches non-attributed text"
   );
 
   return str;
 }
+
+function waitForStateChange(id, state, isEnabled, isExtra = false) {
+  return waitForEvent(EVENT_STATE_CHANGE, e => {
+    e.QueryInterface(nsIAccessibleStateChangeEvent);
+    return (
+      e.state == state &&
+      e.isExtraState == isExtra &&
+      isEnabled == e.isEnabled &&
+      id == getAccessibleDOMNodeID(e.accessible)
+    );
+  });
+}
--- a/accessible/tests/browser/telemetry/browser_HCM_telemetry.js
+++ b/accessible/tests/browser/telemetry/browser_HCM_telemetry.js
@@ -90,35 +90,35 @@ async function setForegroundColor(color)
 
 async function setBackgroundColor(color) {
   Services.prefs.setStringPref("browser.display.background_color", color);
 }
 
 add_task(async function testInit() {
   const dialogWin = await openColorsDialog();
   const menulistHCM = dialogWin.document.getElementById("useDocumentColors");
-  if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
+  if (AppConstants.platform == "win") {
     is(
       menulistHCM.value,
       "0",
-      "HCM menulist should be set to only with HCM theme on startup for windows and mac"
+      "HCM menulist should be set to only with HCM theme on startup for windows"
     );
 
     // Verify correct default value
     TelemetryTestUtils.assertKeyedScalar(
       TelemetryTestUtils.getProcessScalars("parent", true, true),
       "a11y.theme",
       "default",
       false
     );
   } else {
     is(
       menulistHCM.value,
       "1",
-      "HCM menulist should be set to never on startup for other platforms"
+      "HCM menulist should be set to never on startup for non-windows platforms"
     );
 
     // Verify correct default value
     TelemetryTestUtils.assertKeyedScalar(
       TelemetryTestUtils.getProcessScalars("parent", true, true),
       "a11y.theme",
       "always",
       false
--- 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/role/test_aria.html
+++ b/accessible/tests/mochitest/role/test_aria.html
@@ -97,16 +97,18 @@
       testRole("aria_menubar", ROLE_MENUBAR);
       testRole("aria_menubar_mixed", ROLE_MENUBAR);
       testRole("aria_menuitem", ROLE_MENUITEM);
       testRole("aria_menuitem_mixed", ROLE_MENUITEM);
       testRole("aria_menuitemcheckbox", ROLE_CHECK_MENU_ITEM);
       testRole("aria_menuitemcheckbox_mixed", ROLE_CHECK_MENU_ITEM);
       testRole("aria_menuitemradio", ROLE_RADIO_MENU_ITEM);
       testRole("aria_menuitemradio_mixed", ROLE_RADIO_MENU_ITEM);
+      testRole("aria_meter", ROLE_METER);
+      testRole("aria_meter_mixed", ROLE_METER);
       testRole("aria_note", ROLE_NOTE);
       testRole("aria_note_mixed", ROLE_NOTE);
       testRole("aria_paragraph", ROLE_PARAGRAPH);
       testRole("aria_paragraph_mixed", ROLE_PARAGRAPH);
       testRole("aria_presentation", ROLE_TEXT); // weak role
       testRole("aria_presentation_mixed", ROLE_TEXT); // weak role
       testRole("aria_progressbar", ROLE_PROGRESSBAR);
       testRole("aria_progressbar_mixed", ROLE_PROGRESSBAR);
@@ -445,16 +447,18 @@
   <span id="aria_menubar" role="menubar"></span>
   <span id="aria_menubar_mixed" role="mENUBAr"></span>
   <span id="aria_menuitem" role="menuitem"></span>
   <span id="aria_menuitem_mixed" role="mENUITEm"></span>
   <span id="aria_menuitemcheckbox" role="menuitemcheckbox"></span>
   <span id="aria_menuitemcheckbox_mixed" role="mENUITEMCHECKBOx"></span>
   <span id="aria_menuitemradio" role="menuitemradio"></span>
   <span id="aria_menuitemradio_mixed" role="mENUITEMRADIo"></span>
+  <span id="aria_meter" role="meter"></span>
+  <span id="aria_meter_mixed" role="meTer"></span>
   <span id="aria_note" role="note"></span>
   <span id="aria_note_mixed" role="nOTe"></span>
   <span id="aria_paragraph" role="paragraph"></span>
   <span id="aria_paragraph_mixed" role="pARAGRAPh"></span>
   <span id="aria_presentation" role="presentation" tabindex="0"></span>
   <span id="aria_presentation_mixed" role="pRESENTATIOn" tabindex="0"></span>
   <span id="aria_progressbar" role="progressbar"></span>
   <span id="aria_progressbar_mixed" role="pROGRESSBAr"></span>
--- 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/ia2/ia2AccessibleValue.cpp
+++ b/accessible/windows/ia2/ia2AccessibleValue.cpp
@@ -16,16 +16,20 @@
 #include "mozilla/FloatingPoint.h"
 
 using namespace mozilla::a11y;
 
 AccessibleWrap* ia2AccessibleValue::LocalAcc() {
   return static_cast<MsaaAccessible*>(this)->LocalAcc();
 }
 
+Accessible* ia2AccessibleValue::Acc() {
+  return static_cast<MsaaAccessible*>(this)->Acc();
+}
+
 // IUnknown
 
 STDMETHODIMP
 ia2AccessibleValue::QueryInterface(REFIID iid, void** ppv) {
   if (!ppv) return E_INVALIDARG;
 
   *ppv = nullptr;
 
@@ -46,17 +50,17 @@ ia2AccessibleValue::QueryInterface(REFII
 // IAccessibleValue
 
 STDMETHODIMP
 ia2AccessibleValue::get_currentValue(VARIANT* aCurrentValue) {
   if (!aCurrentValue) return E_INVALIDARG;
 
   VariantInit(aCurrentValue);
 
-  AccessibleWrap* valueAcc = LocalAcc();
+  Accessible* valueAcc = Acc();
   if (!valueAcc) {
     return CO_E_OBJNOTCONNECTED;
   }
   double currentValue;
 
   currentValue = valueAcc->CurValue();
 
   if (IsNaN(currentValue)) return S_FALSE;
@@ -79,17 +83,17 @@ ia2AccessibleValue::setCurrentValue(VARI
 }
 
 STDMETHODIMP
 ia2AccessibleValue::get_maximumValue(VARIANT* aMaximumValue) {
   if (!aMaximumValue) return E_INVALIDARG;
 
   VariantInit(aMaximumValue);
 
-  AccessibleWrap* valueAcc = LocalAcc();
+  Accessible* valueAcc = Acc();
   if (!valueAcc) {
     return CO_E_OBJNOTCONNECTED;
   }
   double maximumValue;
 
   maximumValue = valueAcc->MaxValue();
 
   if (IsNaN(maximumValue)) return S_FALSE;
@@ -100,17 +104,17 @@ ia2AccessibleValue::get_maximumValue(VAR
 }
 
 STDMETHODIMP
 ia2AccessibleValue::get_minimumValue(VARIANT* aMinimumValue) {
   if (!aMinimumValue) return E_INVALIDARG;
 
   VariantInit(aMinimumValue);
 
-  AccessibleWrap* valueAcc = LocalAcc();
+  Accessible* valueAcc = Acc();
   if (!valueAcc) {
     return CO_E_OBJNOTCONNECTED;
   }
   double minimumValue;
 
   minimumValue = valueAcc->MinValue();
 
   if (IsNaN(minimumValue)) return S_FALSE;
--- a/accessible/windows/ia2/ia2AccessibleValue.h
+++ b/accessible/windows/ia2/ia2AccessibleValue.h
@@ -28,15 +28,16 @@ class ia2AccessibleValue : public IAcces
 
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_maximumValue(
       /* [retval][out] */ VARIANT* maximumValue);
 
   virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_minimumValue(
       /* [retval][out] */ VARIANT* minimumValue);
 
  private:
+  Accessible* Acc();
   AccessibleWrap* LocalAcc();
 };
 
 }  // namespace a11y
 }  // namespace mozilla
 
 #endif
--- a/accessible/windows/ia2/moz.build
+++ b/accessible/windows/ia2/moz.build
@@ -52,8 +52,10 @@ FINAL_LIBRARY = "xul"
 #
 #   #endif !_MIDL_USE_GUIDDEF_
 #
 # which clang-cl complains about.  MSVC doesn't, so turn this warning off.
 if CONFIG["CC_TYPE"] == "clang-cl":
     CXXFLAGS += ["-Wno-extra-tokens"]
 
 include("/ipc/chromium/chromium-config.mozbuild")
+
+REQUIRES_UNIFIED_BUILD = True
--- a/accessible/windows/msaa/MsaaAccessible.cpp
+++ b/accessible/windows/msaa/MsaaAccessible.cpp
@@ -961,22 +961,19 @@ MsaaAccessible::get_accName(
   HRESULT hr = ResolveChild(varChild, getter_AddRefs(accessible));
   if (FAILED(hr)) {
     return hr;
   }
 
   if (accessible) {
     return accessible->get_accName(kVarChildIdSelf, pszName);
   }
-  if (mAcc->IsRemote()) {
-    return E_NOTIMPL;  // XXX Not supported for RemoteAccessible yet.
-  }
 
   nsAutoString name;
-  LocalAcc()->Name(name);
+  Acc()->Name(name);
 
   // The name was not provided, e.g. no alt attribute for an image. A screen
   // reader may choose to invent its own accessible name, e.g. from an image src
   // attribute. Refer to eNoNameOnPurpose return value.
   if (name.IsVoid()) return S_FALSE;
 
   *pszName = ::SysAllocStringLen(name.get(), name.Length());
   if (!*pszName) return E_OUTOFMEMORY;
@@ -1028,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/windows/msaa/moz.build
+++ b/accessible/windows/msaa/moz.build
@@ -76,8 +76,10 @@ LOCAL_INCLUDES += [
 #
 # which clang-cl complains about.  MSVC doesn't, so turn this warning off.
 if CONFIG["CC_TYPE"] == "clang-cl":
     CXXFLAGS += ["-Wno-extra-tokens"]
 
 include("/ipc/chromium/chromium-config.mozbuild")
 
 FINAL_LIBRARY = "xul"
+
+REQUIRES_UNIFIED_BUILD = True
--- a/accessible/windows/sdn/moz.build
+++ b/accessible/windows/sdn/moz.build
@@ -17,8 +17,10 @@ LOCAL_INCLUDES += [
     "/accessible/windows/msaa",
     "/accessible/xpcom",
     "/accessible/xul",
 ]
 
 include("/ipc/chromium/chromium-config.mozbuild")
 
 FINAL_LIBRARY = "xul"
+
+REQUIRES_UNIFIED_BUILD = True
--- a/accessible/xpcom/xpcAccessibilityService.cpp
+++ b/accessible/xpcom/xpcAccessibilityService.cpp
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "xpcAccessibilityService.h"
 
+#include "mozilla/dom/Document.h"
+
 #include "nsAccessiblePivot.h"
 #include "nsAccessibilityService.h"
 #include "Platform.h"
 #include "xpcAccessibleApplication.h"
 #include "xpcAccessibleDocument.h"
 
 #ifdef A11Y_LOG
 #  include "Logging.h"
--- a/accessible/xpcom/xpcAccessible.cpp
+++ b/accessible/xpcom/xpcAccessible.cpp
@@ -239,37 +239,29 @@ xpcAccessible::GetState(uint32_t* aState
 
 NS_IMETHODIMP
 xpcAccessible::GetName(nsAString& aName) {
   aName.Truncate();
 
   if (!IntlGeneric()) return NS_ERROR_FAILURE;
 
   nsAutoString name;
-  if (RemoteAccessible* proxy = IntlGeneric()->AsRemote()) {
-    proxy->Name(name);
-  } else {
-    Intl()->Name(name);
-  }
+  IntlGeneric()->Name(name);
 
   aName.Assign(name);
 
   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/xpcom/xpcAccessibleValue.cpp
+++ b/accessible/xpcom/xpcAccessibleValue.cpp
@@ -1,37 +1,33 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "xpcAccessibleGeneric.h"
 #include "LocalAccessible.h"
+#include "LocalAccessible-inl.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 NS_IMETHODIMP
 xpcAccessibleValue::GetMaximumValue(double* aValue) {
   NS_ENSURE_ARG_POINTER(aValue);
   *aValue = 0;
 
   if (!Intl()) return NS_ERROR_FAILURE;
 
   if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) {
     return NS_ERROR_FAILURE;
   }
 
-  double value;
-  if (Intl()->IsLocal()) {
-    value = Intl()->AsLocal()->MaxValue();
-  } else {
-    value = Intl()->AsRemote()->MaxValue();
-  }
+  double value = Intl()->MaxValue();
 
   if (!IsNaN(value)) *aValue = value;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleValue::GetMinimumValue(double* aValue) {
@@ -39,22 +35,17 @@ xpcAccessibleValue::GetMinimumValue(doub
   *aValue = 0;
 
   if (!Intl()) return NS_ERROR_FAILURE;
 
   if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) {
     return NS_ERROR_FAILURE;
   }
 
-  double value;
-  if (Intl()->IsLocal()) {
-    value = Intl()->AsLocal()->MinValue();
-  } else {
-    value = Intl()->AsRemote()->MinValue();
-  }
+  double value = Intl()->MinValue();
 
   if (!IsNaN(value)) *aValue = value;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleValue::GetCurrentValue(double* aValue) {
@@ -62,22 +53,17 @@ xpcAccessibleValue::GetCurrentValue(doub
   *aValue = 0;
 
   if (!Intl()) return NS_ERROR_FAILURE;
 
   if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) {
     return NS_ERROR_FAILURE;
   }
 
-  double value;
-  if (Intl()->IsLocal()) {
-    value = Intl()->AsLocal()->CurValue();
-  } else {
-    value = Intl()->AsRemote()->CurValue();
-  }
+  double value = Intl()->CurValue();
 
   if (!IsNaN(value)) *aValue = value;
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 xpcAccessibleValue::SetCurrentValue(double aValue) {
@@ -102,19 +88,14 @@ xpcAccessibleValue::GetMinimumIncrement(
   *aValue = 0;
 
   if (!Intl()) return NS_ERROR_FAILURE;
 
   if (Intl()->IsLocal() && Intl()->AsLocal()->IsDefunct()) {
     return NS_ERROR_FAILURE;
   }
 
-  double value;
-  if (Intl()->IsLocal()) {
-    value = Intl()->AsLocal()->Step();
-  } else {
-    value = Intl()->AsRemote()->Step();
-  }
+  double value = Intl()->Step();
 
   if (!IsNaN(value)) *aValue = value;
 
   return NS_OK;
 }
--- 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
@@ -8,16 +8,17 @@
 #include "LocalAccessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "DocAccessible.h"
 #include "Role.h"
 #include "States.h"
 #include "XULFormControlAccessible.h"
 
+#include "nsIContentInlines.h"
 #include "nsIDOMXULContainerElement.h"
 #include "nsIDOMXULSelectCntrlEl.h"
 #include "nsIDOMXULSelectCntrlItemEl.h"
 #include "nsIContent.h"
 #include "nsMenuBarFrame.h"
 #include "nsMenuPopupFrame.h"
 
 #include "mozilla/Preferences.h"
@@ -123,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/accessible/xul/XULTreeGridAccessible.cpp
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "XULTreeGridAccessibleWrap.h"
 
 #include "AccAttributes.h"
+#include "LocalAccessible-inl.h"
 #include "nsAccCache.h"
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "DocAccessible.h"
 #include "nsEventShell.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
--- 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);
@@ -358,33 +374,41 @@ pref("browser.urlbar.maxHistoricalSearch
 // The default behavior for the urlbar can be configured to use any combination
 // 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",         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);
 
 // Show QuickSuggest onboarding dialog on the nth browser restarts.
-pref("browser.urlbar.quicksuggest.showOnboardingDialogAfterNRestarts", 2);
+pref("browser.urlbar.quicksuggest.showOnboardingDialogAfterNRestarts", 0);
 
 // 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
@@ -444,16 +468,22 @@ pref("browser.urlbar.dnsResolveSingleWor
 // up with two panels on top of each other. Since for now we can't detect that
 // we leave this choice to the user, hopefully in the future this can be flipped
 // for everyone.
 pref("browser.urlbar.keepPanelOpenDuringImeComposition", false);
 
 // Whether Firefox Suggest group labels are shown in the urlbar view.
 pref("browser.urlbar.groupLabels.enabled", true);
 
+// Whether Merino is enabled as a quick suggest source in the urlbar.
+pref("browser.urlbar.merino.enabled", false);
+
+// The Merino endpoint URL, not including parameters.
+pref("browser.urlbar.merino.endpointURL", "https://merino.services.mozilla.com/api/v1/suggest");
+
 pref("browser.altClickSave", false);
 
 // Enable logging downloads operations to the Console.
 pref("browser.download.loglevel", "Error");
 
 // Number of milliseconds to wait for the http headers (and thus
 // the Content-Disposition filename) before giving up and falling back to
 // picking a filename without that info in hand so that the user sees some
@@ -1016,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);
@@ -1302,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);
@@ -1416,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.
@@ -1443,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);
@@ -1753,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/app/winlauncher/freestanding/moz.build
+++ b/browser/app/winlauncher/freestanding/moz.build
@@ -49,8 +49,10 @@ if CONFIG["COMPILE_ENVIRONMENT"] and CON
         inputs=["ntdll_freestanding.def"],
         flags=[CONFIG["LLVM_DLLTOOL"]] + CONFIG["LLVM_DLLTOOL_FLAGS"],
     )
 
 DisableStlWrapping()
 
 with Files("**"):
     BUG_COMPONENT = ("Firefox", "Launcher Process")
+
+REQUIRES_UNIFIED_BUILD = True
--- a/browser/app/winlauncher/moz.build
+++ b/browser/app/winlauncher/moz.build
@@ -44,8 +44,10 @@ if CONFIG["MOZ_LAUNCHER_PROCESS"]:
     ]
     for var in ("MOZ_APP_BASENAME", "MOZ_APP_VENDOR"):
         DEFINES[var] = '"%s"' % CONFIG[var]
 
 DisableStlWrapping()
 
 with Files("**"):
     BUG_COMPONENT = ("Firefox", "Launcher Process")
+
+REQUIRES_UNIFIED_BUILD = True
--- 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"
@@ -141,17 +141,17 @@
                      oncommand="openPreferences()"/>
       <toolbarbutton id="appMenu-more-button2"
                      class="subviewbutton subviewbutton-nav"
                      data-l10n-id="appmenuitem-more-tools"
                      closemenu="none"
                      oncommand="PanelUI.showMoreToolsPanel(this);"/>
       <toolbarbutton id="appMenu-help-button2"
                      class="subviewbutton subviewbutton-nav"
-                     label="&appMenuHelp.label;"
+                     data-l10n-id="appmenuitem-help"
                      closemenu="none"
                      oncommand="PanelUI.showSubView('PanelUI-helpView', this)"/>
 #ifndef XP_MACOSX
       <toolbarseparator/>
       <toolbarbutton id="appMenu-quit-button2"
                      class="subviewbutton"
                      data-l10n-id="appmenuitem-exit2"
                      key="key_quitApplication"
@@ -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,144 +423,139 @@
                          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">
             <vbox id="PanelUI-remotetabs-tabslist"
-                  showAllLabel="&appMenuRemoteTabs.showAll.label;"
-                  showAllTooltipText="&appMenuRemoteTabs.showAll.tooltip;"
-                  showMoreLabel="appmenu-fxa-show-more-tabs"
-                  showMoreTooltipText="&appMenuRemoteTabs.showMore.tooltip;"
-                  notabsforclientlabel="&appMenuRemoteTabs.notabs.label;"
+            notabsforclientlabel="appmenu-remote-tabs-notabs"
                   />
           </vbox>
           <!-- Sync is ready to Sync but we are still fetching the tabs to show -->
           <vbox id="PanelUI-remotetabs-fetching">
             <!-- Show intentionally blank panel, see bug 1239845 -->
           </vbox>
           <!-- Sync is ready to Sync but the "tabs" engine isn't enabled-->
           <hbox id="PanelUI-remotetabs-tabsdisabledpane" pack="center" flex="1">
             <vbox class="PanelUI-remotetabs-instruction-box" align="center">
               <hbox pack="center">
                 <image class="fxaSyncIllustrationIssue"/>
               </hbox>
-              <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.tabsnotsyncing.label;</label>
+              <label class="PanelUI-remotetabs-instruction-label" data-l10n-id="appmenu-remote-tabs-tabsnotsyncing"></label>
               <hbox pack="center">
                 <toolbarbutton class="PanelUI-remotetabs-button"
                                id="PanelUI-remotetabs-tabsdisabledpane-button"
-                               label="&appMenuRemoteTabs.opensyncprefs.label;"
+                               data-l10n-id="appmenu-remote-tabs-opensettings"
                                oncommand="gSync.openPrefs('synced-tabs');"/>
               </hbox>
             </vbox>
           </hbox>
           <!-- Sync has only 1 (ie, this) device connected -->
           <hbox id="PanelUI-remotetabs-nodevicespane" pack="center" flex="1">
             <vbox class="PanelUI-remotetabs-instruction-box" align="center">
               <hbox pack="center">
                 <image class="fxaSyncIllustrationIssue"/>
               </hbox>
-              <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.noclients.subtitle;</label>
+              <label class="PanelUI-remotetabs-instruction-label" data-l10n-id="appmenu-remote-tabs-noclients"></label>
               <toolbarbutton id="PanelUI-remotetabs-connect-device-button"
                              class="PanelUI-remotetabs-button"
-                             label="&appMenuRemoteTabs.connectdevice.label;"
+                             data-l10n-id="appmenu-remote-tabs-connectdevice"
                              oncommand="gSync.openConnectAnotherDevice('synced-tabs');"/>
             </vbox>
           </hbox>
         </deck>
       </vbox>
       <!-- a box to ensure contained boxes are centered horizonally -->
       <hbox pack="center" flex="1">
         <!-- When Sync is not configured -->
         <vbox id="PanelUI-remotetabs-setupsync"
               flex="1"
               align="center"
               class="PanelUI-remotetabs-instruction-box"
               hidden="true">
           <image class="fxaSyncIllustration"/>
-          <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.welcome.label;</label>
+          <label class="PanelUI-remotetabs-instruction-label" data-l10n-id="appmenu-remote-tabs-welcome"></label>
           <toolbarbutton class="PanelUI-remotetabs-button"
                          id="PanelUI-remotetabs-setupsync-button"
                          data-l10n-id="appmenu-remote-tabs-sign-into-sync"
                          oncommand="gSync.openPrefs('synced-tabs');"/>
         </vbox>
         <!-- When Sync is not enabled -->
         <vbox id="PanelUI-remotetabs-syncdisabled"
               flex="1"
               align="center"
               class="PanelUI-remotetabs-instruction-box"
               hidden="true">
           <image class="fxaSyncIllustration"/>
-          <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.welcome.label;</label>
+          <label class="PanelUI-remotetabs-instruction-label" data-l10n-id="appmenu-remote-tabs-welcome"></label>
           <toolbarbutton class="PanelUI-remotetabs-button"
                          id="PanelUI-remotetabs-syncdisabled-button"
                          data-l10n-id="appmenu-remote-tabs-turn-on-sync"
                          oncommand="gSync.openPrefs('synced-tabs');"/>
         </vbox>
         <!-- When Sync needs re-authentication -->
         <vbox id="PanelUI-remotetabs-reauthsync"
               flex="1"
               align="center"
               class="PanelUI-remotetabs-instruction-box"
               hidden="true">
           <image class="fxaSyncIllustrationIssue"/>
-          <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.welcome.label;</label>
+          <label class="PanelUI-remotetabs-instruction-label" data-l10n-id="appmenu-remote-tabs-welcome"></label>
           <toolbarbutton class="PanelUI-remotetabs-button"
                          id="PanelUI-remotetabs-reauthsync-button"
                          data-l10n-id="appmenu-remote-tabs-sign-into-sync"
                          oncommand="gSync.openPrefs('synced-tabs');"/>
         </vbox>
         <!-- When Sync needs verification -->
         <vbox id="PanelUI-remotetabs-unverified"
               flex="1"
               align="center"
               class="PanelUI-remotetabs-instruction-box"
               hidden="true">
           <image class="fxaSyncIllustrationIssue"/>
-          <label class="PanelUI-remotetabs-instruction-label">&appMenuRemoteTabs.unverified.label;</label>
+          <label class="PanelUI-remotetabs-instruction-label" data-l10n-id="appmenu-remote-tabs-unverified"></label>
           <toolbarbutton class="PanelUI-remotetabs-button"
                          id="PanelUI-remotetabs-unverified-button"
-                         label="&appMenuRemoteTabs.opensyncprefs.label;"
+                         data-l10n-id="appmenu-remote-tabs-opensettings"
                          oncommand="gSync.openPrefs('synced-tabs');"/>
         </vbox>
       </hbox>
     </vbox>
   </panelview>
 
   <panelview id="PanelUI-fxa" class="PanelUI-subView" descriptionheightworkaround="true">
     <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"
@@ -598,77 +596,73 @@
               class="subviewbutton"
               data-l10n-id="fxa-menu-sign-out"
               oncommand="gSync.disconnect();"/>
       <toolbarseparator id="PanelUI-remote-tabs-separator"/>
       <deck id="PanelUI-fxa-remotetabs-deck">
         <!-- Sync is ready to Sync and the "tabs" engine is enabled -->
         <vbox id="PanelUI-fxa-remotetabs-tabspane">
           <vbox id="PanelUI-fxa-remotetabs-tabslist"
-                showAllLabel="&appMenuRemoteTabs.showAll.label;"
-                showAllTooltipText="&appMenuRemoteTabs.showAll.tooltip;"
-                showMoreLabel="appmenu-fxa-show-more-tabs"
-                showMoreTooltipText="&appMenuRemoteTabs.showMore.tooltip;"
-                notabsforclientlabel="&appMenuRemoteTabs.notabs.label;"
-                />
+          notabsforclientlabel="appmenu-remote-tabs-notabs"
+          />
         </vbox>
         <!-- Sync is ready to Sync but we are still fetching the tabs to show -->
         <vbox id="PanelUI-fxa-remotetabs-fetching">
           <!-- Show intentionally blank panel, see bug 1239845 -->
         </vbox>
       </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"
-                     label="&appMenuRemoteTabs.connectdevice.label;"
+                     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-captivePortal.js
+++ b/browser/base/content/browser-captivePortal.js
@@ -21,32 +21,16 @@ var CaptivePortalWatcher = {
   // In the situation above, this is set to true while we wait for the recheck.
   // This flag exists so that tests can appropriately simulate a recheck.
   _waitingForRecheck: false,
 
   // This holds a weak reference to the captive portal tab so we can close the tab
   // after successful login if we're redirected to the canonicalURL.
   _previousCaptivePortalTab: null,
 
-  // We will only show the VPN promo if we're pretty sure that the captive
-  // portal "Login" button has been pressed relatively recently
-  // before the captive-portal-login-success notification was received so that
-  // we can have reasonable degree of confidence that the user has some mental
-  // context why when we might be showing them the promo.
-  _loginButtonPressedTimeStamp: 0, // 0 is magic value meaning "not set"
-
-  // Here's where we define "recently" for the button above:
-  //
-  // On the one hand, if for some reason the login doesn't happen (eg user
-  // decides airplane wifi charge is too high), the next time they try to
-  // log into either this or another captive portal, we could end up showing
-  // the VPN promo when it's been a long time since they saw the UI, and
-  // they've lost mental context.
-  _LOGIN_BUTTON_PRESSED_TIMEOUT: 60 * 1000 * 1000, // 60 mins
-
   get _captivePortalNotification() {
     return gNotificationBox.getNotificationWithValue(
       this.PORTAL_NOTIFICATION_VALUE
     );
   },
 
   get canonicalURL() {
     return Services.prefs.getCharPref("captivedetect.canonicalURL");
@@ -59,17 +43,16 @@ var CaptivePortalWatcher = {
     ));
   },
 
   init() {
     Services.obs.addObserver(this, "ensure-captive-portal-tab");
     Services.obs.addObserver(this, "captive-portal-login");
     Services.obs.addObserver(this, "captive-portal-login-abort");
     Services.obs.addObserver(this, "captive-portal-login-success");
-    Services.obs.addObserver(this, "captive-portal-login-button-pressed");
 
     this._cps = Cc["@mozilla.org/network/captive-portal-service;1"].getService(
       Ci.nsICaptivePortalService
     );
 
     if (this._cps.state == this._cps.LOCKED_PORTAL) {
       // A captive portal has already been detected.
       this._captivePortalDetected();
@@ -95,17 +78,16 @@ var CaptivePortalWatcher = {
     );
   },
 
   uninit() {
     Services.obs.removeObserver(this, "ensure-captive-portal-tab");
     Services.obs.removeObserver(this, "captive-portal-login");
     Services.obs.removeObserver(this, "captive-portal-login-abort");
     Services.obs.removeObserver(this, "captive-portal-login-success");
-    Services.obs.removeObserver(this, "captive-portal-login-button-pressed");
 
     this._cancelDelayedCaptivePortal();
   },
 
   delayedStartup() {
     if (this._delayedRecheckPending) {
       delete this._delayedRecheckPending;
       this._cps.recheckCaptivePortal();
@@ -118,33 +100,18 @@ var CaptivePortalWatcher = {
         this.ensureCaptivePortalTab();
         break;
       case "captive-portal-login":
         this._captivePortalDetected();
         break;
       case "captive-portal-login-abort":
         this._captivePortalGone(false);
         break;
-      case "captive-portal-login-button-pressed":
-        this._loginButtonPressedTimeStamp = Cu.now();
-        break;
       case "captive-portal-login-success":
         this._captivePortalGone(true);
-
-        if (
-          this._loginButtonPressedTimeStamp &&
-          Cu.now() - this._loginButtonPressedTimeStamp <
-            this._LOGIN_BUTTON_PRESSED_TIMEOUT
-        ) {
-          Services.obs.notifyObservers(
-            null,
-            "captive-portal-login-success-after-button-pressed"
-          );
-          this._loginButtonPressedTimeStamp = 0;
-        }
         break;
       case "delayed-captive-portal-handled":
         this._cancelDelayedCaptivePortal();
         break;
     }
   },
 
   onLocationChange(browser) {
@@ -205,21 +172,16 @@ var CaptivePortalWatcher = {
     // so they can log in before continuing to browse.
     if (win != Services.focus.activeWindow) {
       this._delayedCaptivePortalDetectedInProgress = true;
       window.addEventListener("activate", this, { once: true });
       Services.obs.addObserver(this, "delayed-captive-portal-handled");
     }
 
     this._showNotification();
-    Services.telemetry.recordEvent(
-      "networking.captive_portal",
-      "login_infobar_shown",
-      "infobar"
-    );
   },
 
   /**
    * Called after we regain focus if we detect a portal while a browser window
    * doesn't have focus. Triggers a portal recheck to reaffirm state, and adds
    * the tab if needed after a short delay to allow the recheck to complete.
    */
   _delayedCaptivePortalDetected() {
@@ -274,24 +236,16 @@ var CaptivePortalWatcher = {
       tab &&
       tab.linkedBrowser &&
       tab.linkedBrowser.currentURI.equalsExceptRef(canonicalURI)
     ) {
       this._previousCaptivePortalTab = null;
       gBrowser.removeTab(tab);
     }
     this._captivePortalTab = null;
-
-    if (aSuccess) {
-      Services.telemetry.recordEvent(
-        "networking.captive_portal",
-        "login_successful",
-        "detector"
-      );
-    }
   },
 
   _cancelDelayedCaptivePortal() {
     if (this._delayedCaptivePortalDetectedInProgress) {
       this._delayedCaptivePortalDetectedInProgress = false;
       Services.obs.removeObserver(this, "delayed-captive-portal-handled");
       window.removeEventListener("activate", this);
     }
@@ -334,21 +288,16 @@ var CaptivePortalWatcher = {
     let buttons = [
       {
         label: this._browserBundle.GetStringFromName(
           "captivePortal.showLoginPage2"
         ),
         callback: () => {
           this.ensureCaptivePortalTab();
 
-          Services.obs.notifyObservers(
-            null,
-            "captive-portal-login-button-pressed"
-          );
-
           Services.telemetry.recordEvent(
             "networking.captive_portal",
             "login_button_pressed",
             "login_button"
           );
 
           // Returning true prevents the notification from closing.
           return true;
@@ -371,16 +320,22 @@ var CaptivePortalWatcher = {
       message,
       this.PORTAL_NOTIFICATION_VALUE,
       "",
       gNotificationBox.PRIORITY_INFO_MEDIUM,
       buttons,
       closeHandler
     );
 
+    Services.telemetry.recordEvent(
+      "networking.captive_portal",
+      "login_infobar_shown",
+      "infobar"
+    );
+
     gBrowser.tabContainer.addEventListener("TabSelect", this);
   },
 
   _removeNotification() {
     let n = this._captivePortalNotification;
     if (!n || !n.parentNode) {
       return;
     }
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -56,30 +56,29 @@
       <menuitem id="context-viewsource-highlightSyntax"
                 type="checkbox"
                 oncommand="gViewSourceUtils.getPageActor(gContextMenu.browser).sendAsyncMessage('ViewSource:ToggleSyntaxHighlighting');"/>
       <menuseparator id="context-sep-viewsource-commands"/>
       <menuitem id="spell-no-suggestions"
                 disabled="true"
                 data-l10n-id="text-action-spell-no-suggestions"/>
       <menuitem id="spell-add-to-dictionary"
-                label="&spellAddToDictionary.label;"
-                accesskey="&spellAddToDictionary.accesskey;"
+                data-l10n-id="text-action-spell-add-to-dictionary"
                 oncommand="InlineSpellCheckerUI.addToDictionary();"/>
       <menuitem id="spell-undo-add-to-dictionary"
-                label="&spellUndoAddToDictionary.label;"
-                accesskey="&spellUndoAddToDictionary.accesskey;"
+                data-l10n-id="text-action-spell-undo-add-to-dictionary"
                 oncommand="InlineSpellCheckerUI.undoAddToDictionary();" />
       <menuseparator id="spell-suggestions-separator"/>
       <menuitem id="context-openlinkincurrent"
                 data-l10n-id="main-context-menu-open-link"
                 oncommand="gContextMenu.openLinkInCurrent();"/>
 # label and data-usercontextid are dynamically set.
       <menuitem id="context-openlinkincontainertab"
-                accesskey="&openLinkCmdInTab.accesskey;"
+                data-l10n-id="main-context-menu-open-link-in-container-tab"
+                data-l10n-args='{"containerName":""}'
                 oncommand="gContextMenu.openLinkInTab(event);"/>
       <menuitem id="context-openlinkintab"
                 data-l10n-id="main-context-menu-open-link-new-tab"
                 data-usercontextid="0"
                 oncommand="gContextMenu.openLinkInTab(event);"/>
 
       <menu id="context-openlinkinusercontext-menu"
             data-l10n-id="main-context-menu-open-link-container-tab"
@@ -356,32 +355,28 @@
           <menuitem id="context-frameOsPid"
                     label="PID: Unknown"
                     disabled="true"/>
 #endif
         </menupopup>
       </menu>
       <menuseparator id="spell-separator"/>
       <menuitem id="spell-check-enabled"
-                label="&spellCheckToggle.label;"
+                data-l10n-id="text-action-spell-check-toggle"
                 type="checkbox"
-                accesskey="&spellCheckToggle.accesskey;"
                 oncommand="InlineSpellCheckerUI.toggleEnabled(window);"/>
       <menuitem id="spell-add-dictionaries-main"
-                label="&spellAddDictionaries.label;"
-                accesskey="&spellAddDictionaries.accesskey;"
+                data-l10n-id="text-action-spell-add-dictionaries"
                 oncommand="gContextMenu.addDictionaries();"/>
       <menu id="spell-dictionaries"
-            label="&spellDictionaries.label;"
-            accesskey="&spellDictionaries.accesskey;">
+            data-l10n-id="text-action-spell-dictionaries">
           <menupopup id="spell-dictionaries-menu">
               <menuseparator id="spell-language-separator"/>
               <menuitem id="spell-add-dictionaries"
-                        label="&spellAddDictionaries.label;"
-                        accesskey="&spellAddDictionaries.accesskey;"
+                        data-l10n-id="text-action-spell-add-dictionaries"
                         oncommand="gContextMenu.addDictionaries();"/>
           </menupopup>
       </menu>
       <menuseparator hidden="true" id="context-sep-bidi"/>
       <menuitem hidden="true" id="context-bidi-text-direction-toggle"
                 data-l10n-id="main-context-menu-bidi-switch-text"
                 command="cmd_switchTextDirection"/>
       <menuitem hidden="true" id="context-bidi-page-direction-toggle"
--- 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
@@ -192,23 +192,26 @@ this.SyncedTabsPanelList = class SyncedT
 
   _clearSyncedTabList() {
     let list = this.tabsList;
     while (list.lastChild) {
       list.lastChild.remove();
     }
   }
 
-  _appendMessageLabel(messageAttr, appendTo = null) {
+  _createNoSyncedTabsElement(messageAttr, appendTo = null) {
     if (!appendTo) {
       appendTo = this.tabsList;
     }
-    let message = this.tabsList.getAttribute(messageAttr);
+
     let messageLabel = document.createXULElement("label");
-    messageLabel.textContent = message;
+    document.l10n.setAttributes(
+      messageLabel,
+      this.tabsList.getAttribute(messageAttr)
+    );
     appendTo.appendChild(messageLabel);
     return messageLabel;
   }
 
   _appendSyncClient(
     client,
     container,
     labelId,
@@ -224,28 +227,30 @@ this.SyncedTabsPanelList = class SyncedT
         time: gSync.formatLastSyncDate(new Date(client.lastModified)),
       })
     );
     clientItem.textContent = client.name;
 
     container.appendChild(clientItem);
 
     if (!client.tabs.length) {
-      let label = this._appendMessageLabel("notabsforclientlabel", container);
+      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);
@@ -289,33 +294,27 @@ this.SyncedTabsPanelList = class SyncedT
       } else {
         CustomizableUI.hidePanelForNode(item);
       }
     });
     return item;
   }
 
   _createShowMoreSyncedTabsElement(clientId) {
-    let labelAttr = "showMoreLabel";
-    let tooltipAttr = "showMoreTooltipText";
     let showCount = Infinity;
 
     let showMoreItem = document.createXULElement("toolbarbutton");
     showMoreItem.setAttribute("itemtype", "showmorebutton");
     showMoreItem.classList.add(
       "subviewbutton",
       "subviewbutton-nav",
       "subviewbutton-nav-down"
     );
-    let label = gSync.fluentStrings.formatValueSync(
-      this.tabsList.getAttribute(labelAttr)
-    );
-    showMoreItem.setAttribute("label", label);
-    let tooltipText = this.tabsList.getAttribute(tooltipAttr);
-    showMoreItem.setAttribute("tooltiptext", tooltipText);
+    document.l10n.setAttributes(showMoreItem, "appmenu-remote-tabs-showmore");
+
     showMoreItem.addEventListener("click", e => {
       e.preventDefault();
       e.stopPropagation();
       this._showSyncedTabs({ clientId, maxTabs: showCount });
     });
     return showMoreItem;
   }
 
@@ -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
@@ -1108,44 +1108,42 @@ var gPopupBlockerObserver = {
         : uriOrPrincipal.spec;
       var pm = Services.perms;
       if (
         pm.testPermissionFromPrincipal(browser.contentPrincipal, "popup") ==
         pm.ALLOW_ACTION
       ) {
         // Offer an item to block popups for this site, if an allow-list entry exists
         // already for it.
-        let blockString = gNavigatorBundle.getFormattedString("popupBlock", [
-          uriHost,
-        ]);
-        blockedPopupAllowSite.setAttribute("label", blockString);
+        document.l10n.setAttributes(
+          blockedPopupAllowSite,
+          "popups-infobar-block",
+          { uriHost }
+        );
         blockedPopupAllowSite.setAttribute("block", "true");
       } else {
         // Offer an item to allow popups for this site
-        let allowString = gNavigatorBundle.getFormattedString("popupAllow", [
-          uriHost,
-        ]);
-        blockedPopupAllowSite.setAttribute("label", allowString);
+        document.l10n.setAttributes(
+          blockedPopupAllowSite,
+          "popups-infobar-allow",
+          { uriHost }
+        );
         blockedPopupAllowSite.removeAttribute("block");
       }
     } catch (e) {
       blockedPopupAllowSite.hidden = true;
     }
 
     let blockedPopupDontShowMessage = document.getElementById(
       "blockedPopupDontShowMessage"
     );
     let showMessage = Services.prefs.getBoolPref(
       "privacy.popups.showBrowserMessage"
     );
     blockedPopupDontShowMessage.setAttribute("checked", !showMessage);
-    blockedPopupDontShowMessage.setAttribute(
-      "label",
-      gNavigatorBundle.getString("popupWarningDontShowFromMessage")
-    );
 
     let blockedPopupsSeparator = document.getElementById(
       "blockedPopupsSeparator"
     );
     blockedPopupsSeparator.hidden = true;
 
     browser.popupBlocker.getBlockedPopups().then(blockedPopups => {
       let foundUsablePopupURI = false;
@@ -2015,26 +2013,16 @@ var gBrowserInit = {
       // Enable the Restore Last Session command if needed
       RestoreLastSessionObserver.init();
 
       SidebarUI.startDelayedLoad();
 
       PanicButtonNotifier.init();
     });
 
-    gBrowser.tabContainer.addEventListener("TabSelect", function() {
-      for (let panel of document.querySelectorAll(
-        "panel[tabspecific='true']"
-      )) {
-        if (panel.state == "open") {
-          panel.hidePopup();
-        }
-      }
-    });
-
     if (BrowserHandler.kiosk) {
       // We don't modify popup windows for kiosk mode
       if (!gURLBar.readOnly) {
         window.fullScreen = true;
       }
     }
 
     if (Services.policies.status === Services.policies.ACTIVE) {
@@ -5419,16 +5407,35 @@ var XULBrowserWindow = {
       setToolbarVisibility(
         bookmarksToolbar,
         gBookmarksToolbarVisibility,
         false,
         false
       );
     }
 
+    let closeOpenPanels = selector => {
+      for (let panel of document.querySelectorAll(selector)) {
+        if (panel.state == "open") {
+          panel.hidePopup();
+        }
+      }
+    };
+
+    // If the location is changed due to switching tabs,
+    // ensure we close any open tabspecific panels.
+    if (aIsSimulated) {
+      closeOpenPanels("panel[tabspecific='true']");
+    }
+
+    // Ensure we close any remaining open locationspecific panels
+    if (!isSameDocument) {
+      closeOpenPanels("panel[locationspecific='true']");
+    }
+
     // About pages other than about:reader are not currently supported by
     // screenshots (see Bug 1620992).
     Services.obs.notifyObservers(
       window,
       "toggle-screenshot-disable",
       aLocationURI.scheme == "about" &&
         !aLocationURI.spec.startsWith("about:reader")
     );
@@ -8261,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/browser.xhtml
+++ b/browser/base/content/browser.xhtml
@@ -68,22 +68,24 @@
   <link rel="localization" href="browser/branding/brandings.ftl"/>
   <link rel="localization" href="toolkit/global/textActions.ftl"/>
   <link rel="localization" href="browser/browser.ftl"/>
   <link rel="localization" href="browser/browserContext.ftl"/>
   <link rel="localization" href="browser/browserSets.ftl"/>
   <link rel="localization" href="browser/menubar.ftl"/>
   <link rel="localization" href="browser/protectionsPanel.ftl"/>
   <link rel="localization" href="browser/appmenu.ftl"/>
+  <link rel="localization" href="browser/panelUI.ftl"/>
   <link rel="localization" href="preview/interventions.ftl"/>
   <link rel="localization" href="browser/sidebarMenu.ftl"/>
   <link rel="localization" href="browser/allTabsMenu.ftl"/>
   <link rel="localization" href="browser/places.ftl"/>
   <link rel="localization" href="toolkit/printing/printUI.ftl"/>
   <link rel="localization" href="browser/tabbrowser.ftl"/>
+  <link rel="localization" href="preview/firefoxSuggest.ftl"/>
 
   <title data-l10n-id="browser-main-window-title"></title>
 
 # All JS files which are needed by browser.xhtml and other top level windows to
 # support MacOS specific features *must* go into the global-scripts.inc file so
 # that they can be shared with macWindow.inc.xhtml.
 #include global-scripts.inc
 
--- 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
@@ -323,17 +337,16 @@ function initPage() {
     }
   }
 
   if (gIsCertError) {
     if (showCaptivePortalUI) {
       initPageCaptivePortal();
     } else {
       initPageCertError();
-      updateContainerPosition();
     }
 
     initCertErrorPageActions();
     setTechnicalDetailsOnCertError();
     return;
   }
 
   setFocus("#netErrorButtonContainer > .try-again");
@@ -589,38 +602,16 @@ async function setNetErrorMessageFromCod
   });
 
   let desc2 = document.getElementById("errorShortDescText2");
   document.l10n.setAttributes(desc2, "cert-error-code-prefix", {
     error: errorCodeStr,
   });
 }
 
-// This function centers the error container after its content updates.
-// It is currently duplicated in NetErrorChild.jsm to avoid having to do
-// async communication to the page that would result in flicker.
-// TODO(johannh): Get rid of this duplication.
-function updateContainerPosition() {
-  let textContainer = document.getElementById("text-container");
-  // Using the vh CSS property our margin adapts nicely to window size changes.
-  // Unfortunately, this doesn't work correctly in iframes, which is why we need
-  // to manually compute the height there.
-  if (window.parent == window) {
-    textContainer.style.marginTop = `calc(50vh - ${textContainer.clientHeight /
-      2}px)`;
-  } else {
-    let offset =
-      document.documentElement.clientHeight / 2 -
-      textContainer.clientHeight / 2;
-    if (offset > 0) {
-      textContainer.style.marginTop = `${offset}px`;
-    }
-  }
-}
-
 function initPageCaptivePortal() {
   document.body.className = "captiveportal";
   document
     .getElementById("openPortalLoginPageButton")
     .addEventListener("click", () => {
       RPMSendAsyncMessage("Browser:OpenCaptivePortalPage");
     });
 
@@ -831,17 +822,16 @@ function setCertErrorDetails(event) {
       if (es) {
         // eslint-disable-next-line no-unsanitized/property
         es.innerHTML = errWhatToDo.innerHTML;
       }
       if (est) {
         // eslint-disable-next-line no-unsanitized/property
         est.innerHTML = errWhatToDoTitle.innerHTML;
       }
-      updateContainerPosition();
       break;
 
     // This error code currently only exists for the Symantec distrust
     // in Firefox 63, so we add copy explaining that to the user.
     // In case of future distrusts of that scale we might need to add
     // additional parameters that allow us to identify the affected party
     // without replicating the complex logic from certverifier code.
     case "MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED":
@@ -856,17 +846,16 @@ function setCertErrorDetails(event) {
 
       let adminDesc = document.createElement("p");
       document.l10n.setAttributes(
         adminDesc,
         "cert-error-symantec-distrust-admin"
       );
 
       learnMoreLink.href = baseURL + "symantec-warning";
-      updateContainerPosition();
       break;
 
     case "MOZILLA_PKIX_ERROR_MITM_DETECTED":
       let autoEnabledEnterpriseRoots = RPMGetBoolPref(
         "security.enterprise_roots.auto-enabled",
         false
       );
       if (mitmPrimingEnabled && autoEnabledEnterpriseRoots) {
@@ -889,18 +878,16 @@ function setCertErrorDetails(event) {
       desc = document.getElementById("ed_mitm");
       // eslint-disable-next-line no-unsanitized/property
       document.getElementById("errorShortDescText").innerHTML = desc.innerHTML;
 
       // eslint-disable-next-line no-unsanitized/property
       es.innerHTML = errWhatToDo.innerHTML;
       // eslint-disable-next-line no-unsanitized/property
       est.innerHTML = errWhatToDoTitle.innerHTML;
-
-      updateContainerPosition();
       break;
 
     case "MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT":
       learnMoreLink.href = baseURL + "security-error";
       break;
 
     // In case the certificate expired we make sure the system clock
     // matches the remote-settings service (blocklist via Kinto) ping time
@@ -1016,17 +1003,16 @@ function setCertErrorDetails(event) {
           // eslint-disable-next-line no-unsanitized/property
           es.innerHTML = errWhatToDo.innerHTML;
         }
         if (est) {
           // eslint-disable-next-line no-unsanitized/property
           est.textContent = errWhatToDoTitle.textContent;
           est.style.fontWeight = "bold";
         }
-        updateContainerPosition();
       }
       break;
   }
 }
 
 // The optional argument is only here for testing purposes.
 async function setTechnicalDetailsOnCertError(
   failedCertInfo = document.getFailedCertSecurityInfo()
@@ -1171,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",
@@ -1222,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.
@@ -1241,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);"/>
@@ -231,17 +233,17 @@
            role="alert">
      <vbox>
       <hbox id="UITourTooltipBody">
         <image id="UITourTooltipIcon"/>
         <vbox flex="1">
           <hbox id="UITourTooltipTitleContainer">
             <label id="UITourTooltipTitle" flex="1"/>
             <toolbarbutton id="UITourTooltipClose" class="close-icon"
-                           tooltiptext="&uiTour.infoPanel.close;"/>
+                           data-l10n-id="ui-tour-info-panel-close"/>
           </hbox>
           <description id="UITourTooltipDescription" flex="1"/>
         </vbox>
       </hbox>
       <hbox id="UITourTooltipButtons" flex="1" align="center"/>
      </vbox>
     </panel>
   </html:template>
@@ -373,28 +375,22 @@
               class="viewCustomizeToolbar"
               data-lazy-l10n-id="toolbar-context-menu-view-customize-toolbar-2"/>
   </menupopup>
 
   <menupopup id="blockedPopupOptions"
              onpopupshowing="gPopupBlockerObserver.fillPopupList(event);"
              onpopuphiding="gPopupBlockerObserver.onPopupHiding(event);">
     <menuitem id="blockedPopupAllowSite"
-              accesskey="&allowPopups.accesskey;"
               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"
-              accesskey="&dontShowMessage.accesskey;"
+              data-l10n-id="popups-infobar-dont-show-message"
               type="checkbox"
               oncommand="gPopupBlockerObserver.dontShowMessage();"/>
     <menuseparator id="blockedPopupsSeparator"/>
   </menupopup>
 
   <menupopup id="autohide-context"
          onpopupshowing="FullScreen.getAutohide(this.firstChild);">
     <menuitem type="checkbox" data-l10n-id="full-screen-autohide"
@@ -415,18 +411,17 @@
                               return;
                             gContextMenu.hiding(this);
                             gContextMenu = null;
                             updateEditUIVisibility();">
 #include browser-context.inc
   </menupopup>
 
   <menupopup id="pictureInPictureToggleContextMenu">
-    <menuitem label="&pictureInPictureHideToggle.label;"
-              accesskey="&pictureInPictureHideToggle.accesskey;"
+    <menuitem data-l10n-id="picture-in-picture-hide-toggle"
               oncommand="PictureInPicture.hideToggle();" />
   </menupopup>
 
 #include ../../components/places/content/placesContextMenu.inc.xhtml
 
   <panel id="ctrlTab-panel" hidden="true" norestorefocus="true" level="top" orient="vertical">
     <hbox id="ctrlTab-previews"/>
     <hbox id="ctrlTab-showAll-container" pack="center"/>
--- a/browser/base/content/metrics.yaml
+++ b/browser/base/content/metrics.yaml
@@ -1,6 +1,6 @@
 # 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/.
 
 ---
-$schema: moz://mozilla.org/schemas/glean/metrics/1-0-0
+$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
--- 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);"
@@ -608,17 +608,17 @@
         <!-- NB: temporary solution for bug 985024, this should go away soon. -->
         <menuitem id="BMB_bookmarksShowAllTop"
                   class="subviewbutton"
                   data-l10n-id="bookmarks-manage-bookmarks"
                   command="Browser:ShowAllBookmarks"
                   key="manBookmarkKb"/>
         <menuseparator/>
         <menu id="BMB_bookmarksToolbar"
-              class="bookmark-item subviewbutton"
+              class="bookmark-item subviewbutton menu-iconic"
               data-l10n-id="bookmarks-toolbar-menu"
               container="true">
           <menupopup id="BMB_bookmarksToolbarPopup"
                      is="places-popup"
                      placespopup="true"
                      nofooterpopup="true"
                      context="placesContext"
                      onpopupshowing="if (!this.parentNode._placesView)
@@ -629,17 +629,17 @@
                       data-l10n-id="bookmarks-tools-toolbar-visibility-menuitem"
                       data-l10n-args='{ "isVisible": false }'
                       oncommand="BookmarkingUI.toggleBookmarksToolbar('bookmarks-widget');"/>
             <menuseparator/>
             <!-- Bookmarks toolbar items -->
           </menupopup>
         </menu>
         <menu id="BMB_unsortedBookmarks"
-              class="bookmark-item subviewbutton"
+              class="bookmark-item subviewbutton menu-iconic"
               data-l10n-id="bookmarks-other-bookmarks-menu"
               container="true">
           <menupopup id="BMB_unsortedBookmarksPopup"
                      is="places-popup"
                      placespopup="true"
                      nofooterpopup="true"
                      context="placesContext"
                      onpopupshowing="if (!this.parentNode._placesView)
--- 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;
@@ -389,21 +389,23 @@ class nsContextMenu {
       inContainer = true;
       var item = document.getElementById("context-openlinkincontainertab");
 
       item.setAttribute("data-usercontextid", this.contentData.userContextId);
 
       var label = ContextualIdentityService.getUserContextLabel(
         this.contentData.userContextId
       );
-      item.setAttribute(
-        "label",
-        gBrowserBundle.formatStringFromName("userContextOpenLink.label", [
-          label,
-        ])
+
+      document.l10n.setAttributes(
+        item,
+        "main-context-menu-open-link-in-container-tab",
+        {
+          containerName: label,
+        }
       );
     }
 
     var shouldShow =
       this.onSaveableLink || isMailtoInternal || this.onPlainTextLink;
     var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
     let showContainers =
       Services.prefs.getBoolPref("privacy.userContext.enabled") &&
@@ -768,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/about/browser_aboutNetError.js
+++ b/browser/base/content/test/about/browser_aboutNetError.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const SSL3_PAGE = "https://ssl3.example.com/";
 const TLS10_PAGE = "https://tls1.example.com/";
 const TLS12_PAGE = "https://tls12.example.com/";
+const TRIPLEDES_PAGE = "https://3des.example.com/";
 
 // This includes all the cipher suite prefs we have.
 const CIPHER_SUITE_PREFS = [
   "security.ssl3.dhe_rsa_aes_128_sha",
   "security.ssl3.dhe_rsa_aes_256_sha",
   "security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256",
   "security.ssl3.ecdhe_ecdsa_aes_128_sha",
   "security.ssl3.ecdhe_ecdsa_aes_256_gcm_sha384",
@@ -20,27 +21,30 @@ const CIPHER_SUITE_PREFS = [
   "security.ssl3.ecdhe_rsa_aes_128_sha",
   "security.ssl3.ecdhe_rsa_aes_256_gcm_sha384",
   "security.ssl3.ecdhe_rsa_aes_256_sha",
   "security.ssl3.ecdhe_rsa_chacha20_poly1305_sha256",
   "security.ssl3.rsa_aes_128_sha",
   "security.ssl3.rsa_aes_256_sha",
   "security.ssl3.rsa_aes_128_gcm_sha256",
   "security.ssl3.rsa_aes_256_gcm_sha384",
-  "security.ssl3.rsa_des_ede3_sha",
+  "security.ssl3.deprecated.rsa_des_ede3_sha",
   "security.tls13.aes_128_gcm_sha256",
   "security.tls13.aes_256_gcm_sha384",
   "security.tls13.chacha20_poly1305_sha256",
 ];
 
 function resetPrefs() {
   Services.prefs.clearUserPref("security.tls.version.min");
   Services.prefs.clearUserPref("security.tls.version.max");
   Services.prefs.clearUserPref("security.tls.version.enable-deprecated");
   Services.prefs.clearUserPref("security.certerrors.tls.version.show-override");
+  CIPHER_SUITE_PREFS.forEach(suitePref => {
+    Services.prefs.clearUserPref(suitePref);
+  });
 }
 
 add_task(async function resetToDefaultConfig() {
   info(
     "Change TLS config to cause page load to fail, check that reset button is shown and that it works"
   );
 
   // Just twiddling version will trigger the TLS 1.0 offer.  So to test the
@@ -312,8 +316,46 @@ add_task(async function overrideUIPref()
       !ContentTaskUtils.is_visible(enableTls10Button),
       "Option to re-enable TLS 1.0 is not visible"
     );
   });
 
   resetPrefs();
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
+
+// Test that ciphersuites that use 3DES (namely, TLS_RSA_WITH_3DES_EDE_CBC_SHA)
+// can only be enabled when deprecated TLS is enabled.
+add_task(async function onlyAllow3DESWithDeprecatedTLS() {
+  // By default, connecting to a server that only uses 3DES should fail.
+  await BrowserTestUtils.withNewTab(
+    { gBrowser, url: "about:blank" },
+    async browser => {
+      BrowserTestUtils.loadURI(browser, TRIPLEDES_PAGE);
+      await BrowserTestUtils.waitForErrorPage(browser);
+    }
+  );
+
+  // Enabling deprecated TLS should also enable 3DES.
+  Services.prefs.setBoolPref("security.tls.version.enable-deprecated", true);
+  await BrowserTestUtils.withNewTab(
+    { gBrowser, url: "about:blank" },
+    async browser => {
+      BrowserTestUtils.loadURI(browser, TRIPLEDES_PAGE);
+      await BrowserTestUtils.browserLoaded(browser, false, TRIPLEDES_PAGE);
+    }
+  );
+
+  // 3DES can be disabled separately.
+  Services.prefs.setBoolPref(
+    "security.ssl3.deprecated.rsa_des_ede3_sha",
+    false
+  );
+  await BrowserTestUtils.withNewTab(
+    { gBrowser, url: "about:blank" },
+    async browser => {
+      BrowserTestUtils.loadURI(browser, TRIPLEDES_PAGE);
+      await BrowserTestUtils.waitForErrorPage(browser);
+    }
+  );
+
+  resetPrefs();
+});
--- a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js
+++ b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js
@@ -46,49 +46,41 @@ let testcases = [
         "The captive portal tab should be selected."
       );
       testShowLoginPageButtonVisibility(notification, "hidden");
     }
 
     let button = notification.buttonContainer.querySelector(
       "button.notification-button"
     );
-    async function clickButtonAndExpectNewPortalTabAndNotification() {
-      let newTabCreation = BrowserTestUtils.waitForNewTab(
-        win.gBrowser,
-        CANONICAL_URL
-      );
-      let buttonPressedEvent = TestUtils.topicObserved(
-        "captive-portal-login-button-pressed"
-      );
-
+    async function clickButtonAndExpectNewPortalTab() {
+      let p = BrowserTestUtils.waitForNewTab(win.gBrowser, CANONICAL_URL);
       button.click();
-
-      const [tab] = await Promise.all([newTabCreation, buttonPressedEvent]);
+      let tab = await p;
       is(
         win.gBrowser.selectedTab,
         tab,
         "The captive portal tab should be selected."
       );
       return tab;
     }
 
     // Simulate clicking the button. The portal tab should be opened and
     // selected and the button should hide.
-    let tab = await clickButtonAndExpectNewPortalTabAndNotification();
+    let tab = await clickButtonAndExpectNewPortalTab();
     testPortalTabSelectedAndButtonNotVisible();
 
     // Close the tab. The button should become visible.
     BrowserTestUtils.removeTab(tab);
     ensureNoPortalTab(win);
     testShowLoginPageButtonVisibility(notification, "visible");
 
     // When the button is clicked, a new portal tab should be opened and
     // selected.
-    tab = await clickButtonAndExpectNewPortalTabAndNotification();
+    tab = await clickButtonAndExpectNewPortalTab();
 
     // Open another arbitrary tab. The button should become visible. When it's clicked,
     // the portal tab should be selected.
     let anotherTab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser);
     testShowLoginPageButtonVisibility(notification, "visible");
     button.click();
     is(
       win.gBrowser.selectedTab,
@@ -96,82 +88,21 @@ let testcases = [
       "The captive portal tab should be selected."
     );
 
     // Close the portal tab and select the arbitrary tab. The button should become
     // visible and when it's clicked, a new portal tab should be opened.
     BrowserTestUtils.removeTab(tab);
     win.gBrowser.selectedTab = anotherTab;
     testShowLoginPageButtonVisibility(notification, "visible");
-    tab = await clickButtonAndExpectNewPortalTabAndNotification();
+    tab = await clickButtonAndExpectNewPortalTab();
 
     BrowserTestUtils.removeTab(anotherTab);
     await freePortal(true);
     ensureNoPortalTab(win);
     ensureNoPortalNotification(win);
     await closeWindowAndWaitForWindowActivate(win);
   },
-  async function testLoginSuccessAfterButtonPress() {
-    // test that button not clicked generates no notification
-    let loginSuccessAfterButtonPress = TestUtils.topicObserved(
-      "captive-portal-login-success-after-button-pressed"
-    );
-    window.CaptivePortalWatcher.observe(null, "captive-portal-login-success");
-
-    let exception = undefined;
-    try {
-      await waitForPromiseWithTimeout(loginSuccessAfterButtonPress);
-    } catch (ex) {
-      exception = ex;
-    }
-    isnot(
-      exception,
-      undefined,
-      "captive-portal-login-success-after-button-pressed should not have" +
-        " been sent, because button was not pressed"
-    );
-
-    // test that button clicked does generate a notification
-    loginSuccessAfterButtonPress = TestUtils.topicObserved(
-      "captive-portal-login-success-after-button-pressed"
-    );
-
-    window.CaptivePortalWatcher.observe(
-      null,
-      "captive-portal-login-button-pressed"
-    );
-    window.CaptivePortalWatcher.observe(null, "captive-portal-login-success");
-
-    await loginSuccessAfterButtonPress;
-
-    // test that subsequent captive-portal-login-success without a click
-    // does not send a notification
-
-    // Normally, we would stub Cu.now() and make it move the clock forward.
-    // Unfortunately, since it's in C++, we can't do that here, so instead
-    // we move the time stamp into the past so that things will have expired.
-    window.CaptivePortalWatcher._loginButtonPressedTimeStamp -=
-      window.CaptivePortalWatcher._LOGIN_BUTTON_PRESSED_TIMEOUT;
-
-    loginSuccessAfterButtonPress = TestUtils.topicObserved(
-      "captive-portal-login-success-after-button-pressed"
-    );
-
-    window.CaptivePortalWatcher.observe(null, "captive-portal-login-success");
-    exception = undefined;
-    try {
-      await waitForPromiseWithTimeout(loginSuccessAfterButtonPress);
-    } catch (ex) {
-      exception = ex;
-    }
-
-    isnot(
-      exception,
-      undefined,
-      "captive-portal-login-success-after-button-pressed should not have" +
-        " been sent, button was not pressed again"
-    );
-  },
 ];
 
 for (let testcase of testcases) {
   add_task(testcase);
 }
--- a/browser/base/content/test/captivePortal/head.js
+++ b/browser/base/content/test/captivePortal/head.js
@@ -263,20 +263,8 @@ async function openCaptivePortalLoginTab
   is(
     gBrowser.selectedTab,
     portalTab,
     "Captive Portal login page is now open in a new foreground tab."
   );
 
   return portalTab;
 }
-
-// XXX This likely wants to move to TestUtils, mostly borrowed from
-// browser_doorhanger_form_password_edit.js
-async function waitForPromiseWithTimeout(promise, timeoutMs = 5000) {
-  let timedOut = new Promise((resolve, reject) => {
-    /* eslint-disable-next-line mozilla/no-arbitrary-setTimeout */
-    setTimeout(() => {
-      reject(new Error(`Timed out in ${timeoutMs} ms.`));
-    }, timeoutMs);
-  });
-  await Promise.race([promise, timedOut]);
-}
--- 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/fullscreen/browser.ini
+++ b/browser/base/content/test/fullscreen/browser.ini
@@ -5,16 +5,17 @@ support-files =
 [browser_bug1557041.js]
 skip-if = os == 'linux' # Bug 1561973
 [browser_fullscreen_permissions_prompt.js]
 skip-if = debug && os == 'mac' # Bug 1568570
 [browser_fullscreen_cross_origin.js]
 support-files = fullscreen.html fullscreen_frame.html
 [browser_bug1620341.js]
 support-files = fullscreen.html fullscreen_frame.html
+skip-if = win10_2004 # Bug 1727168 and Bug 1723574
 [browser_fullscreen_enterInUrlbar.js]
 skip-if = (os == 'mac') || (os == 'linux') #Bug 1648649
 [browser_fullscreen_window_open.js]
 skip-if = debug && os == 'mac' # Bug 1568570
 [browser_fullscreen_window_focus.js]
 skip-if =
   os == 'mac' # Bug 1568570
 [browser_fullscreen_api_fission.js]
--- 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.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -15,17 +15,18 @@ prefs =
   extensions.formautofill.available='on'
   browser.urlbar.disableExtendForTests=true
   # The Screenshots extension is disabled by default in Mochitests. We re-enable
   # it here, since it's a more realistic configuration.
   extensions.screenshots.disabled=false
 support-files =
   head.js
 [browser_appmenu.js]
-skip-if = asan || debug || (os == 'win' && bits == 32) || (os == 'win' && processor == 'aarch64') # Bug 1382809, bug 1369959, Win32 because of intermittent OOM failures, bug 1533141 for aarch64
+skip-if =
+  asan || debug || (os == 'win' && bits == 32) || (os == 'win' && processor == 'aarch64') # Bug 1382809, bug 1369959, Win32 because of intermittent OOM failures, bug 1533141 for aarch64
 [browser_preferences_usage.js]
 https_first_disabled = true
 skip-if =
   !debug
   apple_catalina  # platform migration
   os == 'win' && fission  # Bug 1713890 - new Fission platform triage
 [browser_startup.js]
 [browser_startup_content.js]
--- 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/siteIdentity/browser.ini
+++ b/browser/base/content/test/siteIdentity/browser.ini
@@ -63,16 +63,19 @@ support-files =
   file_mixedPassiveContent.html
   file_csp_block_all_mixedcontent.html
 [browser_identityPopup_clearSiteData.js]
 skip-if = (os == "linux" && bits == 64) # Bug 1577395
 [browser_identityPopup_clearSiteData_extensions.js]
 [browser_identityPopup_custom_roots.js]
 https_first_disabled = true
 [browser_identityPopup_focus.js]
+skip-if =
+  verify
+  os == "linux" && (asan || tsan) # Bug 1723899
 [browser_identityPopup_HttpsOnlyMode.js]
 [browser_mcb_redirect.js]
 https_first_disabled = true
 tags = mcb
 support-files =
   test_mcb_redirect.html
   test_mcb_redirect_image.html
   test_mcb_double_redirect_image.html
--- 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/",
 
@@ -260,16 +262,19 @@ var whitelist = [
   {
     file:
       "chrome://browser/content/screenshots/icon-welcome-face-without-eyes.svg",
   },
   { file: "chrome://browser/content/screenshots/menu-fullpage.svg" },
   { file: "chrome://browser/content/screenshots/menu-visible.svg" },
 
   { file: "resource://app/modules/SnapshotSelector.jsm" },
+
+  // toolkit/xre/MacRunFromDmgUtils.mm
+  { file: "resource://gre/localization/en-US/toolkit/global/run-from-dmg.ftl" },
 ];
 
 if (AppConstants.NIGHTLY_BUILD && AppConstants.platform != "win") {
   // This path is refereneced in nsFxrCommandLineHandler.cpp, which is only
   // compiled in Windows. Whitelisted this path so that non-Windows builds
   // can access the FxR UI via --chrome rather than --fxr (which includes VR-
   // specific functionality)
   whitelist.push({ file: "chrome://fxr/content/fxrui.html" });
--- a/browser/base/content/test/tabPrompts/browser_closeTabSpecificPanels.js
+++ b/browser/base/content/test/tabPrompts/browser_closeTabSpecificPanels.js
@@ -7,17 +7,19 @@
  *
  */
 
 add_task(async function() {
   let tab1 = BrowserTestUtils.addTab(gBrowser, "http://mochi.test:8888/#0");
   let tab2 = BrowserTestUtils.addTab(gBrowser, "http://mochi.test:8888/#1");
   let specificPanel = document.createXULElement("panel");
   specificPanel.setAttribute("tabspecific", "true");
+  specificPanel.setAttribute("noautohide", "true");
   let generalPanel = document.createXULElement("panel");
+  generalPanel.setAttribute("noautohide", "true");
   let anchor = document.getElementById(CustomizableUI.AREA_NAVBAR);
 
   anchor.appendChild(specificPanel);
   anchor.appendChild(generalPanel);
   is(specificPanel.state, "closed", "specificPanel starts as closed");
   is(generalPanel.state, "closed", "generalPanel starts as closed");
 
   let specificPanelPromise = BrowserTestUtils.waitForEvent(
--- 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/get_user_media.html
+++ b/browser/base/content/test/webrtc/get_user_media.html
@@ -26,16 +26,17 @@ function message(m) {
 var gStreams = [];
 var gVideoEvents = [];
 var gAudioEvents = [];
 
 async function requestDevice(aAudio, aVideo, aShare, aBadDevice = false) {
   const opts = {video: aVideo, audio: aAudio};
   if (aShare) {
     opts.video = { mediaSource: aShare };
+    SpecialPowers.wrap(document).notifyUserGestureActivation();
   }
   if (useFakeStreams) {
     opts.fake = true;
   }
 
   if (aVideo && aBadDevice) {
     opts.video = {
       deviceId: "bad device",
--- a/browser/base/content/test/webrtc/get_user_media_in_frame.html
+++ b/browser/base/content/test/webrtc/get_user_media_in_frame.html
@@ -23,20 +23,17 @@ function message(m) {
   window.top.postMessage(m, "*");
 }
 
 var gStreams = [];
 
 function requestDevice(aAudio, aVideo, aShare) {
   var opts = {video: aVideo, audio: aAudio};
   if (aShare) {
-    opts.video = {
-      mozMediaSource: aShare,
-      mediaSource: aShare,
-    };
+    opts.video = { mediaSource: aShare };
   } else if (useFakeStreams) {
     opts.fake = true;
   }
 
   window.navigator.mediaDevices.getUserMedia(opts)
     .then(stream => {
       gStreams.push(stream);
       message("ok");
--- a/browser/base/content/test/webrtc/get_user_media_in_xorigin_frame.html
+++ b/browser/base/content/test/webrtc/get_user_media_in_xorigin_frame.html
@@ -23,20 +23,17 @@ function message(m) {
   window.parent.postMessage(m, "*");
 }
 
 var gStreams = [];
 
 function requestDevice(aAudio, aVideo, aShare) {
   var opts = {video: aVideo, audio: aAudio};
   if (aShare) {
-    opts.video = {
-      mozMediaSource: aShare,
-      mediaSource: aShare,
-    };
+    opts.video = { mediaSource: aShare };
   } else if (useFakeStreams) {
     opts.fake = true;
   }
 
   window.navigator.mediaDevices.getUserMedia(opts)
     .then(stream => {
       gStreams.push(stream);
       message("ok");
--- 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(
@@ -920,16 +920,31 @@ nsDefaultCommandLineHandler.prototype = 
   QueryInterface: ChromeUtils.generateQI(["nsICommandLineHandler"]),
 
   _haveProfile: false,
 
   /* nsICommandLineHandler */
   handle: function dch_handle(cmdLine) {
     var urilist = [];
 
+    if (
+      cmdLine.state == Ci.nsICommandLine.STATE_INITIAL_LAUNCH &&
+      Services.startup.wasSilentlyStarted
+    ) {
+      // 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;
+    }
+
     if (AppConstants.platform == "win") {
       // If we don't have a profile selected yet (e.g. the Profile Manager is
       // displayed) we will crash if we open an url and then select a profile. To
       // prevent this handle all url command line flags and set the command line's
       // preventDefault to true to prevent the display of the ui. The initial
       // command line will be retained when nsAppRunner calls LaunchChild though
       // urls launched after the initial launch will be lost.
       if (!this._haveProfile) {
--- 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/aboutLogins.html
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -1,17 +1,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/. -->
 
 <!DOCTYPE html>
 <html>
   <head>
     <meta charset="utf-8">
-    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; img-src data: blob: https://firefoxusercontent.com;"/>
+    <meta http-equiv="Content-Security-Policy" content="default-src 'none'; object-src 'none'; script-src resource: chrome:; img-src data: blob: https://firefoxusercontent.com https://profile.accounts.firefox.com;"/>
     <title data-l10n-id="about-logins-page-title"></title>
     <link rel="localization" href="branding/brand.ftl">
     <link rel="localization" href="browser/branding/sync-brand.ftl">
     <link rel="localization" href="browser/branding/brandings.ftl">
     <link rel="localization" href="browser/aboutLogins.ftl">
     <script type="module" src="chrome://browser/content/aboutlogins/components/confirmation-dialog.js"></script>
     <script type="module" src="chrome://browser/content/aboutlogins/components/remove-logins-dialog.js"></script>
     <script type="module" src="chrome://browser/content/aboutlogins/components/import-summary-dialog.js"></script>
@@ -383,15 +383,14 @@
       <button class="menu-button ghost-button" data-l10n-id="menu"></button>
       <ul class="menu" role="menu" hidden>
         <button role="menuitem" class="menuitem-button menuitem-import-browser ghost-button" hidden data-supported-platforms="Win32,MacIntel" data-event-name="AboutLoginsImportFromBrowser" data-l10n-id="about-logins-menu-menuitem-import-from-another-browser"></button>
         <button role="menuitem" class="menuitem-button menuitem-import-file ghost-button" data-event-name="AboutLoginsImportFromFile" data-l10n-id="about-logins-menu-menuitem-import-from-a-file"></button>
         <button role="menuitem" class="menuitem-button menuitem-export ghost-button" data-event-name="AboutLoginsExportPasswordsDialog" data-l10n-id="about-logins-menu-menuitem-export-logins"></button>
         <button role="menuitem" class="menuitem-button menuitem-remove-all-logins ghost-button" data-event-name="AboutLoginsRemoveAllLoginsDialog" data-l10n-id="about-logins-menu-menuitem-remove-all-logins"></button>
         <hr role="separator" class="menuitem-separator"></hr>
         <button role="menuitem" class="menuitem-button menuitem-preferences ghost-button" data-event-name="AboutLoginsOpenPreferences" data-l10n-id="menu-menuitem-preferences"></button>
-        <hr role="separator" class="menuitem-separator"></hr>
         <button role="menuitem" class="menuitem-button menuitem-help ghost-button" data-event-name="AboutLoginsGetHelp" data-l10n-id="about-logins-menu-menuitem-help"></button>
       </ul>
     </template>
 
   </body>
 </html>
--- a/browser/components/aboutlogins/content/components/fxaccounts-button.js
+++ b/browser/components/aboutlogins/content/components/fxaccounts-button.js
@@ -42,20 +42,28 @@ export default class FxAccountsButton ex
       );
     }
   }
 
   render() {
     this._loggedOutView.hidden = !!this._loggedIn;
     this._loggedInView.hidden = !this._loggedIn;
     this._emailText.textContent = this._email;
-    this._avatarButton.style.setProperty(
-      "--avatar-url",
-      `url(${this._avatarURL})`
-    );
+    if (this._avatarURL) {
+      this._avatarButton.style.setProperty(
+        "--avatar-url",
+        `url(${this._avatarURL})`
+      );
+    } else {
+      let defaultAvatar = "chrome://browser/skin/fxa/avatar-color.svg";
+      this._avatarButton.style.setProperty(
+        "--avatar-url",
+        `url(${defaultAvatar})`
+      );
+    }
   }
 
   /**
    *
    * @param {object} state
    *                   loggedIn: {Boolean} FxAccount authentication
    *                             status.
    *                   email: {String} Email address used with FxAccount. Must
--- 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
@@ -7,20 +7,22 @@ prefs =
   # lower the interval for event telemetry in the content process to update the parent process
   toolkit.telemetry.ipcBatchTimeout=10
 
 # Run first so content events from previous tests won't trickle in.
 # Skip ASAN and debug since waiting for content events is already slow.
 [browser_aaa_eventTelemetry_run_first.js]
 skip-if =
   asan || tsan || ccov || debug || (os == "win" && !debug) # bug 1605494 is more prevalent on linux, Bug 1627419
+  os == "mac" && os_version == "10.15" && !debug # Bug 1710079
 [browser_alertDismissedAfterChangingPassword.js]
 [browser_breachAlertShowingForAddedLogin.js]
 [browser_confirmDeleteDialog.js]
 [browser_contextmenuFillLogins.js]
+skip-if = win10_2004 && debug # Bug 1723573
 [browser_copyToClipboardButton.js]
 [browser_createLogin.js]
 [browser_deleteLogin.js]
 [browser_fxAccounts.js]
 [browser_loginItemErrors.js]
 skip-if = debug # Bug 1577710
 [browser_loginListChanges.js]
 [browser_loginSortOrderRestored.js]
@@ -32,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/browser/browser_openImportCSV.js
+++ b/browser/components/aboutlogins/tests/browser/browser_openImportCSV.js
@@ -252,16 +252,22 @@ class CsvImportHelper {
       }
     );
     return data;
   }
 }
 
 const random = Math.round(Math.random() * 100000001);
 
+add_task(async function setup() {
+  registerCleanupFunction(() => {
+    Services.logins.removeAllUserFacingLogins();
+  });
+});
+
 add_task(async function test_open_import_one_item_from_csv() {
   await BrowserTestUtils.withNewTab(
     { gBrowser, url: "about:logins" },
     async browser => {
       await CsvImportHelper.clickImportFromCsvMenu(browser, [
         "url,username,password,httpRealm,formActionOrigin,guid,timeCreated,timeLastUsed,timePasswordChanged",
         `https://example.com,joe${random}@example.com,qwerty,My realm,,{${random}-e194-4279-ae1b-d7d281bb46f0},1589617814635,1589710449871,1589617846802`,
       ]);
--- a/browser/components/aboutlogins/tests/chrome/test_fxaccounts_button.html
+++ b/browser/components/aboutlogins/tests/chrome/test_fxaccounts_button.html
@@ -44,17 +44,16 @@ add_task(async function test_default_sta
   ok(isHidden(gFxAccountsButton.shadowRoot.querySelector(".logged-in-view")),
     "logged-in-view view is hidden by default");
 });
 
 add_task(async function test_logged_in_without_login_syncing() {
   gFxAccountsButton.updateState({
     fxAccountsEnabled: true,
     loggedIn: true,
-    avatarURL: TEST_AVATAR_URL,
     loginSyncingEnabled: false,
   });
 
   ok(isHidden(gFxAccountsButton.shadowRoot.querySelector(".logged-out-view")),
     "logged-out-view view is hidden");
   ok(!isHidden(gFxAccountsButton.shadowRoot.querySelector(".logged-in-view")),
     "logged-in-view view is visible");
 });
--- 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) {
@@ -1838,16 +1831,19 @@ var CustomizableUIInternal = {
       if (aWidget.disabled) {
         node.setAttribute("disabled", true);
       }
       node.setAttribute("removable", aWidget.removable);
       node.setAttribute("overflows", aWidget.overflows);
       if (aWidget.tabSpecific) {
         node.setAttribute("tabspecific", aWidget.tabSpecific);
       }
+      if (aWidget.locationSpecific) {
+        node.setAttribute("locationspecific", aWidget.locationSpecific);
+      }
 
       let shortcut;
       if (aWidget.shortcutId) {
         let keyEl = aDocument.getElementById(aWidget.shortcutId);
         if (keyEl) {
           shortcut = ShortcutUtils.prettifyShortcut(keyEl);
         } else {
           log.error(
@@ -2889,16 +2885,17 @@ var CustomizableUIInternal = {
       instances: new Map(),
       currentArea: null,
       localized: true,
       removable: true,
       overflows: true,
       defaultArea: null,
       shortcutId: null,
       tabSpecific: false,
+      locationSpecific: false,
       tooltiptext: null,
       l10nId: null,
       showInPrivateBrowsing: true,
       _introducedInVersion: -1,
     };
 
     if (typeof aData.id != "string" || !/^[a-z0-9-_]{1,}$/i.test(aData.id)) {
       log.error("Given an illegal id in normalizeWidget: " + aData.id);
@@ -2932,16 +2929,17 @@ var CustomizableUIInternal = {
       }
     }
 
     const kOptBoolProps = [
       "removable",
       "showInPrivateBrowsing",
       "overflows",
       "tabSpecific",
+      "locationSpecific",
       "localized",
     ];
     for (let prop of kOptBoolProps) {
       if (typeof aData[prop] == "boolean") {
         widget[prop] = aData[prop];
       }
     }
 
@@ -4003,16 +4001,20 @@ var CustomizableUI = {
    *                  the shortcut as part of the tooltip for builtin widgets
    *                  (which have strings inside
    *                  customizableWidgets.properties). If you're in an add-on,
    *                  you should not set this property.
    *                  If l10nId is provided, the resulting shortcut is passed
    *                  as the "$shortcut" variable to the fluent message.
    * - showInPrivateBrowsing: whether to show the widget in private browsing
    *                          mode (optional, default: true)
+   * - tabSpecific:      If true, closes the panel if the tab changes.
+   * - locationSpecific: If true, closes the panel if the location changes.
+   *                     This is similar to tabSpecific, but also if the location
+   *                     changes in the same tab, we may want to close the panel.
    *
    * @param aProperties the specifications for the widget.
    * @return a wrapper around the created widget (see getWidget)
    */
   createWidget(aProperties) {
     return CustomizableUIInternal.wrapWidget(
       CustomizableUIInternal.createWidget(aProperties)
     );
@@ -5076,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);
@@ -5156,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/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -1364,23 +1364,31 @@ var PanelView = class extends Associated
       style.removeProperty("max-height");
     }
   }
 
   /**
    * Adds a header with the given title, or removes it if the title is empty.
    */
   set headerText(value) {
+    let ensureHeaderSeparator = headerNode => {
+      if (headerNode.nextSibling.tagName != "toolbarseparator") {
+        let separator = this.document.createXULElement("toolbarseparator");
+        this.node.insertBefore(separator, headerNode.nextSibling);
+      }
+    };
+
     // If the header already exists, update or remove it as requested.
     let header = this.node.firstElementChild;
     if (header && header.classList.contains("panel-header")) {
       if (value) {
         // The back button has a label in it - we want to select
         // the label that's a direct child of the header.
         header.querySelector(".panel-header > h1 > span").textContent = value;
+        ensureHeaderSeparator(header);
       } else {
         if (header.nextSibling.tagName == "toolbarseparator") {
           header.nextSibling.remove();
         }
         header.remove();
       }
       return;
     }
@@ -1411,20 +1419,17 @@ var PanelView = class extends Associated
     let h1 = this.document.createElement("h1");
     let span = this.document.createElement("span");
     span.textContent = value;
     h1.appendChild(span);
 
     header.append(backButton, h1);
     this.node.prepend(header);
 
-    if (header.nextSibling.tagName != "toolbarseparator") {
-      let separator = this.document.createXULElement("toolbarseparator");
-      this.node.insertBefore(separator, header.nextSibling);
-    }
+    ensureHeaderSeparator(header);
   }
 
   /**
    * Also make sure that the correct method is called on CustomizableWidget.
    */
   dispatchCustomEvent(...args) {
     CustomizableUI.ensureSubviewListeners(this.node);
     return super.dispatchCustomEvent(...args);
--- a/browser/components/customizableui/content/panelUI.inc.xhtml
+++ b/browser/components/customizableui/content/panelUI.inc.xhtml
@@ -47,18 +47,17 @@
               id="customizationPanelItemContextMenuPin"
               data-lazy-l10n-id="toolbar-context-menu-pin-to-overflow-menu"
               closemenu="single"
               class="customize-context-moveToPanel"/>
     <menuitem oncommand="gCustomizeMode.addToToolbar(this.parentNode.triggerNode, 'panelitem-context')"
               id="customizationPanelItemContextMenuUnpin"
               closemenu="single"
               class="customize-context-moveToToolbar"
-              accesskey="&customizeMenu.unpinFromOverflowMenu.accesskey;"
-              label="&customizeMenu.unpinFromOverflowMenu.label;"/>
+              data-l10n-id="customize-menu-unpin-from-overflowmenu"/>
     <menuitem oncommand="gCustomizeMode.removeFromArea(this.parentNode.triggerNode, 'panelitem-context')"
               closemenu="single"
               class="customize-context-removeFromPanel"
               data-lazy-l10n-id="toolbar-context-menu-remove-from-toolbar"/>
     <menuseparator/>
     <menuitem command="cmd_CustomizeToolbars"
               class="viewCustomizeToolbar"
               data-lazy-l10n-id="toolbar-context-menu-view-customize-toolbar"/>
@@ -70,22 +69,22 @@
          type="arrow"
          position="bottomcenter topright"
          hidden="true"
          role="alert"
          orient="vertical">
     <hbox id="panic-button-success-header">
       <image id="panic-button-success-icon" alt=""/>
       <vbox>
-        <description>&panicButton.thankyou.msg1;</description>
-        <description>&panicButton.thankyou.msg2;</description>
+        <description data-l10n-id="panic-button-thankyou-msg1"></description>
+        <description data-l10n-id="panic-button-thankyou-msg2"></description>
       </vbox>
     </hbox>
-    <button label="&panicButton.thankyou.buttonlabel;"
-            id="panic-button-success-closebutton"
+    <button id="panic-button-success-closebutton"
+            data-l10n-id="panic-button-thankyou-button"
             oncommand="PanicButtonNotifier.close()"/>
   </panel>
 </html:template>
 
 <html:template id="appMenuNotificationTemplate">
   <panel id="appMenu-notification-popup"
          class="popup-notification-panel panel-no-padding"
          type="arrow"
@@ -190,29 +189,22 @@
   </panel>
 </html:template>
 
 <html:template id="customModeWrapper">
   <menupopup id="customizationPaletteItemContextMenu"
              onpopupshowing="gCustomizeMode.onPaletteContextMenuShowing(event)">
     <menuitem oncommand="gCustomizeMode.addToToolbar(this.parentNode.triggerNode, 'palette-context')"
               class="customize-context-addToToolbar"
-              accesskey="&customizeMenu.addToToolbar.accesskey;"
-              label="&customizeMenu.addToToolbar.label;"/>
+              data-l10n-id="customize-menu-add-to-toolbar"/>
     <menuitem oncommand="gCustomizeMode.addToPanel(this.parentNode.triggerNode, 'palette-context')"
               class="customize-context-addToPanel"
-              accesskey="&customizeMenu.addToOverflowMenu.accesskey;"
-              label="&customizeMenu.addToOverflowMenu.label;"/>
+              data-l10n-id="customize-menu-add-to-overflowmenu"/>
   </menupopup>
 
-  <menupopup id="customizationPanelContextMenu">
-    <menuitem command="cmd_CustomizeToolbars"
-              accesskey="&customizeMenu.addMoreItems.accesskey;"
-              label="&customizeMenu.addMoreItems.label;"/>
-  </menupopup>
   <panel id="downloads-button-autohide-panel"
          role="group"
          type="arrow"
          onpopupshown="gCustomizeMode._downloadPanelAutoHideTimeout = setTimeout(() => event.target.hidePopup(), 4000);"
          onmouseover="clearTimeout(gCustomizeMode._downloadPanelAutoHideTimeout);"
          onmouseout="gCustomizeMode._downloadPanelAutoHideTimeout = setTimeout(() => event.target.hidePopup(), 2000);"
          onpopuphidden="clearTimeout(gCustomizeMode._downloadPanelAutoHideTimeout);"
          >
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -458,21 +458,29 @@ 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);
+      }
       if (this._disableAnimations) {
         tempPanel.setAttribute("animate", "false");
       }
       tempPanel.setAttribute("context", "");
       document
         .getElementById(CustomizableUI.AREA_NAVBAR)
         .appendChild(tempPanel);
 
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -131,16 +131,17 @@ tags = fullscreen
 skip-if = os == "mac"
 [browser_1087303_button_preferences.js]
 [browser_1089591_still_customizable_after_reset.js]
 [browser_1096763_seen_widgets_post_reset.js]
 [browser_1161838_inserted_new_default_buttons.js]
 skip-if = verify
 [browser_1484275_PanelMultiView_toggle_with_other_popup.js]
 [browser_1701883_restore_defaults_pocket_pref.js]
+[browser_1702200_PanelMultiView_header_separator.js]
 [browser_allow_dragging_removable_false.js]
 [browser_bookmarks_toolbar_collapsed_restore_default.js]
 [browser_bookmarks_toolbar_shown_newtab.js]
 [browser_bootstrapped_custom_toolbar.js]
 [browser_ctrl_click_panel_opening.js]
 [browser_currentset_post_reset.js]
 [browser_customizemode_contextmenu_menubuttonstate.js]
 skip-if = os == "win" && bits == 64 # 1526429
@@ -158,16 +159,17 @@ https_first_disabled = true
 [browser_history_restore_session.js]
 [browser_insert_before_moved_node.js]
 [browser_menubar_visibility.js]
 skip-if = os == "mac" # no toggle-able menubar on macOS.
 [browser_overflow_use_subviews.js]
 skip-if = verify
 [browser_palette_labels.js]
 [browser_panel_keyboard_navigation.js]
+[browser_panel_locationSpecific.js]
 [browser_panel_toggle.js]
 [browser_panelUINotifications.js]
 [browser_panelUINotifications_fullscreen.js]
 tags = fullscreen
 skip-if = os == "mac"
 [browser_panelUINotifications_fullscreen_noAutoHideToolbar.js]
 skip-if = (verify && (os == 'linux' || os == 'mac'))
 tags = fullscreen
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_1702200_PanelMultiView_header_separator.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests whether the separator insertion works correctly for the
+ * special case where we remove the content except the header itself
+ * before showing the panel view.
+ */
+
+const TEST_SITE = "http://127.0.0.1";
+const RECENTLY_CLOSED_TABS_PANEL_ID = "appMenu-library-recentlyClosedTabs";
+const RECENTLY_CLOSED_TABS_ITEM_ID = "appMenuRecentlyClosedTabs";
+
+function assertHeaderSeparator() {
+  let header = document.querySelector(
+    `#${RECENTLY_CLOSED_TABS_PANEL_ID} .panel-header`
+  );
+  Assert.equal(
+    header.nextSibling.tagName,
+    "toolbarseparator",
+    "toolbarseparator should be shown below header"
+  );
+}
+
+/**
+ * Open and close a tab so we can access the "Recently
+ * closed tabs" panel
+ */
+add_task(async function test_setup() {
+  let tab = BrowserTestUtils.addTab(gBrowser, TEST_SITE);
+  gBrowser.selectedTab = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser, false, null, true);
+  await BrowserTestUtils.removeTab(tab);
+});
+
+/**
+ * Tests whether the toolbarseparator is shown correctly
+ * after re-entering same sub view, see bug 1702200
+ *
+ * - App Menu
+ *   - History
+ *     - Recently closed tabs
+ */
+add_task(async function test_header_toolbarseparator() {
+  await gCUITestUtils.openMainMenu();
+
+  let historyView = PanelMultiView.getViewNode(document, "PanelUI-history");
+  document.getElementById("appMenu-history-button").click();
+  await BrowserTestUtils.waitForEvent(historyView, "ViewShown");
+
+  // Open Recently Closed Tabs and make sure there is a header separator
+  let closedTabsView = PanelMultiView.getViewNode(
+    document,
+    RECENTLY_CLOSED_TABS_PANEL_ID
+  );
+  Assert.ok(!document.getElementById(RECENTLY_CLOSED_TABS_ITEM_ID).disabled);
+  document.getElementById(RECENTLY_CLOSED_TABS_ITEM_ID).click();
+  await BrowserTestUtils.waitForEvent(closedTabsView, "ViewShown");
+  assertHeaderSeparator();
+
+  // Go back and re-open the same view, header separator should be
+  // re-added as well
+  document
+    .querySelector(`#${RECENTLY_CLOSED_TABS_PANEL_ID} .subviewbutton-back`)
+    .click();
+  await BrowserTestUtils.waitForEvent(historyView, "ViewShown");
+  document.getElementById(RECENTLY_CLOSED_TABS_ITEM_ID).click();
+  await BrowserTestUtils.waitForEvent(closedTabsView, "ViewShown");
+  assertHeaderSeparator();
+
+  await gCUITestUtils.hideMainMenu();
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/customizableui/test/browser_panel_locationSpecific.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/*
+ * This test creates multiple panels, one that has been tagged as location specific
+ * and one that isn't. When the location changes, the specific panel should close.
+ * The non-specific panel should remain open.
+ *
+ */
+
+add_task(async function() {
+  let specificPanel = document.createXULElement("panel");
+  specificPanel.setAttribute("locationspecific", "true");
+  specificPanel.setAttribute("noautohide", "true");
+  specificPanel.height = "100px";
+  specificPanel.width = "100px";
+
+  let generalPanel = document.createXULElement("panel");
+  generalPanel.setAttribute("noautohide", "true");
+  generalPanel.height = "100px";
+  generalPanel.width = "100px";
+
+  let anchor = document.getElementById(CustomizableUI.AREA_NAVBAR);
+
+  anchor.appendChild(specificPanel);
+  anchor.appendChild(generalPanel);
+  is(specificPanel.state, "closed", "specificPanel starts as closed");
+  is(generalPanel.state, "closed", "generalPanel starts as closed");
+
+  let specificPanelPromise = BrowserTestUtils.waitForEvent(
+    specificPanel,
+    "popupshown"
+  );
+
+  specificPanel.openPopupAtScreen(0, 0);
+
+  await specificPanelPromise;
+  is(specificPanel.state, "open", "specificPanel has been opened");
+
+  let generalPanelPromise = BrowserTestUtils.waitForEvent(
+    generalPanel,
+    "popupshown"
+  );
+
+  generalPanel.openPopupAtScreen(100, 0);
+
+  await generalPanelPromise;
+  is(generalPanel.state, "open", "generalPanel has been opened");
+
+  let specificPanelHiddenPromise = BrowserTestUtils.waitForEvent(
+    specificPanel,
+    "popuphidden"
+  );
+
+  // Simulate a location change, and check which panel closes.
+  let browser = gBrowser.selectedBrowser;
+  let loaded = BrowserTestUtils.browserLoaded(browser);
+  BrowserTestUtils.loadURI(browser, "http://mochi.test:8888/#0");
+  await loaded;
+
+  await specificPanelHiddenPromise;
+
+  is(
+    specificPanel.state,
+    "closed",
+    "specificPanel panel is closed after location change"
+  );
+  is(
+    generalPanel.state,
+    "open",
+    "generalPanel is still open after location change"
+  );
+
+  specificPanel.remove();
+  generalPanel.remove();
+});
--- 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